Merge branch 'betting-merge'
This commit is contained in:
commit
f78b6c1ec6
207 changed files with 244254 additions and 2931 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -8,6 +8,8 @@ Makefile
|
|||
compile_commands.json
|
||||
moc_*
|
||||
*.moc
|
||||
|
||||
genesis.json
|
||||
hardfork.hpp
|
||||
|
||||
libraries/utilities/git_revision.cpp
|
||||
|
|
@ -39,3 +41,6 @@ object_database/*
|
|||
|
||||
*.pyc
|
||||
*.pyo
|
||||
|
||||
.romek
|
||||
.*
|
||||
|
|
|
|||
8
.gitmodules
vendored
8
.gitmodules
vendored
|
|
@ -1,8 +1,8 @@
|
|||
[submodule "docs"]
|
||||
path = docs
|
||||
url = https://github.com/cryptonomex/graphene.wiki.git
|
||||
path = docs
|
||||
url = https://github.com/bitshares/bitshares-core.wiki.git
|
||||
ignore = dirty
|
||||
[submodule "libraries/fc"]
|
||||
path = libraries/fc
|
||||
url = git@git.syncad.com:blocktrades/fc.git
|
||||
path = libraries/fc
|
||||
url = https://bitbucket.org/peerplaysblockchain/peerplays-fc.git
|
||||
ignore = dirty
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ 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" )
|
||||
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)
|
||||
|
|
@ -32,13 +32,16 @@ if (USE_PCH)
|
|||
include (cotire)
|
||||
endif(USE_PCH)
|
||||
|
||||
list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libraries/fc/CMakeModules" )
|
||||
IF( NOT WIN32 )
|
||||
list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libraries/fc/CMakeModules" )
|
||||
ENDIF( NOT WIN32 )
|
||||
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
|
||||
iostreams
|
||||
date_time
|
||||
system
|
||||
filesystem
|
||||
|
|
@ -129,6 +132,10 @@ else( WIN32 ) # Apple AND Linux
|
|||
|
||||
if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp" )
|
||||
elseif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
|
||||
if( CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.0.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0.0 )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization" )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if( "${CMAKE_GENERATOR}" STREQUAL "Ninja" )
|
||||
|
|
@ -143,8 +150,6 @@ else( WIN32 ) # Apple AND Linux
|
|||
|
||||
endif( WIN32 )
|
||||
|
||||
find_package( BerkeleyDB )
|
||||
|
||||
set(ENABLE_COVERAGE_TESTING FALSE CACHE BOOL "Build BitShares for code coverage analysis")
|
||||
|
||||
if(ENABLE_COVERAGE_TESTING)
|
||||
|
|
|
|||
|
|
@ -1,98 +0,0 @@
|
|||
# Find the BerkeleyDB includes and library
|
||||
# Customizable variables:
|
||||
# BDB_ROOT_DIR
|
||||
# This variable points to the BerkeleyDB root directory. On Windows the
|
||||
# library location typically will have to be provided explicitly using the
|
||||
# -D command-line option. Alternatively, the DBROOTDIR environment variable
|
||||
# can be set.
|
||||
#
|
||||
# BDB_STATIC_LIBS
|
||||
# Should be set to 1 if static version of libraries should be found. Defaults to 0 (shared libs).
|
||||
#
|
||||
# This module defines
|
||||
# BDB_INCLUDE_DIR, where to find db.h, etc.
|
||||
# BDB_LIBRARIES, the libraries needed to use BerkeleyDB.
|
||||
|
||||
IF (NOT DEFINED BDB_ROOT_DIR)
|
||||
SET (BDB_ROOT_DIR $ENV{DBROOTDIR})
|
||||
ENDIF()
|
||||
|
||||
MESSAGE (STATUS "Using ${BDB_ROOT_DIR} as BerkeleyDB root")
|
||||
|
||||
IF(NOT DEFINED BDB_STATIC_LIBS)
|
||||
SET (BDB_STATIC_LIBS 0)
|
||||
ENDIF()
|
||||
|
||||
FIND_PATH(BDB_INCLUDE_DIR NAMES db.h db_cxx.h
|
||||
HINTS "${BDB_ROOT_DIR}/include"
|
||||
PATHS ${BDB_ROOT_DIR}
|
||||
/usr/include/libdb5
|
||||
/usr/include/db5
|
||||
/usr/include/libdb4
|
||||
/usr/include/db4
|
||||
/usr/local/include/libdb5
|
||||
/usr/local/include/db5
|
||||
/usr/local/include/libdb4
|
||||
/usr/local/include/db4
|
||||
PATH_SUFFIXES include
|
||||
)
|
||||
|
||||
IF (WIN32)
|
||||
IF(NOT DEFINED BDB_VERSION)
|
||||
SET (DB_VERSION "60")
|
||||
ENDIF ()
|
||||
|
||||
SET (BDB_LIB_BASENAME "libdb")
|
||||
|
||||
IF (${BDB_STATIC_LIBS} EQUAL 1)
|
||||
SET (BDB_LIBS_SUFFIX_RELEASE "s")
|
||||
SET (BDB_LIBS_SUFFIX_DEBUG "sD")
|
||||
ELSE()
|
||||
SET (BDB_LIBS_SUFFIX_RELEASE "")
|
||||
SET (BDB_LIBS_SUFFIX_DEBUG "D")
|
||||
ENDIF()
|
||||
|
||||
ELSE (WIN32)
|
||||
IF(NOT DEFINED BDB_VERSION)
|
||||
SET (DB_VERSION "-6.0")
|
||||
ENDIF ()
|
||||
|
||||
# On unix library in all versions have the same names.
|
||||
SET (BDB_LIBS_SUFFIX_RELEASE "")
|
||||
SET (BDB_LIBS_SUFFIX_DEBUG "")
|
||||
|
||||
SET (BDB_LIB_BASENAME "db_cxx")
|
||||
ENDIF (WIN32)
|
||||
|
||||
message (STATUS "Looking for: ${BDB_LIB_BASENAME}${DB_VERSION}${BDB_LIBS_SUFFIX_RELEASE}")
|
||||
FIND_LIBRARY(BDB_LIBRARY_RELEASE "${BDB_LIB_BASENAME}${DB_VERSION}${BDB_LIBS_SUFFIX_RELEASE}" "${BDB_LIB_BASENAME}"
|
||||
HINTS "${BDB_ROOT_DIR}/lib" PATHS ${BDB_ROOT_DIR} ${BDB_INCLUDE_DIR} "/usr/local/lib" PATH_SUFFIXES lib
|
||||
)
|
||||
|
||||
FIND_LIBRARY(BDB_LIBRARY_DEBUG "${BDB_LIB_BASENAME}${DB_VERSION}${BDB_LIBS_SUFFIX_DEBUG}" "${BDB_LIB_BASENAME}"
|
||||
HINTS "${BDB_ROOT_DIR}/lib" PATHS ${BDB_ROOT_DIR} ${BDB_INCLUDE_DIR} "/usr/local/lib" PATH_SUFFIXES lib
|
||||
)
|
||||
|
||||
IF (BDB_LIBRARY_RELEASE AND BDB_LIBRARY_DEBUG )
|
||||
SET (_BDB_LIBRARY
|
||||
debug ${BDB_LIBRARY_DEBUG}
|
||||
optimized ${BDB_LIBRARY_RELEASE}
|
||||
)
|
||||
ELSEIF(BDB_LIBRARY_RELEASE)
|
||||
SET (_BDB_LIBRARY ${BDB_LIBRARY_RELEASE})
|
||||
ELSEIF(BDB_LIBRARY_DEBUG)
|
||||
SET (_BDB_LIBRARY ${BDB_LIBRARY_DEBUG})
|
||||
ENDIF()
|
||||
|
||||
MESSAGE (STATUS ${_BDB_LIBRARY})
|
||||
|
||||
IF(_BDB_LIBRARY)
|
||||
LIST (APPEND BDB_LIBRARIES ${_BDB_LIBRARY})
|
||||
ENDIF()
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(BerkeleyDB
|
||||
FOUND_VAR BerkeleyDB_FOUND
|
||||
REQUIRED_VARS BDB_INCLUDE_DIR BDB_LIBRARIES
|
||||
FAIL_MESSAGE "Could not find Berkeley DB >= 4.1" )
|
||||
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
find_program(NPM_EXECUTABLE npm)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args("NPM" DEFAULT_MSG NPM_EXECUTABLE)
|
||||
|
||||
find_program(LINEMAN_EXECUTABLE lineman)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args("Lineman" DEFAULT_MSG LINEMAN_EXECUTABLE)
|
||||
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
find_program(NODEJS_EXECUTABLE node)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args("NodeJs" DEFAULT_MSG NODEJS_EXECUTABLE)
|
||||
File diff suppressed because it is too large
Load diff
0
CONTRIBUTORS.txt
Normal file
0
CONTRIBUTORS.txt
Normal file
|
|
@ -1,4 +1,5 @@
|
|||
Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
Copyright (c) 2015-2016 Cryptonomex Inc. <contact@cryptonomex.com>
|
||||
Copyright (c) 2015-2017 contributors <CONTRIBUTORS.txt>
|
||||
|
||||
The MIT License
|
||||
|
||||
|
|
@ -19,4 +20,3 @@ 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.
|
||||
|
||||
145
README.md
145
README.md
|
|
@ -1,95 +1,87 @@
|
|||
Intro for new developers
|
||||
------------------------
|
||||
BitShares Core
|
||||
==============
|
||||
* [Getting Started](#getting-started)
|
||||
* [Support](#support)
|
||||
* [Using the API](#using-the-api)
|
||||
* [Accessing restricted API's](#accessing-restricted-apis)
|
||||
* [FAQ](#faq)
|
||||
* [License](#license)
|
||||
|
||||
This is a quick introduction to get new developers up to speed on Graphene.
|
||||
BitShares Core is the BitShares blockchain implementation and command-line interface.
|
||||
The web wallet is [BitShares UI](https://github.com/bitshares/bitshares-ui).
|
||||
|
||||
Starting Graphene
|
||||
-----------------
|
||||
Visit [BitShares.org](https://bitshares.org/) to learn about BitShares and join the community at [BitSharesTalk.org](https://bitsharestalk.org/).
|
||||
|
||||
For Ubuntu 14.04 LTS users, see this link first:
|
||||
https://github.com/cryptonomex/graphene/wiki/build-ubuntu
|
||||
**NOTE:** The official BitShares git repository location, default branch, and submodule remotes were recently changed. Existing
|
||||
repositories can be updated with the following steps:
|
||||
|
||||
and then proceed with:
|
||||
|
||||
git clone https://github.com/cryptonomex/graphene.git
|
||||
cd graphene
|
||||
git remote set-url origin https://github.com/bitshares/bitshares-core.git
|
||||
git checkout master
|
||||
git remote set-head origin --auto
|
||||
git pull
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --recursive
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug .
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
Build instructions and additional documentation are available in the
|
||||
[wiki](https://github.com/bitshares/bitshares-core/wiki).
|
||||
|
||||
We recommend building on Ubuntu 16.04 LTS, and the build dependencies may be installed with:
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get install autoconf cmake git libboost-all-dev libssl-dev
|
||||
|
||||
To build after all dependencies are installed:
|
||||
|
||||
git clone https://github.com/bitshares/bitshares-core.git
|
||||
cd bitshares-core
|
||||
git checkout <LATEST_RELEASE_TAG>
|
||||
git submodule update --init --recursive
|
||||
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .
|
||||
make
|
||||
|
||||
**NOTE:** BitShares requires an [OpenSSL](https://www.openssl.org/) version in the 1.0.x series. OpenSSL 1.1.0 and newer are NOT supported. If your system OpenSSL version is newer, then you will need to manually provide an older version of OpenSSL and specify it to CMake using `-DOPENSSL_INCLUDE_DIR`, `-DOPENSSL_SSL_LIBRARY`, and `-DOPENSSL_CRYPTO_LIBRARY`.
|
||||
|
||||
**NOTE:** BitShares requires a [Boost](http://www.boost.org/) version in the range [1.57, 1.60]. Versions earlier than
|
||||
1.57 or newer than 1.60 are NOT supported. If your system Boost version is newer, then you will need to manually build
|
||||
an older version of Boost and specify it to CMake using `DBOOST_ROOT`.
|
||||
|
||||
After building, the witness node can be launched with:
|
||||
|
||||
./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:
|
||||
The node will automatically create a data directory including a config file. It may take several hours to fully synchronize
|
||||
the blockchain. After syncing, you can exit the node using Ctrl+C and setup the command-line wallet by editing
|
||||
`witness_node_data_dir/config.ini` as follows:
|
||||
|
||||
rpc-endpoint = 127.0.0.1:8090
|
||||
|
||||
Then, in a separate terminal window, start the command-line wallet `cli_wallet`:
|
||||
After starting the witness node again, in a separate terminal you can run:
|
||||
|
||||
./programs/cli_wallet/cli_wallet
|
||||
|
||||
To set your iniital password to 'password' use:
|
||||
Set your inital password:
|
||||
|
||||
>>> set_password password
|
||||
>>> unlock password
|
||||
>>> set_password <PASSWORD>
|
||||
>>> unlock <PASSWORD>
|
||||
|
||||
To import your initial balance:
|
||||
|
||||
>>> import_balance nathan [5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3] true
|
||||
>>> import_balance <ACCOUNT NAME> [<WIF_KEY>] 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).
|
||||
Use `help` to see all available wallet commands. Source definition and listing of all commands is available
|
||||
[here](https://github.com/bitshares/bitshares-core/blob/master/libraries/wallet/include/graphene/wallet/wallet.hpp).
|
||||
|
||||
Code coverage testing
|
||||
---------------------
|
||||
Support
|
||||
-------
|
||||
Technical support is available in the [BitSharesTalk technical support subforum](https://bitsharestalk.org/index.php?board=45.0).
|
||||
|
||||
Check how much code is covered by unit tests, using gcov/lcov (see http://ltp.sourceforge.net/coverage/lcov.php ).
|
||||
BitShares Core bugs can be reported directly to the [issue tracker](https://github.com/bitshares/bitshares-core/issues).
|
||||
|
||||
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`
|
||||
BitShares UI bugs should be reported to the [UI issue tracker](https://github.com/bitshares/bitshares-ui/issues)
|
||||
|
||||
Using the API
|
||||
-------------
|
||||
|
|
@ -144,7 +136,7 @@ necessary to use the wallet:
|
|||
]
|
||||
}
|
||||
|
||||
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.
|
||||
Passwords are stored in `base64` 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:
|
||||
|
|
@ -162,13 +154,8 @@ If you want information which is not available from an API, it might be availabl
|
|||
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
|
||||
---------
|
||||
FAQ
|
||||
---
|
||||
|
||||
- Is there a way to generate help with parameter names and method descriptions?
|
||||
|
||||
|
|
@ -207,7 +194,7 @@ Questions
|
|||
|
||||
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).
|
||||
[types.hpp](https://github.com/bitshares/bitshares-2/blob/bitshares/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.
|
||||
|
|
@ -238,3 +225,7 @@ Questions
|
|||
connecting to. Therefore the API to add p2p connections needs to be set up with proper access
|
||||
controls.
|
||||
|
||||
License
|
||||
-------
|
||||
BitShares Core is under the MIT license. See [LICENSE](https://github.com/bitshares/bitshares-core/blob/master/LICENSE.txt)
|
||||
for more information.
|
||||
|
|
|
|||
161
betting2-nhl
Normal file
161
betting2-nhl
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
|
||||
|
||||
### PRE...
|
||||
|
||||
import_key init0 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init1 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init2 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init3 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init4 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init5 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init6 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init7 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init8 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init9 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init10 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
|
||||
#upgrade_account nathan 1
|
||||
import_balance nathan [5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3] 1
|
||||
|
||||
|
||||
|
||||
transfer nathan init0 125 PPY "" true
|
||||
transfer nathan init1 125 PPY "" true
|
||||
#transfer nathan init2 125 PPY "" true
|
||||
#transfer nathan init3 125 PPY "" true
|
||||
#transfer nathan init4 125 PPY "" true
|
||||
#transfer nathan init5 125 PPY "" true
|
||||
#transfer nathan init6 125 PPY "" true
|
||||
#transfer nathan init7 125 PPY "" true
|
||||
#transfer nathan init8 125 PPY "" true
|
||||
#transfer nathan init9 125 PPY "" true
|
||||
#transfer nathan init10 125 PPY "" true
|
||||
|
||||
transfer nathan 1.2.0 1250 PPY "" true
|
||||
transfer nathan 1.2.1 1250 PPY "" true
|
||||
|
||||
transfer nathan 1.2.5 1250 PPY "" true
|
||||
|
||||
vote_for_witness nathan init0 1 1
|
||||
vote_for_witness nathan init1 1 1
|
||||
|
||||
whitelist_account init0 1.2.1 1 1
|
||||
whitelist_account init1 1.2.1 1 1
|
||||
#whitelist_account init2 1.2.1 1 1
|
||||
#whitelist_account init2 1.2.1 1 1
|
||||
#whitelist_account init3 1.2.1 1 1
|
||||
#whitelist_account init4 1.2.1 1 1
|
||||
#whitelist_account init5 1.2.1 1 1
|
||||
#whitelist_account init6 1.2.1 1 1
|
||||
#whitelist_account init7 1.2.1 1 1
|
||||
#whitelist_account init8 1.2.1 1 1
|
||||
#whitelist_account init9 1.2.1 1 1
|
||||
#whitelist_account init10 1.2.1 1 1
|
||||
|
||||
|
||||
# ALICE & BOB
|
||||
|
||||
register_account alice PPY4zSJHx7D84T1j6HQ7keXWdtabBBWJxvfJw72XmEyqmgdoo1njF PPY4zSJHx7D84T1j6HQ7keXWdtabBBWJxvfJw72XmEyqmgdoo1njF nathan nathan 0 true
|
||||
register_account bob PPY4zSJHx7D84T1j6HQ7keXWdtabBBWJxvfJw72XmEyqmgdoo1njF PPY4zSJHx7D84T1j6HQ7keXWdtabBBWJxvfJw72XmEyqmgdoo1njF nathan nathan 0 true
|
||||
|
||||
transfer nathan alice 1250 PPY "" true
|
||||
transfer nathan bob 1250 PPY "" true
|
||||
|
||||
import_key alice 5HuCDiMeESd86xrRvTbexLjkVg2BEoKrb7BAA5RLgXizkgV3shs
|
||||
import_key bob 5HuCDiMeESd86xrRvTbexLjkVg2BEoKrb7BAA5RLgXizkgV3shs
|
||||
|
||||
|
||||
### SPORT
|
||||
|
||||
propose_create_sport nathan "2017-05-16-T07:46:03" [["en","Ice Hockey"],["zh_Hans","冰球"],["ja","アイスホッケー"]] 1
|
||||
propose_create_sport nathan "2017-05-16-T07:47:01" [["en","spce balls"], ["pl","gra w kulki"]] 1
|
||||
|
||||
// proposal
|
||||
#get_object 1.10.0
|
||||
#get_object 1.10.1
|
||||
|
||||
#approve_proposal nathan 1.10.0 { "active_approvals_to_add" : [ "init0", "init1","init2", "init3", "init4", "init5", "init6", "init7", "init8", "init9", "init10"] } 1
|
||||
approve_proposal nathan 1.10.0 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
approve_proposal nathan 1.10.12 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
#get_object 1.16.0
|
||||
#get_object 1.16.1
|
||||
|
||||
|
||||
### EVENT GROUOP
|
||||
|
||||
propose_create_event_group nathan "2017-05-16-T08:45:01" [ ["en", "NHL"], ["zh_Hans", "國家冰球聯盟"], ["ja", "ナショナルホッケーリーグ"] ] "1.16.0" 1
|
||||
approve_proposal nathan 1.10.2 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.17.0
|
||||
|
||||
|
||||
### EVENT
|
||||
|
||||
propose_create_event nathan "2017-08-07-T11:25:01" [["en", "Washington Capitals/Chicago Blackhawks"], ["zh_Hans", "華盛頓首都隊/芝加哥黑鷹"], ["ja", "ワシントン・キャピタルズ/シカゴ・ブラックホークス"]] [["en","2016-17"]] "2018-09-16-T07:47:01" "1.17.0" 1
|
||||
|
||||
propose_create_event nathan "2017-08-07-T11:25:01" [["en", "Washington Capitals/Chicago Blackhawks"], ["zh_Hans", "華盛頓首都隊/芝加哥黑鷹"], ["ja", "ワシントン・キャピタルズ/シカゴ・ブラックホークス"]] [["en","2017-18"]] "2018-09-16-T07:47:01" "1.17.0" 1
|
||||
|
||||
//propose_update_event nathan "2017-08-07-T11:25:01" "1.18.0" "1.17.0" [["en", "Washington Capitals/Chicago Blackhawks"], ["zh_Hans", "華盛頓首都隊/芝加哥黑鷹"], ["ja", "ワシントン・キャピタルズ/シカゴ・ブラックホークス"]] [["en","2016-17"]] "2018-09-16-T07:47:01" 1
|
||||
|
||||
approve_proposal nathan 1.10.3 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.18.0
|
||||
|
||||
|
||||
# RULES
|
||||
|
||||
propose_create_betting_market_rules nathan "2017-08-31-T10:35:01" [["en","NHL Rules v1.0"]] [["en", "The winner will be the team with the most points at the end of the game. The team with fewer points will not be the winner."]] 1
|
||||
|
||||
approve_proposal nathan 1.10.8 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.19.0
|
||||
|
||||
|
||||
### BETTING_MARKET GROUP
|
||||
|
||||
propose_create_betting_market_group nathan "2017-08-31-T10:39:01" [["en", "Moneyline"]] "1.18.0" "1.19.0" "1.3.0" 1
|
||||
propose_create_betting_market_group nathan "2017-08-25-T11:10:01" [["en", "Moneyline NHL"]] "1.18.1" "1.19.1" "1.3.0" 1
|
||||
|
||||
approve_proposal nathan 1.10.9 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.20.0
|
||||
|
||||
|
||||
### BETTING_MARKET
|
||||
|
||||
propose_create_betting_market nathan "2017-08-31-T10:42:01" "1.20.0" [["en","Washington Capitals win"]] [] 1
|
||||
propose_create_betting_market nathan "2017-08-31-T10:42:01" "1.20.0" [["en","Chicago Blackhawks win"]] [] 1
|
||||
|
||||
approve_proposal nathan 1.10.10 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
approve_proposal nathan 1.10.11 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.21.0
|
||||
//get_object 1.21.1
|
||||
|
||||
//list_betting_markets 1.20.0
|
||||
|
||||
|
||||
# PLACE_BET
|
||||
|
||||
//place_bet bob "1.21.0" 1 { "amount" : 100 } 20000 20000 true
|
||||
//place_bet alice "1.21.0" 0 { "amount" : 100 } 20000 20000 true
|
||||
|
||||
place_bet alice "1.21.0" 1 "123" "PPY" 200 true
|
||||
//place_bet bob "1.21.0" 0 "123" "PPY" 200 true
|
||||
|
||||
|
||||
place_bet carol "1.21.1" 0 "123" "PPY" 200 true
|
||||
cancel_bet carol 1.22.2 1
|
||||
|
||||
get_unmatched_bets_for_bettor 1.21.1 1.2.22
|
||||
|
||||
|
||||
# RESOLVE
|
||||
|
||||
propose_resolve_betting_market_group nathan "2017-08-16-T17:50:01" "1.20.0" [ [ "1.21.0" , 0] [ "1.21.1" , 1] ] 1
|
||||
|
||||
approve_proposal nathan 1.10.9 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
|
||||
|
||||
165
betting2-wimbledon
Normal file
165
betting2-wimbledon
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
|
||||
|
||||
|
||||
http://api.coindesk.com/charts/data?data=close&startdate=2017-01-01&enddate=2017-08-28&exchanges=bpi,bitstamp,coinbase,itbit,okcoin&dev=1&index=USD
|
||||
|
||||
|
||||
|
||||
select date, b.coin_type as bought_coin_type, bought_quantity, s.coin_type as sold_coin_type, sold_quantity from external_trades as t
|
||||
where processed = false
|
||||
left join coin_types as b ON t.bought_coin_type_id = b.coin_type_id
|
||||
left join coin_types as s ON t.sold_coin_type_id = s.coin_type_id
|
||||
|
||||
|
||||
### PRE...
|
||||
|
||||
see betting-nhl
|
||||
|
||||
|
||||
### SPORT
|
||||
|
||||
propose_create_sport nathan "2017-08-16-T08:20:01" [["en","Tennis"], ["pl","Tenis"]] 1
|
||||
|
||||
// proposal
|
||||
#get_object 1.10.10
|
||||
|
||||
approve_proposal nathan 1.10.10 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
#get_object 1.16.2
|
||||
|
||||
|
||||
|
||||
|
||||
### EVENT GROUOP
|
||||
|
||||
propose_create_event_group nathan "2017-08-16-T08:25:01" [ ["en", "Wimbledon"] ] "1.16.2" 1
|
||||
|
||||
approve_proposal nathan 1.10.12 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
// proposal
|
||||
#get_object 1.10.11
|
||||
|
||||
|
||||
//get_object 1.17.1
|
||||
|
||||
propose_update_event_group nathan "2017-08-16-T09:20:01" "1.17.2" "1.16.2" [ ["en", "US Open"] ] 1
|
||||
approve_proposal nathan 1.10.12 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
approve_proposal nathan 1.10.13 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
|
||||
//get_object 1.17.2
|
||||
|
||||
|
||||
### EVENT
|
||||
|
||||
propose_create_event nathan "2017-08-16-T16:15:01" [["en", "R. Federer/T. Berdych"]] [["en","2017"]] "2017-09-16-T07:47:01" "1.17.1" 1
|
||||
propose_create_event nathan "2017-08-16-T16:15:01" [["en", "M. Cilic/S. Querrye"]] [["en","2017"]] "2017-09-16-T07:47:01" "1.17.1" 1
|
||||
|
||||
approve_proposal nathan 1.10.14 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
approve_proposal nathan 1.10.15 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.18.1
|
||||
//get_object 1.18.2
|
||||
|
||||
|
||||
# RULES
|
||||
|
||||
propose_create_betting_market_rules nathan "2017-08-16-T16:20:01" [["en","Tennis Rules v1.0"]] [["en", "The winner is the player who wins the last ball in the match.."]] 1
|
||||
|
||||
approve_proposal nathan 1.10.16 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
0.14
|
||||
//get_object 1.19.?
|
||||
|
||||
|
||||
### BETTING_MARKET GROUP
|
||||
|
||||
propose_create_betting_market_group nathan "2017-08-16-T16:25:01" [["en", "Moneyline 1st sf men"]] "1.18.1" "1.19.1" "1.3.0" 1
|
||||
propose_create_betting_market_group nathan "2017-08-16-T16:25:01" [["en", "Moneyline 2nd sf men"]] "1.18.2" "1.19.1" "1.3.0" 1
|
||||
|
||||
approve_proposal nathan 1.10.17 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
approve_proposal nathan 1.10.18 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.20.0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### FINAL
|
||||
|
||||
propose_create_event nathan "2017-08-16-T16:30:01" [["en", "R. Federer/M. Cilic"]] [["en","2017"]] "2017-09-16-T07:47:01" "1.17.1" 1
|
||||
approve_proposal nathan 1.10.19 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
|
||||
propose_create_betting_market_group nathan "2017-08-16-T16:35:01" [["en", "Moneyline final men"]] "1.18.3" "1.19.1" "1.3.0" 1
|
||||
approve_proposal nathan 1.10.20 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### BETTING_MARKET
|
||||
|
||||
propose_create_betting_market nathan "2017-08-16-T17:05:01" "1.20.1" [["en", "T. Berdych defeats R. Federer"}]] [] 1
|
||||
propose_create_betting_market nathan "2017-08-16-T17:05:02" "1.20.1" [["en","R. Federer defeats T. Berdych"]] [] 1
|
||||
|
||||
approve_proposal nathan 1.10.21 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
approve_proposal nathan 1.10.22 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.21.2
|
||||
//get_object 1.21.3
|
||||
|
||||
//list_betting_markets 1.20.1
|
||||
|
||||
|
||||
|
||||
propose_create_betting_market nathan "2017-08-16-T17:10:01" "1.20.2" [["en", "M. Cilic defeats S. Querrey"]] [] 1
|
||||
propose_create_betting_market nathan "2017-08-16-T17:10:02" "1.20.2" [["en","S. Querrey defeats M. Cilic"]] [] 1
|
||||
|
||||
approve_proposal nathan 1.10.23 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
approve_proposal nathan 1.10.24 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.21.4
|
||||
//get_object 1.21.5
|
||||
|
||||
//list_betting_markets 1.20.2
|
||||
|
||||
|
||||
|
||||
|
||||
propose_create_betting_market nathan "2017-08-16-T17:15:01" "1.20.3" [["en", "R. Federer defeats M. Cilic"]] [] 1
|
||||
propose_create_betting_market nathan "2017-08-16-T17:15:02" "1.20.3" [["en","M. Cilic defeats R. Federer"]] [] 1
|
||||
|
||||
approve_proposal nathan 1.10.25 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
approve_proposal nathan 1.10.26 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.21.6
|
||||
//get_object 1.21.7
|
||||
|
||||
//
|
||||
|
||||
|
||||
|
||||
# PLACE_BET
|
||||
|
||||
#place_bet bob "1.21.2" 1 { "amount" : 100 } 20000 20000 true
|
||||
#place_bet alice "1.21.2" 0 { "amount" : 100 } 20000 20000 true
|
||||
|
||||
|
||||
place_bet alice "1.21.1" 1 "123" "PPY" 200 true
|
||||
place_bet bob "1.21.1" 0 "123" "PPY" 200 true
|
||||
|
||||
|
||||
|
||||
# RESOLVE
|
||||
|
||||
propose_resolve_betting_market_group nathan "2017-08-16-T17:50:01" "1.20.2" [ [ "1.21.4" , 0] [ "1.21.5" , 1] ] 1
|
||||
|
||||
approve_proposal nathan 1.10.27 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
|
||||
|
||||
1113
betting_simulator.html
Normal file
1113
betting_simulator.html
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -69,6 +69,6 @@ appenders=stderr
|
|||
|
||||
# route messages sent to the "p2p" logger to the p2p appender declared above
|
||||
[logger.p2p]
|
||||
level=debug
|
||||
level=info
|
||||
appenders=p2p
|
||||
|
||||
|
|
|
|||
2
docs
2
docs
|
|
@ -1 +1 @@
|
|||
Subproject commit 97435c1a622e41e0a5fc1be72aaadea62e1b7adb
|
||||
Subproject commit bd792d02c70e7686da2b27197eba4fd6df30477c
|
||||
540
genesis.json
540
genesis.json
File diff suppressed because one or more lines are too long
216158
genesis/genesis.json
Normal file
216158
genesis/genesis.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1 +1 @@
|
|||
2.0.160208
|
||||
2.0.170522
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ add_library( graphene_app
|
|||
${EGENESIS_HEADERS}
|
||||
)
|
||||
|
||||
target_link_libraries( graphene_app graphene_market_history graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities )
|
||||
# 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_utilities graphene_debug_witness )
|
||||
target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie )
|
||||
target_include_directories( graphene_app
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" )
|
||||
|
|
@ -27,3 +29,4 @@ INSTALL( TARGETS
|
|||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/app" )
|
||||
|
|
|
|||
|
|
@ -36,9 +36,11 @@
|
|||
#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>
|
||||
#include <fc/thread/future.hpp>
|
||||
|
||||
namespace graphene { namespace app {
|
||||
|
||||
|
|
@ -79,6 +81,10 @@ namespace graphene { namespace app {
|
|||
{
|
||||
_database_api = std::make_shared< database_api >( std::ref( *_app.chain_database() ) );
|
||||
}
|
||||
else if( api_name == "block_api" )
|
||||
{
|
||||
_block_api = std::make_shared< block_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 ) );
|
||||
|
|
@ -95,9 +101,39 @@ namespace graphene { namespace app {
|
|||
{
|
||||
_crypto_api = std::make_shared< crypto_api >();
|
||||
}
|
||||
else if( api_name == "asset_api" )
|
||||
{
|
||||
_asset_api = std::make_shared< asset_api >( std::ref( *_app.chain_database() ) );
|
||||
}
|
||||
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) );
|
||||
}
|
||||
else if( api_name == "bookie_api" )
|
||||
{
|
||||
// can only enable this API if the plugin was loaded
|
||||
if( _app.get_plugin( "bookie" ) )
|
||||
_bookie_api = std::make_shared<graphene::bookie::bookie_api>(std::ref(_app));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// block_api
|
||||
block_api::block_api(graphene::chain::database& db) : _db(db) { }
|
||||
block_api::~block_api() { }
|
||||
|
||||
vector<optional<signed_block>> block_api::get_blocks(uint32_t block_num_from, uint32_t block_num_to)const
|
||||
{
|
||||
FC_ASSERT( block_num_to >= block_num_from );
|
||||
vector<optional<signed_block>> res;
|
||||
for(uint32_t block_num=block_num_from; block_num<=block_num_to; block_num++) {
|
||||
res.push_back(_db.fetch_block_by_number(block_num));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
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); });
|
||||
|
|
@ -131,6 +167,16 @@ namespace graphene { namespace app {
|
|||
_app.p2p_node()->broadcast_transaction(trx);
|
||||
}
|
||||
|
||||
fc::variant network_broadcast_api::broadcast_transaction_synchronous(const signed_transaction& trx)
|
||||
{
|
||||
fc::promise<fc::variant>::ptr prom( new fc::promise<fc::variant>() );
|
||||
broadcast_transaction_with_callback( [=]( const fc::variant& v ){
|
||||
prom->set_value(v);
|
||||
}, trx );
|
||||
|
||||
return fc::future<fc::variant>(prom).wait();
|
||||
}
|
||||
|
||||
void network_broadcast_api::broadcast_block( const signed_block& b )
|
||||
{
|
||||
_app.chain_database()->push_block(b);
|
||||
|
|
@ -187,6 +233,12 @@ namespace graphene { namespace app {
|
|||
return *_network_broadcast_api;
|
||||
}
|
||||
|
||||
fc::api<block_api> login_api::block()const
|
||||
{
|
||||
FC_ASSERT(_block_api);
|
||||
return *_block_api;
|
||||
}
|
||||
|
||||
fc::api<network_node_api> login_api::network_node()const
|
||||
{
|
||||
FC_ASSERT(_network_node_api);
|
||||
|
|
@ -211,6 +263,25 @@ namespace graphene { namespace app {
|
|||
return *_crypto_api;
|
||||
}
|
||||
|
||||
fc::api<asset_api> login_api::asset() const
|
||||
{
|
||||
FC_ASSERT(_asset_api);
|
||||
return *_asset_api;
|
||||
}
|
||||
|
||||
fc::api<graphene::debug_witness::debug_api> login_api::debug() const
|
||||
{
|
||||
FC_ASSERT(_debug_api);
|
||||
return *_debug_api;
|
||||
}
|
||||
|
||||
fc::api<graphene::bookie::bookie_api> login_api::bookie() const
|
||||
{
|
||||
FC_ASSERT(_bookie_api);
|
||||
return *_bookie_api;
|
||||
}
|
||||
|
||||
#if 0
|
||||
vector<account_id_type> get_relevant_accounts( const object* obj )
|
||||
{
|
||||
vector<account_id_type> result;
|
||||
|
|
@ -293,6 +364,26 @@ namespace graphene { namespace app {
|
|||
/** these are free from any accounts */
|
||||
break;
|
||||
}
|
||||
case sport_object_type:
|
||||
case event_group_object_type:
|
||||
case event_object_type:
|
||||
case betting_market_group_object_type:
|
||||
case betting_market_object_type:
|
||||
/** these are free from any accounts */
|
||||
break;
|
||||
case bet_object_type:{
|
||||
const auto& aobj = dynamic_cast<const bet_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
result.push_back( aobj->bettor_id );
|
||||
} 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 )
|
||||
|
|
@ -350,10 +441,15 @@ namespace graphene { namespace app {
|
|||
break;
|
||||
case impl_fba_accumulator_object_type:
|
||||
break;
|
||||
case impl_betting_market_position_object_type:
|
||||
break;
|
||||
case impl_global_betting_statistics_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
|
||||
{
|
||||
|
|
@ -380,28 +476,105 @@ namespace graphene { namespace app {
|
|||
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
|
||||
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());
|
||||
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_account_history_operations( account_id_type account,
|
||||
int operation_id,
|
||||
operation_history_id_type start,
|
||||
operation_history_id_type stop,
|
||||
unsigned limit) 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 ) {
|
||||
|
||||
if(node->operation_id(db).op.which() == operation_id)
|
||||
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;
|
||||
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->id;
|
||||
while(node && node->operation_id.instance.value > stop.instance.value && result.size() < limit)
|
||||
if( start == 0 )
|
||||
start = stats.total_ops;
|
||||
else
|
||||
start = min( stats.total_ops, start );
|
||||
|
||||
|
||||
if( start >= stop && start > stats.removed_ops && limit > 0 )
|
||||
{
|
||||
if(node->id.instance() <= start.instance.value)
|
||||
result.push_back(node->operation_id(db));
|
||||
if(node->next == account_transaction_history_id_type())
|
||||
node = nullptr;
|
||||
else node = db.find(node->next);
|
||||
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 ) );
|
||||
|
||||
do
|
||||
{
|
||||
--itr;
|
||||
result.push_back( itr->operation_id(db) );
|
||||
}
|
||||
while ( itr != itr_stop && result.size() < limit );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<account_balance_object> history_api::list_core_accounts()const
|
||||
{
|
||||
auto list = _app.get_plugin<accounts_list_plugin>( "accounts_list" );
|
||||
FC_ASSERT( list );
|
||||
return list->list_accounts();
|
||||
}
|
||||
|
||||
flat_set<uint32_t> history_api::get_market_history_buckets()const
|
||||
{
|
||||
auto hist = _app.get_plugin<market_history_plugin>( "market_history" );
|
||||
|
|
@ -505,4 +678,78 @@ namespace graphene { namespace app {
|
|||
return fc::ecc::range_get_info( proof );
|
||||
}
|
||||
|
||||
// asset_api
|
||||
asset_api::asset_api(graphene::chain::database& db) : _db(db) { }
|
||||
asset_api::~asset_api() { }
|
||||
|
||||
vector<account_asset_balance> asset_api::get_asset_holders( asset_id_type asset_id, uint32_t start, uint32_t limit ) const {
|
||||
FC_ASSERT(limit <= 100);
|
||||
|
||||
const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >();
|
||||
auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) );
|
||||
|
||||
vector<account_asset_balance> result;
|
||||
|
||||
uint32_t index = 0;
|
||||
for( const account_balance_object& bal : boost::make_iterator_range( range.first, range.second ) )
|
||||
{
|
||||
if( result.size() >= limit )
|
||||
break;
|
||||
|
||||
if( bal.balance.value == 0 )
|
||||
continue;
|
||||
|
||||
if( index++ < start )
|
||||
continue;
|
||||
|
||||
const auto account = _db.find(bal.owner);
|
||||
|
||||
account_asset_balance aab;
|
||||
aab.name = account->name;
|
||||
aab.account_id = account->id;
|
||||
aab.amount = bal.balance.value;
|
||||
|
||||
result.push_back(aab);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
// get number of asset holders.
|
||||
int asset_api::get_asset_holders_count( asset_id_type asset_id ) const {
|
||||
|
||||
const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >();
|
||||
auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) );
|
||||
|
||||
int count = boost::distance(range) - 1;
|
||||
|
||||
return count;
|
||||
}
|
||||
// function to get vector of system assets with holders count.
|
||||
vector<asset_holders> asset_api::get_all_asset_holders() const {
|
||||
|
||||
vector<asset_holders> result;
|
||||
|
||||
vector<asset_id_type> total_assets;
|
||||
for( const asset_object& asset_obj : _db.get_index_type<asset_index>().indices() )
|
||||
{
|
||||
const auto& dasset_obj = asset_obj.dynamic_asset_data_id(_db);
|
||||
|
||||
asset_id_type asset_id;
|
||||
asset_id = dasset_obj.id;
|
||||
|
||||
const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >();
|
||||
auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) );
|
||||
|
||||
int count = boost::distance(range) - 1;
|
||||
|
||||
asset_holders ah;
|
||||
ah.asset_id = asset_id;
|
||||
ah.count = count;
|
||||
|
||||
result.push_back(ah);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} } // graphene::app
|
||||
|
|
|
|||
|
|
@ -28,15 +28,12 @@
|
|||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/time/time.hpp>
|
||||
|
||||
#include <graphene/egenesis/egenesis.hpp>
|
||||
|
||||
#include <graphene/net/core_messages.hpp>
|
||||
#include <graphene/net/exceptions.hpp>
|
||||
|
||||
#include <graphene/time/time.hpp>
|
||||
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
#include <graphene/chain/worker_evaluator.hpp>
|
||||
|
||||
|
|
@ -46,10 +43,12 @@
|
|||
#include <fc/rpc/api_connection.hpp>
|
||||
#include <fc/rpc/websocket_api.hpp>
|
||||
#include <fc/network/resolve.hpp>
|
||||
#include <fc/crypto/base64.hpp>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/signals2.hpp>
|
||||
#include <boost/range/algorithm/reverse.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
|
@ -125,12 +124,17 @@ namespace detail {
|
|||
auto seeds = _options->at("seed-node").as<vector<string>>();
|
||||
for( const string& endpoint_string : seeds )
|
||||
{
|
||||
std::vector<fc::ip::endpoint> endpoints = resolve_string_to_ip_endpoints(endpoint_string);
|
||||
for (const fc::ip::endpoint& endpoint : endpoints)
|
||||
{
|
||||
ilog("Adding seed node ${endpoint}", ("endpoint", endpoint));
|
||||
_p2p_network->add_node(endpoint);
|
||||
_p2p_network->connect_to_endpoint(endpoint);
|
||||
try {
|
||||
std::vector<fc::ip::endpoint> endpoints = resolve_string_to_ip_endpoints(endpoint_string);
|
||||
for (const fc::ip::endpoint& endpoint : endpoints)
|
||||
{
|
||||
ilog("Adding seed node ${endpoint}", ("endpoint", endpoint));
|
||||
_p2p_network->add_node(endpoint);
|
||||
_p2p_network->connect_to_endpoint(endpoint);
|
||||
}
|
||||
} catch( const fc::exception& e ) {
|
||||
wlog( "caught exception ${e} while adding seed node ${endpoint}",
|
||||
("e", e.to_detail_string())("endpoint", endpoint_string) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -141,40 +145,37 @@ namespace detail {
|
|||
auto seeds = fc::json::from_string(seeds_str).as<vector<string>>();
|
||||
for( const string& endpoint_string : seeds )
|
||||
{
|
||||
std::vector<fc::ip::endpoint> endpoints = resolve_string_to_ip_endpoints(endpoint_string);
|
||||
for (const fc::ip::endpoint& endpoint : endpoints)
|
||||
{
|
||||
ilog("Adding seed node ${endpoint}", ("endpoint", endpoint));
|
||||
_p2p_network->add_node(endpoint);
|
||||
try {
|
||||
std::vector<fc::ip::endpoint> endpoints = resolve_string_to_ip_endpoints(endpoint_string);
|
||||
for (const fc::ip::endpoint& endpoint : endpoints)
|
||||
{
|
||||
ilog("Adding seed node ${endpoint}", ("endpoint", endpoint));
|
||||
_p2p_network->add_node(endpoint);
|
||||
}
|
||||
} catch( const fc::exception& e ) {
|
||||
wlog( "caught exception ${e} while adding seed node ${endpoint}",
|
||||
("e", e.to_detail_string())("endpoint", endpoint_string) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// https://bitsharestalk.org/index.php/topic,23715.0.html
|
||||
vector<string> seeds = {
|
||||
"faucet.bitshares.org:1776",
|
||||
"bitshares.openledger.info:1776",
|
||||
"114.92.254.159:62015",
|
||||
"seed.blocktrades.us:1776",
|
||||
"seed04.bitsharesnodes.com:1776", // thom
|
||||
"seed05.bitsharesnodes.com:1776", // thom
|
||||
"seed06.bitsharesnodes.com:1776", // thom
|
||||
"seed07.bitsharesnodes.com:1776", // thom
|
||||
"128.199.131.4:1777", // cube
|
||||
"54.85.252.77:39705", // lafona
|
||||
"104.236.144.84:1777", // puppies
|
||||
"40.127.190.171:1777", // betax
|
||||
"185.25.22.21:1776", // liondani (greece)
|
||||
"23.95.43.126:50696", // iHashFury
|
||||
"109.73.172.144:50696" // iHashFury
|
||||
"peerplays-dev.blocktrades.info:2776"
|
||||
};
|
||||
for( const string& endpoint_string : seeds )
|
||||
{
|
||||
std::vector<fc::ip::endpoint> endpoints = resolve_string_to_ip_endpoints(endpoint_string);
|
||||
for (const fc::ip::endpoint& endpoint : endpoints)
|
||||
{
|
||||
ilog("Adding seed node ${endpoint}", ("endpoint", endpoint));
|
||||
_p2p_network->add_node(endpoint);
|
||||
try {
|
||||
std::vector<fc::ip::endpoint> endpoints = resolve_string_to_ip_endpoints(endpoint_string);
|
||||
for (const fc::ip::endpoint& endpoint : endpoints)
|
||||
{
|
||||
ilog("Adding seed node ${endpoint}", ("endpoint", endpoint));
|
||||
_p2p_network->add_node(endpoint);
|
||||
}
|
||||
} catch( const fc::exception& e ) {
|
||||
wlog( "caught exception ${e} while adding seed node ${endpoint}",
|
||||
("e", e.to_detail_string())("endpoint", endpoint_string) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -219,21 +220,49 @@ namespace detail {
|
|||
FC_CAPTURE_AND_RETHROW((endpoint_string))
|
||||
}
|
||||
|
||||
void new_connection( const fc::http::websocket_connection_ptr& c )
|
||||
{
|
||||
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(*c);
|
||||
auto login = std::make_shared<graphene::app::login_api>( std::ref(*_self) );
|
||||
login->enable_api("database_api");
|
||||
|
||||
wsc->register_api(login->database());
|
||||
wsc->register_api(fc::api<graphene::app::login_api>(login));
|
||||
|
||||
wsc->register_api(fc::api<graphene::app::login_api>(login));
|
||||
|
||||
c->set_session_data( wsc );
|
||||
|
||||
std::string username = "*";
|
||||
std::string password = "*";
|
||||
|
||||
// Try to extract login information from "Authorization" header if present
|
||||
std::string auth = c->get_request_header("Authorization");
|
||||
if( boost::starts_with(auth, "Basic ") ) {
|
||||
|
||||
FC_ASSERT( auth.size() > 6 );
|
||||
auto user_pass = fc::base64_decode(auth.substr(6));
|
||||
|
||||
std::vector<std::string> parts;
|
||||
boost::split( parts, user_pass, boost::is_any_of(":") );
|
||||
|
||||
FC_ASSERT(parts.size() == 2);
|
||||
|
||||
username = parts[0];
|
||||
password = parts[1];
|
||||
}
|
||||
|
||||
login->login(username, password);
|
||||
}
|
||||
|
||||
void reset_websocket_server()
|
||||
{ try {
|
||||
if( !_options->count("rpc-endpoint") )
|
||||
return;
|
||||
|
||||
_websocket_server = std::make_shared<fc::http::websocket_server>();
|
||||
_websocket_server->on_connection( std::bind(&application_impl::new_connection, this, std::placeholders::_1) );
|
||||
|
||||
_websocket_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){
|
||||
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(*c);
|
||||
auto login = std::make_shared<graphene::app::login_api>( std::ref(*_self) );
|
||||
auto db_api = std::make_shared<graphene::app::database_api>( std::ref(*_self->chain_database()) );
|
||||
wsc->register_api(fc::api<graphene::app::database_api>(db_api));
|
||||
wsc->register_api(fc::api<graphene::app::login_api>(login));
|
||||
c->set_session_data( wsc );
|
||||
});
|
||||
ilog("Configured websocket rpc to listen on ${ip}", ("ip",_options->at("rpc-endpoint").as<string>()));
|
||||
_websocket_server->listen( fc::ip::endpoint::from_string(_options->at("rpc-endpoint").as<string>()) );
|
||||
_websocket_server->start_accept();
|
||||
|
|
@ -252,15 +281,8 @@ namespace detail {
|
|||
|
||||
string password = _options->count("server-pem-password") ? _options->at("server-pem-password").as<string>() : "";
|
||||
_websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>( _options->at("server-pem").as<string>(), password );
|
||||
_websocket_tls_server->on_connection( std::bind(&application_impl::new_connection, this, std::placeholders::_1) );
|
||||
|
||||
_websocket_tls_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){
|
||||
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(*c);
|
||||
auto login = std::make_shared<graphene::app::login_api>( std::ref(*_self) );
|
||||
auto db_api = std::make_shared<graphene::app::database_api>( std::ref(*_self->chain_database()) );
|
||||
wsc->register_api(fc::api<graphene::app::database_api>(db_api));
|
||||
wsc->register_api(fc::api<graphene::app::login_api>(login));
|
||||
c->set_session_data( wsc );
|
||||
});
|
||||
ilog("Configured websocket TLS rpc to listen on ${ip}", ("ip",_options->at("rpc-tls-endpoint").as<string>()));
|
||||
_websocket_tls_server->listen( fc::ip::endpoint::from_string(_options->at("rpc-tls-endpoint").as<string>()) );
|
||||
_websocket_tls_server->start_accept();
|
||||
|
|
@ -301,7 +323,7 @@ namespace detail {
|
|||
bool modified_genesis = false;
|
||||
if( _options->count("genesis-timestamp") )
|
||||
{
|
||||
genesis.initial_timestamp = fc::time_point_sec( graphene::time::now() ) + genesis.initial_parameters.block_interval + _options->at("genesis-timestamp").as<uint32_t>();
|
||||
genesis.initial_timestamp = fc::time_point_sec( fc::time_point::now() ) + genesis.initial_parameters.block_interval + _options->at("genesis-timestamp").as<uint32_t>();
|
||||
genesis.initial_timestamp -= genesis.initial_timestamp.sec_since_epoch() % genesis.initial_parameters.block_interval;
|
||||
modified_genesis = true;
|
||||
std::cerr << "Used genesis timestamp: " << genesis.initial_timestamp.to_iso_string() << " (PLEASE RECORD THIS)\n";
|
||||
|
|
@ -352,79 +374,67 @@ namespace detail {
|
|||
}
|
||||
_chain_db->add_checkpoints( loaded_checkpoints );
|
||||
|
||||
if( _options->count("replay-blockchain") )
|
||||
bool replay = false;
|
||||
std::string replay_reason = "reason not provided";
|
||||
|
||||
// never replay if data dir is empty
|
||||
if( fc::exists( _data_dir ) && fc::directory_iterator( _data_dir ) != fc::directory_iterator() )
|
||||
{
|
||||
ilog("Replaying blockchain on user request.");
|
||||
_chain_db->reindex(_data_dir/"blockchain", initial_state());
|
||||
} else if( clean ) {
|
||||
|
||||
auto is_new = [&]() -> bool
|
||||
if( _options->count("replay-blockchain") )
|
||||
{
|
||||
// directory doesn't exist
|
||||
if( !fc::exists( _data_dir ) )
|
||||
return true;
|
||||
// if directory exists but is empty, return true; else false.
|
||||
return ( fc::directory_iterator( _data_dir ) == fc::directory_iterator() );
|
||||
};
|
||||
|
||||
auto is_outdated = [&]() -> bool
|
||||
replay = true;
|
||||
replay_reason = "replay-blockchain argument specified";
|
||||
}
|
||||
else if( !clean )
|
||||
{
|
||||
if( !fc::exists( _data_dir / "db_version" ) )
|
||||
return true;
|
||||
std::string version_str;
|
||||
fc::read_file_contents( _data_dir / "db_version", version_str );
|
||||
return (version_str != GRAPHENE_CURRENT_DB_VERSION);
|
||||
};
|
||||
|
||||
bool need_reindex = (!is_new() && is_outdated());
|
||||
std::string reindex_reason = "version upgrade";
|
||||
|
||||
if( !need_reindex )
|
||||
replay = true;
|
||||
replay_reason = "unclean shutdown detected";
|
||||
}
|
||||
else if( !fc::exists( _data_dir / "db_version" ) )
|
||||
{
|
||||
try
|
||||
replay = true;
|
||||
replay_reason = "db_version file not found";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string version_string;
|
||||
fc::read_file_contents( _data_dir / "db_version", version_string );
|
||||
|
||||
if( version_string != GRAPHENE_CURRENT_DB_VERSION )
|
||||
{
|
||||
_chain_db->open(_data_dir / "blockchain", initial_state);
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
ilog( "caught exception ${e} in open()", ("e", e.to_detail_string()) );
|
||||
need_reindex = true;
|
||||
reindex_reason = "exception in open()";
|
||||
replay = true;
|
||||
replay_reason = "db_version file content mismatch";
|
||||
}
|
||||
}
|
||||
|
||||
if( need_reindex )
|
||||
{
|
||||
ilog("Replaying blockchain due to ${reason}", ("reason", reindex_reason) );
|
||||
|
||||
fc::remove_all( _data_dir / "db_version" );
|
||||
_chain_db->reindex(_data_dir / "blockchain", initial_state());
|
||||
|
||||
// doing this down here helps ensure that DB will be wiped
|
||||
// if any of the above steps were interrupted on a previous run
|
||||
if( !fc::exists( _data_dir / "db_version" ) )
|
||||
{
|
||||
std::ofstream db_version(
|
||||
(_data_dir / "db_version").generic_string().c_str(),
|
||||
std::ios::out | std::ios::binary | std::ios::trunc );
|
||||
std::string version_string = GRAPHENE_CURRENT_DB_VERSION;
|
||||
db_version.write( version_string.c_str(), version_string.size() );
|
||||
db_version.close();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
wlog("Detected unclean shutdown. Replaying blockchain...");
|
||||
_chain_db->reindex(_data_dir / "blockchain", initial_state());
|
||||
}
|
||||
|
||||
if (!_options->count("genesis-json") &&
|
||||
_chain_db->get_chain_id() != graphene::egenesis::get_egenesis_chain_id()) {
|
||||
elog("Detected old database. Nuking and starting over.");
|
||||
_chain_db->wipe(_data_dir / "blockchain", true);
|
||||
_chain_db.reset();
|
||||
_chain_db = std::make_shared<chain::database>();
|
||||
_chain_db->add_checkpoints(loaded_checkpoints);
|
||||
_chain_db->open(_data_dir / "blockchain", initial_state);
|
||||
if( !replay )
|
||||
{
|
||||
try
|
||||
{
|
||||
_chain_db->open( _data_dir / "blockchain", initial_state );
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
ilog( "Caught exception ${e} in open()", ("e", e.to_detail_string()) );
|
||||
|
||||
replay = true;
|
||||
replay_reason = "exception in open()";
|
||||
}
|
||||
}
|
||||
|
||||
if( replay )
|
||||
{
|
||||
ilog( "Replaying blockchain due to: ${reason}", ("reason", replay_reason) );
|
||||
|
||||
fc::remove_all( _data_dir / "db_version" );
|
||||
_chain_db->reindex( _data_dir / "blockchain", initial_state() );
|
||||
|
||||
const auto mode = std::ios::out | std::ios::binary | std::ios::trunc;
|
||||
std::ofstream db_version( (_data_dir / "db_version").generic_string().c_str(), mode );
|
||||
std::string version_string = GRAPHENE_CURRENT_DB_VERSION;
|
||||
db_version.write( version_string.c_str(), version_string.size() );
|
||||
db_version.close();
|
||||
}
|
||||
|
||||
if( _options->count("force-validate") )
|
||||
|
|
@ -433,8 +443,6 @@ namespace detail {
|
|||
_force_validate = true;
|
||||
}
|
||||
|
||||
graphene::time::now();
|
||||
|
||||
if( _options->count("api-access") )
|
||||
_apiaccess = fc::json::from_file( _options->at("api-access").as<boost::filesystem::path>() )
|
||||
.as<api_access>();
|
||||
|
|
@ -450,6 +458,7 @@ namespace detail {
|
|||
wild_access.allowed_apis.push_back( "network_broadcast_api" );
|
||||
wild_access.allowed_apis.push_back( "history_api" );
|
||||
wild_access.allowed_apis.push_back( "crypto_api" );
|
||||
wild_access.allowed_apis.push_back( "bookie_api" );
|
||||
_apiaccess.permission_map["*"] = wild_access;
|
||||
}
|
||||
|
||||
|
|
@ -458,6 +467,7 @@ namespace detail {
|
|||
reset_websocket_tls_server();
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
|
||||
optional< api_access_info > get_api_access_info(const string& username)const
|
||||
{
|
||||
optional< api_access_info > result;
|
||||
|
|
@ -503,7 +513,7 @@ namespace detail {
|
|||
std::vector<fc::uint160_t>& contained_transaction_message_ids) override
|
||||
{ try {
|
||||
|
||||
auto latency = graphene::time::now() - blk_msg.block.timestamp;
|
||||
auto latency = fc::time_point::now() - blk_msg.block.timestamp;
|
||||
if (!sync_mode || blk_msg.block.block_num() % 10000 == 0)
|
||||
{
|
||||
const auto& witness = blk_msg.block.witness(*_chain_db);
|
||||
|
|
@ -516,6 +526,7 @@ namespace detail {
|
|||
("w",witness_account.name)
|
||||
("i",last_irr)("d",blk_msg.block.block_num()-last_irr) );
|
||||
}
|
||||
FC_ASSERT( (latency.count()/1000) > -5000, "Rejecting block with timestamp in the future" );
|
||||
|
||||
try {
|
||||
// TODO: in the case where this block is valid but on a fork that's too old for us to switch to,
|
||||
|
|
@ -813,6 +824,9 @@ namespace detail {
|
|||
return synopsis; // we have no blocks
|
||||
}
|
||||
|
||||
if( low_block_num == 0)
|
||||
low_block_num = 1;
|
||||
|
||||
// at this point:
|
||||
// low_block_num is the block before the first block we can undo,
|
||||
// non_fork_high_block_num is the block before the fork (if the peer is on a fork, or otherwise it is the same as high_block_num)
|
||||
|
|
@ -874,12 +888,6 @@ namespace detail {
|
|||
return fc::time_point_sec::min();
|
||||
} FC_CAPTURE_AND_RETHROW( (block_id) ) }
|
||||
|
||||
/** returns graphene::time::now() */
|
||||
virtual fc::time_point_sec get_blockchain_now() override
|
||||
{
|
||||
return graphene::time::now();
|
||||
}
|
||||
|
||||
virtual item_hash_t get_head_block_id() const override
|
||||
{
|
||||
return _chain_db->head_block_id();
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
#include <graphene/app/database_api.hpp>
|
||||
#include <graphene/chain/get_config.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
|
||||
#include <fc/bloom_filter.hpp>
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
|
@ -31,6 +33,8 @@
|
|||
#include <fc/crypto/hex.hpp>
|
||||
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <boost/rational.hpp>
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
|
||||
#include <cctype>
|
||||
|
||||
|
|
@ -39,6 +43,8 @@
|
|||
|
||||
#define GET_REQUIRED_FEES_MAX_RECURSION 4
|
||||
|
||||
typedef std::map< std::pair<graphene::chain::asset_id_type, graphene::chain::asset_id_type>, std::vector<fc::variant> > market_queue_type;
|
||||
|
||||
namespace graphene { namespace app {
|
||||
|
||||
class database_api_impl;
|
||||
|
|
@ -54,13 +60,14 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
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_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create );
|
||||
void set_pending_transaction_callback( std::function<void(const variant&)> cb );
|
||||
void set_block_applied_callback( std::function<void(const variant& block_id)> cb );
|
||||
void cancel_all_subscriptions();
|
||||
|
||||
// Blocks and transactions
|
||||
optional<block_header> get_block_header(uint32_t block_num)const;
|
||||
map<uint32_t, optional<block_header>> get_block_header_batch(const vector<uint32_t> block_nums)const;
|
||||
optional<signed_block> get_block(uint32_t block_num)const;
|
||||
processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const;
|
||||
|
||||
|
|
@ -70,9 +77,11 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
fc::variant_object get_config()const;
|
||||
chain_id_type get_chain_id()const;
|
||||
dynamic_global_property_object get_dynamic_global_properties()const;
|
||||
global_betting_statistics_object get_global_betting_statistics() const;
|
||||
|
||||
// Keys
|
||||
vector<vector<account_id_type>> get_key_references( vector<public_key_type> key )const;
|
||||
bool is_public_key_registered(string public_key) const;
|
||||
|
||||
// Accounts
|
||||
vector<optional<account_object>> get_accounts(const vector<account_id_type>& account_ids)const;
|
||||
|
|
@ -95,6 +104,15 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
vector<asset_object> list_assets(const string& lower_bound_symbol, uint32_t limit)const;
|
||||
vector<optional<asset_object>> lookup_asset_symbols(const vector<string>& symbols_or_ids)const;
|
||||
|
||||
// Peerplays
|
||||
vector<sport_object> list_sports() const;
|
||||
vector<event_group_object> list_event_groups(sport_id_type sport_id) const;
|
||||
vector<event_object> list_events_in_group(event_group_id_type event_group_id) const;
|
||||
vector<betting_market_group_object> list_betting_market_groups(event_id_type) const;
|
||||
vector<betting_market_object> list_betting_markets(betting_market_group_id_type) const;
|
||||
vector<bet_object> get_unmatched_bets_for_bettor(betting_market_id_type, account_id_type) const;
|
||||
vector<bet_object> get_all_unmatched_bets_for_bettor(account_id_type) const;
|
||||
|
||||
// Markets / feeds
|
||||
vector<limit_order_object> get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const;
|
||||
vector<call_order_object> get_call_orders(asset_id_type a, uint32_t limit)const;
|
||||
|
|
@ -137,6 +155,13 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
// Blinded balances
|
||||
vector<blinded_balance_object> get_blinded_balances( const flat_set<commitment_type>& commitments )const;
|
||||
|
||||
// Tournaments
|
||||
vector<tournament_object> get_tournaments_in_state(tournament_state state, uint32_t limit) const;
|
||||
vector<tournament_object> get_tournaments(tournament_id_type stop, unsigned limit, tournament_id_type start);
|
||||
vector<tournament_object> get_tournaments_by_state(tournament_id_type stop, unsigned limit, tournament_id_type start, tournament_state state);
|
||||
vector<tournament_id_type> get_registered_tournaments(account_id_type account_filter, uint32_t limit) const;
|
||||
|
||||
|
||||
//private:
|
||||
template<typename T>
|
||||
void subscribe_to_item( const T& i )const
|
||||
|
|
@ -157,22 +182,52 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
{
|
||||
if( !_subscribe_callback )
|
||||
return false;
|
||||
return true;
|
||||
|
||||
return _subscribe_filter.contains( i );
|
||||
}
|
||||
|
||||
bool is_impacted_account( const flat_set<account_id_type>& accounts)
|
||||
{
|
||||
if( !_subscribed_accounts.size() || !accounts.size() )
|
||||
return false;
|
||||
|
||||
return std::any_of(accounts.begin(), accounts.end(), [this](const account_id_type& account) {
|
||||
return _subscribed_accounts.find(account) != _subscribed_accounts.end();
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void enqueue_if_subscribed_to_market(const object* obj, market_queue_type& queue, bool full_object=true)
|
||||
{
|
||||
const T* order = dynamic_cast<const T*>(obj);
|
||||
FC_ASSERT( order != nullptr);
|
||||
|
||||
auto market = order->get_market();
|
||||
|
||||
auto sub = _market_subscriptions.find( market );
|
||||
if( sub != _market_subscriptions.end() ) {
|
||||
queue[market].emplace_back( full_object ? obj->to_variant() : fc::variant(obj->id) );
|
||||
}
|
||||
}
|
||||
|
||||
void broadcast_updates( const vector<variant>& updates );
|
||||
void broadcast_market_updates( const market_queue_type& queue);
|
||||
void handle_object_changed(bool force_notify, bool full_object, const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts, std::function<const object*(object_id_type id)> find_object);
|
||||
|
||||
/** called every time a block is applied to report the objects that were changed */
|
||||
void on_objects_changed(const vector<object_id_type>& ids);
|
||||
void on_objects_removed(const vector<const object*>& objs);
|
||||
void on_objects_new(const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts);
|
||||
void on_objects_changed(const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts);
|
||||
void on_objects_removed(const vector<object_id_type>& ids, const vector<const object*>& objs, const flat_set<account_id_type>& impacted_accounts);
|
||||
void on_applied_block();
|
||||
|
||||
mutable fc::bloom_filter _subscribe_filter;
|
||||
bool _notify_remove_create = false;
|
||||
mutable fc::bloom_filter _subscribe_filter;
|
||||
std::set<account_id_type> _subscribed_accounts;
|
||||
std::function<void(const fc::variant&)> _subscribe_callback;
|
||||
std::function<void(const fc::variant&)> _pending_trx_callback;
|
||||
std::function<void(const fc::variant&)> _block_applied_callback;
|
||||
|
||||
boost::signals2::scoped_connection _new_connection;
|
||||
boost::signals2::scoped_connection _change_connection;
|
||||
boost::signals2::scoped_connection _removed_connection;
|
||||
boost::signals2::scoped_connection _applied_block_connection;
|
||||
|
|
@ -195,11 +250,14 @@ database_api::~database_api() {}
|
|||
database_api_impl::database_api_impl( graphene::chain::database& db ):_db(db)
|
||||
{
|
||||
wlog("creating database api ${x}", ("x",int64_t(this)) );
|
||||
_change_connection = _db.changed_objects.connect([this](const vector<object_id_type>& ids) {
|
||||
on_objects_changed(ids);
|
||||
_new_connection = _db.new_objects.connect([this](const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts) {
|
||||
on_objects_new(ids, impacted_accounts);
|
||||
});
|
||||
_removed_connection = _db.removed_objects.connect([this](const vector<const object*>& objs) {
|
||||
on_objects_removed(objs);
|
||||
_change_connection = _db.changed_objects.connect([this](const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts) {
|
||||
on_objects_changed(ids, impacted_accounts);
|
||||
});
|
||||
_removed_connection = _db.removed_objects.connect([this](const vector<object_id_type>& ids, const vector<const object*>& objs, const flat_set<account_id_type>& impacted_accounts) {
|
||||
on_objects_removed(ids, objs, impacted_accounts);
|
||||
});
|
||||
_applied_block_connection = _db.applied_block.connect([this](const signed_block&){ on_applied_block(); });
|
||||
|
||||
|
|
@ -235,10 +293,6 @@ fc::variants database_api_impl::get_objects(const vector<object_id_type>& ids)co
|
|||
this->subscribe_to_item( id );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
elog( "getObjects without subscribe callback??" );
|
||||
}
|
||||
|
||||
fc::variants result;
|
||||
result.reserve(ids.size());
|
||||
|
|
@ -259,24 +313,24 @@ fc::variants database_api_impl::get_objects(const vector<object_id_type>& ids)co
|
|||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
void database_api::set_subscribe_callback( std::function<void(const variant&)> cb, bool clear_filter )
|
||||
void database_api::set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create )
|
||||
{
|
||||
my->set_subscribe_callback( cb, clear_filter );
|
||||
my->set_subscribe_callback( cb, notify_remove_create );
|
||||
}
|
||||
|
||||
void database_api_impl::set_subscribe_callback( std::function<void(const variant&)> cb, bool clear_filter )
|
||||
void database_api_impl::set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create )
|
||||
{
|
||||
edump((clear_filter));
|
||||
//edump((clear_filter));
|
||||
_subscribe_callback = cb;
|
||||
if( clear_filter || !cb )
|
||||
{
|
||||
static fc::bloom_parameters param;
|
||||
param.projected_element_count = 10000;
|
||||
param.false_positive_probability = 1.0/10000;
|
||||
param.maximum_size = 1024*8*8*2;
|
||||
param.compute_optimal_parameters();
|
||||
_subscribe_filter = fc::bloom_filter(param);
|
||||
}
|
||||
_notify_remove_create = notify_remove_create;
|
||||
_subscribed_accounts.clear();
|
||||
|
||||
static fc::bloom_parameters param;
|
||||
param.projected_element_count = 10000;
|
||||
param.false_positive_probability = 1.0/100;
|
||||
param.maximum_size = 1024*8*8*2;
|
||||
param.compute_optimal_parameters();
|
||||
_subscribe_filter = fc::bloom_filter(param);
|
||||
}
|
||||
|
||||
void database_api::set_pending_transaction_callback( std::function<void(const variant&)> cb )
|
||||
|
|
@ -328,6 +382,20 @@ optional<block_header> database_api_impl::get_block_header(uint32_t block_num) c
|
|||
return *result;
|
||||
return {};
|
||||
}
|
||||
map<uint32_t, optional<block_header>> database_api::get_block_header_batch(const vector<uint32_t> block_nums)const
|
||||
{
|
||||
return my->get_block_header_batch( block_nums );
|
||||
}
|
||||
|
||||
map<uint32_t, optional<block_header>> database_api_impl::get_block_header_batch(const vector<uint32_t> block_nums) const
|
||||
{
|
||||
map<uint32_t, optional<block_header>> results;
|
||||
for (const uint32_t block_num : block_nums)
|
||||
{
|
||||
results[block_num] = get_block_header(block_num);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
optional<signed_block> database_api::get_block(uint32_t block_num)const
|
||||
{
|
||||
|
|
@ -417,6 +485,17 @@ dynamic_global_property_object database_api_impl::get_dynamic_global_properties(
|
|||
return _db.get(dynamic_global_property_id_type());
|
||||
}
|
||||
|
||||
global_betting_statistics_object database_api::get_global_betting_statistics() const
|
||||
{
|
||||
return my->get_global_betting_statistics();
|
||||
}
|
||||
|
||||
global_betting_statistics_object database_api_impl::get_global_betting_statistics() const
|
||||
{
|
||||
return _db.get(global_betting_statistics_id_type());
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Keys //
|
||||
|
|
@ -487,6 +566,35 @@ vector<vector<account_id_type>> database_api_impl::get_key_references( vector<pu
|
|||
return final_result;
|
||||
}
|
||||
|
||||
bool database_api::is_public_key_registered(string public_key) const
|
||||
{
|
||||
return my->is_public_key_registered(public_key);
|
||||
}
|
||||
|
||||
bool database_api_impl::is_public_key_registered(string public_key) const
|
||||
{
|
||||
// Short-circuit
|
||||
if (public_key.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search among all keys using an existing map of *current* account keys
|
||||
public_key_type key;
|
||||
try {
|
||||
key = public_key_type(public_key);
|
||||
} catch ( ... ) {
|
||||
// An invalid public key was detected
|
||||
return false;
|
||||
}
|
||||
const auto& idx = _db.get_index_type<account_index>();
|
||||
const auto& aidx = dynamic_cast<const primary_index<account_index>&>(idx);
|
||||
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
|
||||
auto itr = refs.account_to_key_memberships.find(key);
|
||||
bool is_known = itr != refs.account_to_key_memberships.end();
|
||||
|
||||
return is_known;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Accounts //
|
||||
|
|
@ -540,7 +648,8 @@ std::map<std::string, full_account> database_api_impl::get_full_accounts( const
|
|||
|
||||
if( subscribe )
|
||||
{
|
||||
ilog( "subscribe to ${id}", ("id",account->name) );
|
||||
FC_ASSERT( std::distance(_subscribed_accounts.begin(), _subscribed_accounts.end()) <= 100 );
|
||||
_subscribed_accounts.insert( account->get_id() );
|
||||
subscribe_to_item( account->id );
|
||||
}
|
||||
|
||||
|
|
@ -602,6 +711,31 @@ std::map<std::string, full_account> database_api_impl::get_full_accounts( const
|
|||
[&acnt] (const call_order_object& call) {
|
||||
acnt.call_orders.emplace_back(call);
|
||||
});
|
||||
auto settle_range = _db.get_index_type<force_settlement_index>().indices().get<by_account>().equal_range(account->id);
|
||||
std::for_each(settle_range.first, settle_range.second,
|
||||
[&acnt] (const force_settlement_object& settle) {
|
||||
acnt.settle_orders.emplace_back(settle);
|
||||
});
|
||||
|
||||
// get assets issued by user
|
||||
auto asset_range = _db.get_index_type<asset_index>().indices().get<by_issuer>().equal_range(account->id);
|
||||
std::for_each(asset_range.first, asset_range.second,
|
||||
[&acnt] (const asset_object& asset) {
|
||||
acnt.assets.emplace_back(asset.id);
|
||||
});
|
||||
|
||||
// get withdraws permissions
|
||||
auto withdraw_range = _db.get_index_type<withdraw_permission_index>().indices().get<by_from>().equal_range(account->id);
|
||||
std::for_each(withdraw_range.first, withdraw_range.second,
|
||||
[&acnt] (const withdraw_permission_object& withdraw) {
|
||||
acnt.withdraws.emplace_back(withdraw);
|
||||
});
|
||||
|
||||
auto pending_payouts_range =
|
||||
_db.get_index_type<pending_dividend_payout_balance_for_holder_object_index>().indices().get<by_account_dividend_payout>().equal_range(boost::make_tuple(account->id));
|
||||
|
||||
std::copy(pending_payouts_range.first, pending_payouts_range.second, std::back_inserter(acnt.pending_dividend_payments));
|
||||
|
||||
results[account_name_or_id] = acnt;
|
||||
}
|
||||
return results;
|
||||
|
|
@ -878,6 +1012,86 @@ vector<optional<asset_object>> database_api_impl::lookup_asset_symbols(const vec
|
|||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Peerplays //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
vector<sport_object> database_api::list_sports() const
|
||||
{
|
||||
return my->list_sports();
|
||||
}
|
||||
|
||||
vector<sport_object> database_api_impl::list_sports() const
|
||||
{
|
||||
const auto& sport_object_idx = _db.get_index_type<sport_object_index>().indices().get<by_id>();
|
||||
return boost::copy_range<vector<sport_object> >(sport_object_idx);
|
||||
}
|
||||
|
||||
vector<event_group_object> database_api::list_event_groups(sport_id_type sport_id) const
|
||||
{
|
||||
return my->list_event_groups(sport_id);
|
||||
}
|
||||
|
||||
vector<event_group_object> database_api_impl::list_event_groups(sport_id_type sport_id) const
|
||||
{
|
||||
const auto& event_group_idx = _db.get_index_type<event_group_object_index>().indices().get<by_sport_id>();
|
||||
return boost::copy_range<vector<event_group_object> >(event_group_idx.equal_range(sport_id));
|
||||
}
|
||||
|
||||
vector<event_object> database_api::list_events_in_group(event_group_id_type event_group_id) const
|
||||
{
|
||||
return my->list_events_in_group(event_group_id);
|
||||
}
|
||||
|
||||
vector<event_object> database_api_impl::list_events_in_group(event_group_id_type event_group_id) const
|
||||
{
|
||||
const auto& event_idx = _db.get_index_type<event_object_index>().indices().get<by_event_group_id>();
|
||||
return boost::copy_range<vector<event_object> >(event_idx.equal_range(event_group_id));
|
||||
}
|
||||
|
||||
vector<betting_market_group_object> database_api::list_betting_market_groups(event_id_type event_id) const
|
||||
{
|
||||
return my->list_betting_market_groups(event_id);
|
||||
}
|
||||
|
||||
vector<betting_market_group_object> database_api_impl::list_betting_market_groups(event_id_type event_id) const
|
||||
{
|
||||
const auto& betting_market_group_idx = _db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
return boost::copy_range<vector<betting_market_group_object> >(betting_market_group_idx.equal_range(event_id));
|
||||
}
|
||||
|
||||
vector<betting_market_object> database_api::list_betting_markets(betting_market_group_id_type betting_market_group_id) const
|
||||
{
|
||||
return my->list_betting_markets(betting_market_group_id);
|
||||
}
|
||||
|
||||
vector<betting_market_object> database_api_impl::list_betting_markets(betting_market_group_id_type betting_market_group_id) const
|
||||
{
|
||||
const auto& betting_market_idx = _db.get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
||||
return boost::copy_range<vector<betting_market_object> >(betting_market_idx.equal_range(betting_market_group_id));
|
||||
}
|
||||
|
||||
vector<bet_object> database_api::get_unmatched_bets_for_bettor(betting_market_id_type betting_market_id, account_id_type bettor_id) const
|
||||
{
|
||||
return my->get_unmatched_bets_for_bettor(betting_market_id, bettor_id);
|
||||
}
|
||||
|
||||
vector<bet_object> database_api_impl::get_unmatched_bets_for_bettor(betting_market_id_type betting_market_id, account_id_type bettor_id) const
|
||||
{
|
||||
const auto& bet_idx = _db.get_index_type<bet_object_index>().indices().get<by_bettor_and_odds>();
|
||||
return boost::copy_range<vector<bet_object> >(bet_idx.equal_range(std::make_tuple(bettor_id, betting_market_id)));
|
||||
}
|
||||
|
||||
vector<bet_object> database_api::get_all_unmatched_bets_for_bettor(account_id_type bettor_id) const
|
||||
{
|
||||
return my->get_all_unmatched_bets_for_bettor(bettor_id);
|
||||
}
|
||||
|
||||
vector<bet_object> database_api_impl::get_all_unmatched_bets_for_bettor(account_id_type bettor_id) const
|
||||
{
|
||||
const auto& bet_idx = _db.get_index_type<bet_object_index>().indices().get<by_bettor_and_odds>();
|
||||
return boost::copy_range<vector<bet_object> >(bet_idx.equal_range(std::make_tuple(bettor_id)));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Markets / feeds //
|
||||
|
|
@ -998,136 +1212,84 @@ void database_api_impl::unsubscribe_from_market(asset_id_type a, asset_id_type b
|
|||
|
||||
market_ticker database_api::get_ticker( const string& base, const string& quote )const
|
||||
{
|
||||
return my->get_ticker( base, quote );
|
||||
return my->get_ticker( base, quote );
|
||||
}
|
||||
|
||||
market_ticker database_api_impl::get_ticker( const string& base, const string& quote )const
|
||||
{
|
||||
auto assets = lookup_asset_symbols( {base, quote} );
|
||||
FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
|
||||
FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
|
||||
const auto assets = lookup_asset_symbols( {base, quote} );
|
||||
FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
|
||||
FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
|
||||
|
||||
auto base_id = assets[0]->id;
|
||||
auto quote_id = assets[1]->id;
|
||||
market_ticker result;
|
||||
result.base = base;
|
||||
result.quote = quote;
|
||||
result.latest = 0;
|
||||
result.lowest_ask = 0;
|
||||
result.highest_bid = 0;
|
||||
result.percent_change = 0;
|
||||
result.base_volume = 0;
|
||||
result.quote_volume = 0;
|
||||
|
||||
market_ticker result;
|
||||
try {
|
||||
const fc::time_point_sec now = fc::time_point::now();
|
||||
const fc::time_point_sec yesterday = fc::time_point_sec( now.sec_since_epoch() - 86400 );
|
||||
const auto batch_size = 100;
|
||||
|
||||
result.base = base;
|
||||
result.quote = quote;
|
||||
result.base_volume = 0;
|
||||
result.quote_volume = 0;
|
||||
result.percent_change = 0;
|
||||
result.lowest_ask = 0;
|
||||
result.highest_bid = 0;
|
||||
|
||||
auto price_to_real = [&]( const share_type a, int p ) { return double( a.value ) / pow( 10, p ); };
|
||||
|
||||
try {
|
||||
if( base_id > quote_id ) std::swap(base_id, quote_id);
|
||||
|
||||
const auto& bidx = _db.get_index_type<bucket_index>();
|
||||
const auto& by_key_idx = bidx.indices().get<by_key>();
|
||||
uint32_t bucket_size = 86400;
|
||||
auto now = fc::time_point_sec( fc::time_point::now() );
|
||||
|
||||
auto itr = by_key_idx.lower_bound( bucket_key( base_id, quote_id, bucket_size,
|
||||
now - bucket_size ) );
|
||||
|
||||
auto orders = get_order_book( base, quote, 1 );
|
||||
|
||||
if( itr != by_key_idx.end() && itr->key.base == base_id && itr->key.quote == quote_id && itr->key.seconds == bucket_size )
|
||||
{
|
||||
auto trades = get_trade_history( base, quote, now, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 );
|
||||
|
||||
if (assets[0]->id == base_id)
|
||||
{
|
||||
vector<market_trade> trades = get_trade_history( base, quote, now, yesterday, batch_size );
|
||||
if( !trades.empty() )
|
||||
{
|
||||
result.latest = trades[0].price;
|
||||
result.percent_change = ( result.latest / ( price_to_real( itr->open_quote, assets[1]->precision ) / price_to_real( itr->open_base, assets[0]->precision ) ) - 1 ) * 100;
|
||||
//result.lowest_ask = price_to_real( itr->low_quote, assets[1]->precision ) / price_to_real( itr->low_base, assets[0]->precision );
|
||||
//result.highest_bid = price_to_real( itr->high_quote, assets[1]->precision ) / price_to_real( itr->high_base, assets[0]->precision );
|
||||
result.lowest_ask = orders.asks[0].first;
|
||||
result.highest_bid = orders.bids[0].first;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.latest = trades[0].price;
|
||||
result.percent_change = ( result.latest / ( price_to_real( itr->open_base, assets[1]->precision ) / price_to_real( itr->open_quote, assets[0]->precision ) ) - 1) * 100;
|
||||
//result.lowest_ask = price_to_real( itr->low_base, assets[1]->precision ) / price_to_real( itr->low_quote, assets[0]->precision );
|
||||
//result.highest_bid = price_to_real( itr->high_base, assets[1]->precision ) / price_to_real( itr->high_quote, assets[0]->precision );
|
||||
result.lowest_ask = orders.bids[0].first;
|
||||
result.highest_bid = orders.asks[0].first;
|
||||
}
|
||||
|
||||
for ( market_trade t: trades )
|
||||
{
|
||||
result.base_volume += t.amount;
|
||||
result.quote_volume += t.value;
|
||||
}
|
||||
|
||||
while (trades.size() == 100)
|
||||
{
|
||||
for ( market_trade t: trades )
|
||||
while( !trades.empty() )
|
||||
{
|
||||
result.base_volume += t.amount;
|
||||
result.quote_volume += t.value;
|
||||
for( const market_trade& t: trades )
|
||||
{
|
||||
result.base_volume += t.value;
|
||||
result.quote_volume += t.amount;
|
||||
}
|
||||
|
||||
trades = get_trade_history( base, quote, trades.back().date, yesterday, batch_size );
|
||||
}
|
||||
|
||||
trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 );
|
||||
}
|
||||
const auto last_trade_yesterday = get_trade_history( base, quote, yesterday, fc::time_point_sec(), 1 );
|
||||
if( !last_trade_yesterday.empty() )
|
||||
{
|
||||
const auto price_yesterday = last_trade_yesterday[0].price;
|
||||
result.percent_change = ( (result.latest / price_yesterday) - 1 ) * 100;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto last_trade = get_trade_history( base, quote, now, fc::time_point_sec(), 1 );
|
||||
if( !last_trade.empty() )
|
||||
result.latest = last_trade[0].price;
|
||||
}
|
||||
|
||||
}
|
||||
const auto orders = get_order_book( base, quote, 1 );
|
||||
if( !orders.asks.empty() ) result.lowest_ask = orders.asks[0].price;
|
||||
if( !orders.bids.empty() ) result.highest_bid = orders.bids[0].price;
|
||||
} FC_CAPTURE_AND_RETHROW( (base)(quote) )
|
||||
|
||||
return result;
|
||||
} FC_CAPTURE_AND_RETHROW( (base)(quote) )
|
||||
return result;
|
||||
}
|
||||
|
||||
market_volume database_api::get_24_volume( const string& base, const string& quote )const
|
||||
{
|
||||
return my->get_24_volume( base, quote );
|
||||
return my->get_24_volume( base, quote );
|
||||
}
|
||||
|
||||
market_volume database_api_impl::get_24_volume( const string& base, const string& quote )const
|
||||
{
|
||||
auto assets = lookup_asset_symbols( {base, quote} );
|
||||
FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
|
||||
FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
|
||||
const auto ticker = get_ticker( base, quote );
|
||||
|
||||
auto base_id = assets[0]->id;
|
||||
auto quote_id = assets[1]->id;
|
||||
market_volume result;
|
||||
result.base = ticker.base;
|
||||
result.quote = ticker.quote;
|
||||
result.base_volume = ticker.base_volume;
|
||||
result.quote_volume = ticker.quote_volume;
|
||||
|
||||
market_volume result;
|
||||
result.base = base;
|
||||
result.quote = quote;
|
||||
result.base_volume = 0;
|
||||
result.quote_volume = 0;
|
||||
|
||||
try {
|
||||
if( base_id > quote_id ) std::swap(base_id, quote_id);
|
||||
|
||||
uint32_t bucket_size = 86400;
|
||||
auto now = fc::time_point_sec( fc::time_point::now() );
|
||||
|
||||
auto trades = get_trade_history( base, quote, now, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 );
|
||||
|
||||
for ( market_trade t: trades )
|
||||
{
|
||||
result.base_volume += t.amount;
|
||||
result.quote_volume += t.value;
|
||||
}
|
||||
|
||||
while (trades.size() == 100)
|
||||
{
|
||||
for ( market_trade t: trades )
|
||||
{
|
||||
result.base_volume += t.amount;
|
||||
result.quote_volume += t.value;
|
||||
}
|
||||
|
||||
trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 );
|
||||
}
|
||||
|
||||
return result;
|
||||
} FC_CAPTURE_AND_RETHROW( (base)(quote) )
|
||||
return result;
|
||||
}
|
||||
|
||||
order_book database_api::get_order_book( const string& base, const string& quote, unsigned limit )const
|
||||
|
|
@ -1137,6 +1299,7 @@ order_book database_api::get_order_book( const string& base, const string& quote
|
|||
|
||||
order_book database_api_impl::get_order_book( const string& base, const string& quote, unsigned limit )const
|
||||
{
|
||||
using boost::multiprecision::uint128_t;
|
||||
FC_ASSERT( limit <= 50 );
|
||||
|
||||
order_book result;
|
||||
|
|
@ -1156,21 +1319,28 @@ order_book database_api_impl::get_order_book( const string& base, const string&
|
|||
auto price_to_real = [&]( const price& p )
|
||||
{
|
||||
if( p.base.asset_id == base_id )
|
||||
return asset_to_real( p.quote, assets[1]->precision ) / asset_to_real( p.base, assets[0]->precision );
|
||||
return asset_to_real( p.base, assets[0]->precision ) / asset_to_real( p.quote, assets[1]->precision );
|
||||
else
|
||||
return asset_to_real( p.base, assets[1]->precision ) / asset_to_real( p.quote, assets[0]->precision );
|
||||
return asset_to_real( p.quote, assets[0]->precision ) / asset_to_real( p.base, assets[1]->precision );
|
||||
};
|
||||
|
||||
for( const auto& o : orders ) {
|
||||
for( const auto& o : orders )
|
||||
{
|
||||
if( o.sell_price.base.asset_id == base_id )
|
||||
{
|
||||
result.asks.push_back( std::make_pair( price_to_real(o.sell_price),
|
||||
asset_to_real(o.sell_price.base, assets[0]->precision)) );
|
||||
order ord;
|
||||
ord.price = price_to_real( o.sell_price );
|
||||
ord.quote = asset_to_real( share_type( ( uint128_t( o.for_sale.value ) * o.sell_price.quote.amount.value ) / o.sell_price.base.amount.value ), assets[1]->precision );
|
||||
ord.base = asset_to_real( o.for_sale, assets[0]->precision );
|
||||
result.bids.push_back( ord );
|
||||
}
|
||||
else
|
||||
{
|
||||
result.bids.push_back( std::make_pair( price_to_real(o.sell_price),
|
||||
asset_to_real(o.sell_price.quote, assets[0]->precision ) ) );
|
||||
order ord;
|
||||
ord.price = price_to_real( o.sell_price );
|
||||
ord.quote = asset_to_real( o.for_sale, assets[1]->precision );
|
||||
ord.base = asset_to_real( share_type( ( uint128_t( o.for_sale.value ) * o.sell_price.quote.amount.value ) / o.sell_price.base.amount.value ), assets[0]->precision );
|
||||
result.asks.push_back( ord );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1225,13 +1395,13 @@ vector<market_trade> database_api_impl::get_trade_history( const string& base,
|
|||
|
||||
if( assets[0]->id == itr->op.receives.asset_id )
|
||||
{
|
||||
trade.amount = price_to_real( itr->op.receives.amount, assets[0]->precision );
|
||||
trade.value = price_to_real( itr->op.pays.amount, assets[1]->precision );
|
||||
trade.amount = price_to_real( itr->op.pays.amount, assets[1]->precision );
|
||||
trade.value = price_to_real( itr->op.receives.amount, assets[0]->precision );
|
||||
}
|
||||
else
|
||||
{
|
||||
trade.amount = price_to_real( itr->op.pays.amount, assets[0]->precision );
|
||||
trade.value = price_to_real( itr->op.receives.amount, assets[1]->precision );
|
||||
trade.amount = price_to_real( itr->op.receives.amount, assets[1]->precision );
|
||||
trade.value = price_to_real( itr->op.pays.amount, assets[0]->precision );
|
||||
}
|
||||
|
||||
trade.date = itr->time;
|
||||
|
|
@ -1758,6 +1928,99 @@ vector<blinded_balance_object> database_api_impl::get_blinded_balances( const fl
|
|||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Tournament methods //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
vector<tournament_object> database_api::get_tournaments_in_state(tournament_state state, uint32_t limit) const
|
||||
{
|
||||
return my->get_tournaments_in_state(state, limit);
|
||||
}
|
||||
|
||||
vector<tournament_object> database_api_impl::get_tournaments_in_state(tournament_state state, uint32_t limit) const
|
||||
{
|
||||
vector<tournament_object> result;
|
||||
const auto& registration_deadline_index = _db.get_index_type<tournament_index>().indices().get<by_registration_deadline>();
|
||||
const auto range = registration_deadline_index.equal_range(boost::make_tuple(state));
|
||||
for (const tournament_object& tournament_obj : boost::make_iterator_range(range.first, range.second))
|
||||
{
|
||||
result.emplace_back(tournament_obj);
|
||||
subscribe_to_item( tournament_obj.id );
|
||||
|
||||
if (result.size() >= limit)
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<tournament_object> database_api::get_tournaments(tournament_id_type stop,
|
||||
unsigned limit,
|
||||
tournament_id_type start)
|
||||
{
|
||||
return my->get_tournaments(stop, limit, start);
|
||||
}
|
||||
|
||||
vector<tournament_object> database_api_impl::get_tournaments(tournament_id_type stop,
|
||||
unsigned limit,
|
||||
tournament_id_type start)
|
||||
{
|
||||
vector<tournament_object> result;
|
||||
const auto& tournament_idx = _db.get_index_type<tournament_index>().indices().get<by_id>();
|
||||
for (auto elem: tournament_idx) {
|
||||
if( result.size() >= limit ) break;
|
||||
if( ( (elem.get_id().instance.value <= start.instance.value) || start == tournament_id_type()) &&
|
||||
( (elem.get_id().instance.value >= stop.instance.value) || stop == tournament_id_type()))
|
||||
result.push_back( elem );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
vector<tournament_object> database_api::get_tournaments_by_state(tournament_id_type stop,
|
||||
unsigned limit,
|
||||
tournament_id_type start,
|
||||
tournament_state state)
|
||||
{
|
||||
return my->get_tournaments_by_state(stop, limit, start, state);
|
||||
}
|
||||
|
||||
vector<tournament_object> database_api_impl::get_tournaments_by_state(tournament_id_type stop,
|
||||
unsigned limit,
|
||||
tournament_id_type start,
|
||||
tournament_state state)
|
||||
{
|
||||
vector<tournament_object> result;
|
||||
const auto& tournament_idx = _db.get_index_type<tournament_index>().indices().get<by_id>();
|
||||
for (auto elem: tournament_idx) {
|
||||
if( result.size() >= limit ) break;
|
||||
if( ( (elem.get_id().instance.value <= start.instance.value) || start == tournament_id_type()) &&
|
||||
( (elem.get_id().instance.value >= stop.instance.value) || stop == tournament_id_type()) &&
|
||||
elem.get_state() == state )
|
||||
result.push_back( elem );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<tournament_id_type> database_api::get_registered_tournaments(account_id_type account_filter, uint32_t limit) const
|
||||
{
|
||||
return my->get_registered_tournaments(account_filter, limit);
|
||||
}
|
||||
|
||||
vector<tournament_id_type> database_api_impl::get_registered_tournaments(account_id_type account_filter, uint32_t limit) const
|
||||
{
|
||||
const auto& tournament_details_idx = _db.get_index_type<tournament_details_index>();
|
||||
const auto& tournament_details_primary_idx = dynamic_cast<const primary_index<tournament_details_index>&>(tournament_details_idx);
|
||||
const auto& players_idx = tournament_details_primary_idx.get_secondary_index<graphene::chain::tournament_players_index>();
|
||||
|
||||
vector<tournament_id_type> tournament_ids = players_idx.get_registered_tournaments_for_account(account_filter);
|
||||
if (tournament_ids.size() >= limit)
|
||||
tournament_ids.resize(limit);
|
||||
return tournament_ids;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Private methods //
|
||||
|
|
@ -1766,108 +2029,113 @@ vector<blinded_balance_object> database_api_impl::get_blinded_balances( const fl
|
|||
|
||||
void database_api_impl::broadcast_updates( const vector<variant>& updates )
|
||||
{
|
||||
if( updates.size() ) {
|
||||
if( updates.size() && _subscribe_callback ) {
|
||||
auto capture_this = shared_from_this();
|
||||
fc::async([capture_this,updates](){
|
||||
capture_this->_subscribe_callback( fc::variant(updates) );
|
||||
if(capture_this->_subscribe_callback)
|
||||
capture_this->_subscribe_callback( fc::variant(updates) );
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database_api_impl::on_objects_removed( const vector<const object*>& objs )
|
||||
void database_api_impl::broadcast_market_updates( const market_queue_type& queue)
|
||||
{
|
||||
if( queue.size() )
|
||||
{
|
||||
auto capture_this = shared_from_this();
|
||||
fc::async([capture_this, this, queue](){
|
||||
for( const auto& item : queue )
|
||||
{
|
||||
auto sub = _market_subscriptions.find(item.first);
|
||||
if( sub != _market_subscriptions.end() )
|
||||
sub->second( fc::variant(item.second ) );
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database_api_impl::on_objects_removed( const vector<object_id_type>& ids, const vector<const object*>& objs, const flat_set<account_id_type>& impacted_accounts)
|
||||
{
|
||||
handle_object_changed(_notify_remove_create, false, ids, impacted_accounts,
|
||||
[objs](object_id_type id) -> const object* {
|
||||
auto it = std::find_if(
|
||||
objs.begin(), objs.end(),
|
||||
[id](const object* o) {return o != nullptr && o->id == id;});
|
||||
|
||||
if (it != objs.end())
|
||||
return *it;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void database_api_impl::on_objects_new(const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts)
|
||||
{
|
||||
handle_object_changed(_notify_remove_create, true, ids, impacted_accounts,
|
||||
std::bind(&object_database::find_object, &_db, std::placeholders::_1)
|
||||
);
|
||||
}
|
||||
|
||||
void database_api_impl::on_objects_changed(const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts)
|
||||
{
|
||||
handle_object_changed(false, true, ids, impacted_accounts,
|
||||
std::bind(&object_database::find_object, &_db, std::placeholders::_1)
|
||||
);
|
||||
}
|
||||
|
||||
void database_api_impl::handle_object_changed(bool force_notify, bool full_object, const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts, std::function<const object*(object_id_type id)> find_object)
|
||||
{
|
||||
/// we need to ensure the database_api is not deleted for the life of the async operation
|
||||
if( _subscribe_callback )
|
||||
{
|
||||
vector<variant> updates;
|
||||
updates.reserve(objs.size());
|
||||
vector<variant> updates;
|
||||
|
||||
for( auto obj : objs )
|
||||
updates.emplace_back( obj->id );
|
||||
broadcast_updates( updates );
|
||||
for(auto id : ids)
|
||||
{
|
||||
if( force_notify || is_subscribed_to_item(id) || is_impacted_account(impacted_accounts) )
|
||||
{
|
||||
if( full_object )
|
||||
{
|
||||
auto obj = find_object(id);
|
||||
if( obj )
|
||||
{
|
||||
updates.emplace_back( obj->to_variant() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
updates.emplace_back( id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
broadcast_updates(updates);
|
||||
}
|
||||
|
||||
if( _market_subscriptions.size() )
|
||||
{
|
||||
map< pair<asset_id_type, asset_id_type>, vector<variant> > broadcast_queue;
|
||||
for( const auto& obj : objs )
|
||||
{
|
||||
const limit_order_object* order = dynamic_cast<const limit_order_object*>(obj);
|
||||
if( order )
|
||||
{
|
||||
auto sub = _market_subscriptions.find( order->get_market() );
|
||||
if( sub != _market_subscriptions.end() )
|
||||
broadcast_queue[order->get_market()].emplace_back( order->id );
|
||||
}
|
||||
}
|
||||
if( broadcast_queue.size() )
|
||||
{
|
||||
auto capture_this = shared_from_this();
|
||||
fc::async([capture_this,this,broadcast_queue](){
|
||||
for( const auto& item : broadcast_queue )
|
||||
{
|
||||
auto sub = _market_subscriptions.find(item.first);
|
||||
if( sub != _market_subscriptions.end() )
|
||||
sub->second( fc::variant(item.second ) );
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void database_api_impl::on_objects_changed(const vector<object_id_type>& ids)
|
||||
{
|
||||
vector<variant> updates;
|
||||
map< pair<asset_id_type, asset_id_type>, vector<variant> > market_broadcast_queue;
|
||||
|
||||
for(auto id : ids)
|
||||
{
|
||||
const object* obj = nullptr;
|
||||
if( _subscribe_callback )
|
||||
{
|
||||
obj = _db.find_object( id );
|
||||
if( obj )
|
||||
{
|
||||
updates.emplace_back( obj->to_variant() );
|
||||
}
|
||||
else
|
||||
{
|
||||
updates.emplace_back(id); // send just the id to indicate removal
|
||||
}
|
||||
}
|
||||
|
||||
if( _market_subscriptions.size() )
|
||||
{
|
||||
if( !_subscribe_callback )
|
||||
obj = _db.find_object( id );
|
||||
if( obj )
|
||||
{
|
||||
const limit_order_object* order = dynamic_cast<const limit_order_object*>(obj);
|
||||
if( order )
|
||||
{
|
||||
auto sub = _market_subscriptions.find( order->get_market() );
|
||||
if( sub != _market_subscriptions.end() )
|
||||
market_broadcast_queue[order->get_market()].emplace_back( order->id );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto capture_this = shared_from_this();
|
||||
|
||||
market_queue_type broadcast_queue;
|
||||
/// pushing the future back / popping the prior future if it is complete.
|
||||
/// if a connection hangs then this could get backed up and result in
|
||||
/// a failure to exit cleanly.
|
||||
fc::async([capture_this,this,updates,market_broadcast_queue](){
|
||||
if( _subscribe_callback ) _subscribe_callback( updates );
|
||||
//fc::async([capture_this,this,updates,market_broadcast_queue](){
|
||||
//if( _subscribe_callback )
|
||||
// _subscribe_callback( updates );
|
||||
|
||||
for( const auto& item : market_broadcast_queue )
|
||||
for(auto id : ids)
|
||||
{
|
||||
auto sub = _market_subscriptions.find(item.first);
|
||||
if( sub != _market_subscriptions.end() )
|
||||
sub->second( fc::variant(item.second ) );
|
||||
if( id.is<call_order_object>() )
|
||||
{
|
||||
enqueue_if_subscribed_to_market<call_order_object>( find_object(id), broadcast_queue, full_object );
|
||||
}
|
||||
else if( id.is<limit_order_object>() )
|
||||
{
|
||||
enqueue_if_subscribed_to_market<limit_order_object>( find_object(id), broadcast_queue, full_object );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
broadcast_market_updates(broadcast_queue);
|
||||
}
|
||||
}
|
||||
|
||||
/** note: this method cannot yield because it is called in the middle of
|
||||
|
|
|
|||
|
|
@ -90,6 +90,12 @@ struct get_impacted_account_visitor
|
|||
}
|
||||
|
||||
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 )
|
||||
|
|
@ -203,6 +209,72 @@ struct get_impacted_account_visitor
|
|||
_impacted.insert( op.account_id );
|
||||
}
|
||||
|
||||
void operator()( const sport_create_operation& op ) {}
|
||||
void operator()( const sport_update_operation& op ) {}
|
||||
void operator()( const event_group_create_operation& op ) {}
|
||||
void operator()( const event_group_update_operation& op ) {}
|
||||
void operator()( const event_create_operation& op ) {}
|
||||
void operator()( const event_update_operation& op ) {}
|
||||
void operator()( const event_update_status_operation& op ) {}
|
||||
void operator()( const betting_market_rules_create_operation& op ) {}
|
||||
void operator()( const betting_market_rules_update_operation& op ) {}
|
||||
void operator()( const betting_market_group_create_operation& op ) {}
|
||||
void operator()( const betting_market_group_update_operation& op ) {}
|
||||
void operator()( const betting_market_create_operation& op ) {}
|
||||
void operator()( const betting_market_update_operation& op ) {}
|
||||
void operator()( const betting_market_group_resolve_operation& op ) {}
|
||||
void operator()( const betting_market_group_cancel_unmatched_bets_operation& op ) {}
|
||||
|
||||
void operator()( const bet_place_operation& op )
|
||||
{
|
||||
_impacted.insert( op.bettor_id );
|
||||
}
|
||||
void operator()( const bet_cancel_operation& op )
|
||||
{
|
||||
_impacted.insert( op.bettor_id );
|
||||
}
|
||||
void operator()( const bet_canceled_operation& op )
|
||||
{
|
||||
_impacted.insert( op.bettor_id );
|
||||
}
|
||||
void operator()( const bet_adjusted_operation& op )
|
||||
{
|
||||
_impacted.insert( op.bettor_id );
|
||||
}
|
||||
void operator()( const bet_matched_operation& op )
|
||||
{
|
||||
_impacted.insert( op.bettor_id );
|
||||
}
|
||||
void operator()( const betting_market_group_resolved_operation& op )
|
||||
{
|
||||
_impacted.insert( op.bettor_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 )
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@
|
|||
#include <graphene/chain/protocol/confidential.hpp>
|
||||
|
||||
#include <graphene/market_history/market_history_plugin.hpp>
|
||||
#include <graphene/accounts_list/accounts_list_plugin.hpp>
|
||||
|
||||
#include <graphene/debug_witness/debug_api.hpp>
|
||||
#include <graphene/bookie/bookie_api.hpp>
|
||||
|
||||
#include <graphene/net/node.hpp>
|
||||
|
||||
|
|
@ -47,6 +51,7 @@
|
|||
namespace graphene { namespace app {
|
||||
using namespace graphene::chain;
|
||||
using namespace graphene::market_history;
|
||||
using namespace graphene::accounts_list;
|
||||
using namespace fc::ecc;
|
||||
using namespace std;
|
||||
|
||||
|
|
@ -69,6 +74,18 @@ namespace graphene { namespace app {
|
|||
string message_out;
|
||||
};
|
||||
|
||||
struct account_asset_balance
|
||||
{
|
||||
string name;
|
||||
account_id_type account_id;
|
||||
share_type amount;
|
||||
};
|
||||
struct asset_holders
|
||||
{
|
||||
asset_id_type asset_id;
|
||||
int count;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The history_api class implements the RPC API for account history
|
||||
*
|
||||
|
|
@ -92,14 +109,63 @@ namespace graphene { namespace app {
|
|||
unsigned limit = 100,
|
||||
operation_history_id_type start = operation_history_id_type())const;
|
||||
|
||||
/**
|
||||
* @brief Get only asked operations relevant to the specified account
|
||||
* @param account The account whose history should be queried
|
||||
* @param operation_id The ID of the operation we want to get operations in the account( 0 = transfer , 1 = limit order create, ...)
|
||||
* @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_operations(account_id_type account,
|
||||
int operation_id,
|
||||
operation_history_id_type start = operation_history_id_type(),
|
||||
operation_history_id_type stop = operation_history_id_type(),
|
||||
unsigned limit = 100)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;
|
||||
vector<account_balance_object> list_core_accounts()const;
|
||||
flat_set<uint32_t> get_market_history_buckets()const;
|
||||
private:
|
||||
application& _app;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Block api
|
||||
*/
|
||||
class block_api
|
||||
{
|
||||
public:
|
||||
block_api(graphene::chain::database& db);
|
||||
~block_api();
|
||||
|
||||
vector<optional<signed_block>> get_blocks(uint32_t block_num_from, uint32_t block_num_to)const;
|
||||
|
||||
private:
|
||||
graphene::chain::database& _db;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief The network_broadcast_api class allows broadcasting of transactions.
|
||||
*/
|
||||
|
|
@ -133,6 +199,12 @@ namespace graphene { namespace app {
|
|||
*/
|
||||
void broadcast_transaction_with_callback( confirmation_callback cb, 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.
|
||||
*/
|
||||
fc::variant broadcast_transaction_synchronous(const signed_transaction& trx);
|
||||
|
||||
void broadcast_block( const signed_block& block );
|
||||
|
||||
/**
|
||||
|
|
@ -234,6 +306,23 @@ namespace graphene { namespace app {
|
|||
range_proof_info range_get_info( const std::vector<char>& proof );
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*/
|
||||
class asset_api
|
||||
{
|
||||
public:
|
||||
asset_api(graphene::chain::database& db);
|
||||
~asset_api();
|
||||
|
||||
vector<account_asset_balance> get_asset_holders( asset_id_type asset_id, uint32_t start, uint32_t limit )const;
|
||||
int get_asset_holders_count( asset_id_type asset_id )const;
|
||||
vector<asset_holders> get_all_asset_holders() const;
|
||||
|
||||
private:
|
||||
graphene::chain::database& _db;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The login_api class implements the bottom layer of the RPC API
|
||||
*
|
||||
|
|
@ -255,6 +344,8 @@ namespace graphene { namespace app {
|
|||
* has sucessfully authenticated.
|
||||
*/
|
||||
bool login(const string& user, const string& password);
|
||||
/// @brief Retrieve the network block API
|
||||
fc::api<block_api> block()const;
|
||||
/// @brief Retrieve the network broadcast API
|
||||
fc::api<network_broadcast_api> network_broadcast()const;
|
||||
/// @brief Retrieve the database API
|
||||
|
|
@ -265,17 +356,27 @@ namespace graphene { namespace app {
|
|||
fc::api<network_node_api> network_node()const;
|
||||
/// @brief Retrieve the cryptography API
|
||||
fc::api<crypto_api> crypto()const;
|
||||
/// @brief Retrieve the asset API
|
||||
fc::api<asset_api> asset()const;
|
||||
/// @brief Retrieve the debug API (if available)
|
||||
fc::api<graphene::debug_witness::debug_api> debug()const;
|
||||
/// @brief Retrieve the bookie API (if available)
|
||||
fc::api<graphene::bookie::bookie_api> bookie()const;
|
||||
|
||||
private:
|
||||
/// @brief Called to enable an API, not reflected.
|
||||
void enable_api( const string& api_name );
|
||||
private:
|
||||
|
||||
application& _app;
|
||||
optional< fc::api<block_api> > _block_api;
|
||||
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<asset_api> > _asset_api;
|
||||
optional< fc::api<graphene::debug_witness::debug_api> > _debug_api;
|
||||
optional< fc::api<graphene::bookie::bookie_api> > _bookie_api;
|
||||
};
|
||||
|
||||
}} // graphene::app
|
||||
|
|
@ -289,15 +390,25 @@ FC_REFLECT( graphene::app::verify_range_proof_rewind_result,
|
|||
//FC_REFLECT_TYPENAME( fc::ecc::compact_signature );
|
||||
//FC_REFLECT_TYPENAME( fc::ecc::commitment_type );
|
||||
|
||||
FC_REFLECT( graphene::app::account_asset_balance, (name)(account_id)(amount) );
|
||||
FC_REFLECT( graphene::app::asset_holders, (asset_id)(count) );
|
||||
|
||||
FC_API(graphene::app::history_api,
|
||||
(get_account_history)
|
||||
(get_account_history_operations)
|
||||
(get_relative_account_history)
|
||||
(get_fill_order_history)
|
||||
(get_market_history)
|
||||
(get_market_history_buckets)
|
||||
(list_core_accounts)
|
||||
)
|
||||
FC_API(graphene::app::block_api,
|
||||
(get_blocks)
|
||||
)
|
||||
FC_API(graphene::app::network_broadcast_api,
|
||||
(broadcast_transaction)
|
||||
(broadcast_transaction_with_callback)
|
||||
(broadcast_transaction_synchronous)
|
||||
(broadcast_block)
|
||||
)
|
||||
FC_API(graphene::app::network_node_api,
|
||||
|
|
@ -319,11 +430,20 @@ FC_API(graphene::app::crypto_api,
|
|||
(verify_range_proof_rewind)
|
||||
(range_get_info)
|
||||
)
|
||||
FC_API(graphene::app::asset_api,
|
||||
(get_asset_holders)
|
||||
(get_asset_holders_count)
|
||||
(get_all_asset_holders)
|
||||
)
|
||||
FC_API(graphene::app::login_api,
|
||||
(login)
|
||||
(block)
|
||||
(network_broadcast)
|
||||
(database)
|
||||
(history)
|
||||
(network_node)
|
||||
(crypto)
|
||||
(asset)
|
||||
(debug)
|
||||
(bookie)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -38,8 +38,15 @@
|
|||
#include <graphene/chain/market_object.hpp>
|
||||
#include <graphene/chain/operation_history_object.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/sport_object.hpp>
|
||||
#include <graphene/chain/event_group_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/global_betting_statistics_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>
|
||||
|
||||
|
|
@ -64,40 +71,47 @@ using namespace std;
|
|||
|
||||
class database_api_impl;
|
||||
|
||||
struct order
|
||||
{
|
||||
double price;
|
||||
double quote;
|
||||
double base;
|
||||
};
|
||||
|
||||
struct order_book
|
||||
{
|
||||
string base;
|
||||
string quote;
|
||||
vector< pair<double,double> > bids;
|
||||
vector< pair<double,double> > asks;
|
||||
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;
|
||||
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;
|
||||
string base;
|
||||
string quote;
|
||||
double base_volume;
|
||||
double quote_volume;
|
||||
};
|
||||
|
||||
struct market_trade
|
||||
{
|
||||
fc::time_point_sec date;
|
||||
double price;
|
||||
double amount;
|
||||
double value;
|
||||
fc::time_point_sec date;
|
||||
double price;
|
||||
double amount;
|
||||
double value;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -151,6 +165,14 @@ class database_api
|
|||
*/
|
||||
optional<block_header> get_block_header(uint32_t block_num)const;
|
||||
|
||||
/**
|
||||
* @brief Retrieve multiple block header by block numbers
|
||||
* @param block_num vector containing heights of the block whose header should be returned
|
||||
* @return array of headers of the referenced blocks, or null if no matching block was found
|
||||
*/
|
||||
map<uint32_t, optional<block_header>> get_block_header_batch(const vector<uint32_t> block_nums)const;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve a full, signed block
|
||||
* @param block_num Height of the block to be returned
|
||||
|
|
@ -205,6 +227,15 @@ class database_api
|
|||
|
||||
vector<vector<account_id_type>> get_key_references( vector<public_key_type> key )const;
|
||||
|
||||
/**
|
||||
* Determine whether a textual representation of a public key
|
||||
* (in Base-58 format) is *currently* linked
|
||||
* to any *registered* (i.e. non-stealth) account on the blockchain
|
||||
* @param public_key Public key
|
||||
* @return Whether a public key is known
|
||||
*/
|
||||
bool is_public_key_registered(string public_key) const;
|
||||
|
||||
//////////////
|
||||
// Accounts //
|
||||
//////////////
|
||||
|
|
@ -312,6 +343,50 @@ class database_api
|
|||
*/
|
||||
vector<optional<asset_object>> lookup_asset_symbols(const vector<string>& symbols_or_ids)const;
|
||||
|
||||
/////////////////////
|
||||
// Peerplays //
|
||||
/////////////////////
|
||||
|
||||
/**
|
||||
* @brief Get global betting statistics
|
||||
*/
|
||||
global_betting_statistics_object get_global_betting_statistics() const;
|
||||
|
||||
/**
|
||||
* @brief Get a list of all sports
|
||||
*/
|
||||
vector<sport_object> list_sports() const;
|
||||
|
||||
/**
|
||||
* @brief Return a list of all event groups for a sport (e.g. all soccer leagues in soccer)
|
||||
*/
|
||||
vector<event_group_object> list_event_groups(sport_id_type sport_id) const;
|
||||
|
||||
/**
|
||||
* @brief Return a list of all events in an event group
|
||||
*/
|
||||
vector<event_object> list_events_in_group(event_group_id_type event_group_id) const;
|
||||
|
||||
/**
|
||||
* @brief Return a list of all betting market groups for an event
|
||||
*/
|
||||
vector<betting_market_group_object> list_betting_market_groups(event_id_type) const;
|
||||
|
||||
/**
|
||||
* @brief Return a list of all betting markets for a betting market group
|
||||
*/
|
||||
vector<betting_market_object> list_betting_markets(betting_market_group_id_type) const;
|
||||
|
||||
/**
|
||||
* @brief Return a list of all unmatched bets for a given account on a specific betting market
|
||||
*/
|
||||
vector<bet_object> get_unmatched_bets_for_bettor(betting_market_id_type, account_id_type) const;
|
||||
|
||||
/**
|
||||
* @brief Return a list of all unmatched bets for a given account (includes bets on all markets)
|
||||
*/
|
||||
vector<bet_object> get_all_unmatched_bets_for_bettor(account_id_type) const;
|
||||
|
||||
/////////////////////
|
||||
// Markets / feeds //
|
||||
/////////////////////
|
||||
|
|
@ -548,11 +623,35 @@ class database_api
|
|||
*/
|
||||
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) );
|
||||
|
|
@ -570,6 +669,7 @@ FC_API(graphene::app::database_api,
|
|||
|
||||
// Blocks and transactions
|
||||
(get_block_header)
|
||||
(get_block_header_batch)
|
||||
(get_block)
|
||||
(get_transaction)
|
||||
(get_recent_transaction_by_id)
|
||||
|
|
@ -583,6 +683,7 @@ FC_API(graphene::app::database_api,
|
|||
|
||||
// Keys
|
||||
(get_key_references)
|
||||
(is_public_key_registered)
|
||||
|
||||
// Accounts
|
||||
(get_accounts)
|
||||
|
|
@ -605,6 +706,16 @@ FC_API(graphene::app::database_api,
|
|||
(list_assets)
|
||||
(lookup_asset_symbols)
|
||||
|
||||
// Peerplays
|
||||
(list_sports)
|
||||
(get_global_betting_statistics)
|
||||
(list_event_groups)
|
||||
(list_events_in_group)
|
||||
(list_betting_market_groups)
|
||||
(list_betting_markets)
|
||||
(get_unmatched_bets_for_bettor)
|
||||
(get_all_unmatched_bets_for_bettor)
|
||||
|
||||
// Markets / feeds
|
||||
(get_order_book)
|
||||
(get_limit_orders)
|
||||
|
|
@ -648,4 +759,10 @@ FC_API(graphene::app::database_api,
|
|||
|
||||
// Blinded balances
|
||||
(get_blinded_balances)
|
||||
|
||||
// Tournaments
|
||||
(get_tournaments_in_state)
|
||||
(get_tournaments_by_state)
|
||||
(get_tournaments )
|
||||
(get_registered_tournaments)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/vesting_balance_object.hpp>
|
||||
#include <graphene/chain/market_evaluator.hpp>
|
||||
#include <graphene/chain/withdraw_permission_object.hpp>
|
||||
|
||||
namespace graphene { namespace app {
|
||||
using namespace graphene::chain;
|
||||
|
|
@ -43,7 +44,12 @@ namespace graphene { namespace app {
|
|||
vector<vesting_balance_object> vesting_balances;
|
||||
vector<limit_order_object> limit_orders;
|
||||
vector<call_order_object> call_orders;
|
||||
vector<force_settlement_object> settle_orders;
|
||||
vector<proposal_object> proposals;
|
||||
vector<asset_id_type> assets;
|
||||
vector<withdraw_permission_object> withdraws;
|
||||
// vector<pending_dividend_payout_balance_object> pending_dividend_payments;
|
||||
vector<pending_dividend_payout_balance_for_holder_object> pending_dividend_payments;
|
||||
};
|
||||
|
||||
} }
|
||||
|
|
@ -60,5 +66,10 @@ FC_REFLECT( graphene::app::full_account,
|
|||
(vesting_balances)
|
||||
(limit_orders)
|
||||
(call_orders)
|
||||
(settle_orders)
|
||||
(proposals)
|
||||
(assets)
|
||||
(withdraws)
|
||||
(proposals)
|
||||
(pending_dividend_payments)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ set_source_files_properties( "${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain
|
|||
add_dependencies( build_hardfork_hpp cat-parts )
|
||||
|
||||
file(GLOB HEADERS "include/graphene/chain/*.hpp")
|
||||
file(GLOB PROTOCOL_HEADERS "include/graphene/chain/protocol/*.hpp")
|
||||
|
||||
if( GRAPHENE_DISABLE_UNITY_BUILD )
|
||||
set( GRAPHENE_DB_FILES
|
||||
db_balance.cpp
|
||||
db_bet.cpp
|
||||
db_block.cpp
|
||||
db_debug.cpp
|
||||
db_getter.cpp
|
||||
|
|
@ -56,6 +58,7 @@ add_library( graphene_chain
|
|||
protocol/fee_schedule.cpp
|
||||
protocol/confidential.cpp
|
||||
protocol/vote.cpp
|
||||
protocol/tournament.cpp
|
||||
|
||||
genesis_state.cpp
|
||||
get_config.cpp
|
||||
|
|
@ -73,6 +76,10 @@ add_library( graphene_chain
|
|||
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
|
||||
|
|
@ -89,7 +96,20 @@ add_library( graphene_chain
|
|||
|
||||
is_authorized_asset.cpp
|
||||
|
||||
protocol/sport.cpp
|
||||
sport_evaluator.cpp
|
||||
protocol/event_group.cpp
|
||||
event_group_evaluator.cpp
|
||||
protocol/event.cpp
|
||||
event_evaluator.cpp
|
||||
event_object.cpp
|
||||
protocol/betting_market.cpp
|
||||
betting_market_evaluator.cpp
|
||||
betting_market_object.cpp
|
||||
betting_market_group_object.cpp
|
||||
|
||||
${HEADERS}
|
||||
${PROTOCOL_HEADERS}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp"
|
||||
)
|
||||
|
||||
|
|
@ -109,3 +129,5 @@ INSTALL( TARGETS
|
|||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/chain" )
|
||||
INSTALL( FILES ${PROTOCOL_HEADERS} DESTINATION "include/graphene/chain/protocol" )
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#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>
|
||||
|
||||
|
|
@ -54,6 +55,43 @@ void verify_authority_accounts( const database& db, const authority& a )
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
@ -62,14 +100,18 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
|
|||
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." );
|
||||
|
||||
const auto& global_props = d.get_global_properties();
|
||||
const auto& chain_params = global_props.parameters;
|
||||
|
||||
try
|
||||
{
|
||||
verify_authority_accounts( d, op.owner );
|
||||
|
|
@ -84,27 +126,7 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
|
|||
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 );
|
||||
|
||||
uint32_t max_vote_id = global_props.next_available_vote_id;
|
||||
|
||||
FC_ASSERT( op.options.num_witness <= chain_params.maximum_witness_count,
|
||||
"Voted for more witnesses than currently allowed (${c})", ("c", chain_params.maximum_witness_count) );
|
||||
|
||||
FC_ASSERT( op.options.num_committee <= chain_params.maximum_committee_count,
|
||||
"Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) );
|
||||
|
||||
safe<uint32_t> counts[vote_id_type::VOTE_TYPE_COUNT];
|
||||
for( auto id : op.options.votes )
|
||||
{
|
||||
FC_ASSERT( id < max_vote_id );
|
||||
counts[id.type()]++;
|
||||
}
|
||||
FC_ASSERT(counts[vote_id_type::witness] <= op.options.num_witness,
|
||||
"",
|
||||
("count", counts[vote_id_type::witness])("num", op.options.num_witness));
|
||||
FC_ASSERT(counts[vote_id_type::committee] <= op.options.num_committee,
|
||||
"",
|
||||
("count", counts[vote_id_type::committee])("num", op.options.num_committee));
|
||||
verify_account_votes( d, op.options );
|
||||
|
||||
auto& acnt_indx = d.get_index_type<account_index>();
|
||||
if( op.name.size() )
|
||||
|
|
@ -223,8 +245,12 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio
|
|||
FC_ASSERT( !o.extensions.value.owner_special_authority.valid() );
|
||||
FC_ASSERT( !o.extensions.value.active_special_authority.valid() );
|
||||
}
|
||||
|
||||
const auto& chain_params = d.get_global_properties().parameters;
|
||||
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
|
||||
{
|
||||
|
|
@ -241,16 +267,8 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio
|
|||
|
||||
acnt = &o.account(d);
|
||||
|
||||
if( o.new_options )
|
||||
{
|
||||
FC_ASSERT( o.new_options->num_witness <= chain_params.maximum_witness_count );
|
||||
FC_ASSERT( o.new_options->num_committee <= chain_params.maximum_committee_count );
|
||||
uint32_t max_vote_id = d.get_global_properties().next_available_vote_id;
|
||||
for( auto id : o.new_options->votes )
|
||||
{
|
||||
FC_ASSERT( id < max_vote_id );
|
||||
}
|
||||
}
|
||||
if( o.new_options.valid() )
|
||||
verify_account_votes( d, *o.new_options );
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
|
@ -371,11 +389,13 @@ void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator:
|
|||
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();
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
#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 )
|
||||
|
|
@ -366,6 +368,70 @@ void_result asset_update_bitasset_evaluator::do_apply(const asset_update_bitasse
|
|||
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();
|
||||
|
|
|
|||
374
libraries/chain/betting_market_evaluator.cpp
Normal file
374
libraries/chain/betting_market_evaluator.cpp
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#define DEFAULT_LOGGER "betting"
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
#include <graphene/chain/betting_market_evaluator.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/is_authorized_asset.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result betting_market_rules_create_evaluator::do_evaluate(const betting_market_rules_create_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type betting_market_rules_create_evaluator::do_apply(const betting_market_rules_create_operation& op)
|
||||
{ try {
|
||||
const betting_market_rules_object& new_betting_market_rules =
|
||||
db().create<betting_market_rules_object>([&](betting_market_rules_object& betting_market_rules_obj) {
|
||||
betting_market_rules_obj.name = op.name;
|
||||
betting_market_rules_obj.description = op.description;
|
||||
});
|
||||
return new_betting_market_rules.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result betting_market_rules_update_evaluator::do_evaluate(const betting_market_rules_update_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
_rules = &op.betting_market_rules_id(db());
|
||||
FC_ASSERT(op.new_name.valid() || op.new_description.valid(), "nothing to update");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result betting_market_rules_update_evaluator::do_apply(const betting_market_rules_update_operation& op)
|
||||
{ try {
|
||||
db().modify(*_rules, [&](betting_market_rules_object& betting_market_rules) {
|
||||
if (op.new_name.valid())
|
||||
betting_market_rules.name = *op.new_name;
|
||||
if (op.new_description.valid())
|
||||
betting_market_rules.description = *op.new_description;
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result betting_market_group_create_evaluator::do_evaluate(const betting_market_group_create_operation& op)
|
||||
{ try {
|
||||
database& d = db();
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
|
||||
// the event_id in the operation can be a relative id. If it is,
|
||||
// resolve it and verify that it is truly an event
|
||||
object_id_type resolved_event_id = op.event_id;
|
||||
if (is_relative(op.event_id))
|
||||
resolved_event_id = get_relative_id(op.event_id);
|
||||
|
||||
FC_ASSERT(resolved_event_id.space() == event_id_type::space_id &&
|
||||
resolved_event_id.type() == event_id_type::type_id,
|
||||
"event_id must refer to a event_id_type");
|
||||
_event_id = resolved_event_id;
|
||||
FC_ASSERT(d.find_object(_event_id), "Invalid event specified");
|
||||
|
||||
FC_ASSERT(d.find_object(op.asset_id), "Invalid asset specified");
|
||||
|
||||
// the rules_id in the operation can be a relative id. If it is,
|
||||
// resolve it and verify that it is truly rules
|
||||
object_id_type resolved_rules_id = op.rules_id;
|
||||
if (is_relative(op.rules_id))
|
||||
resolved_rules_id = get_relative_id(op.rules_id);
|
||||
|
||||
FC_ASSERT(resolved_rules_id.space() == betting_market_rules_id_type::space_id &&
|
||||
resolved_rules_id.type() == betting_market_rules_id_type::type_id,
|
||||
"rules_id must refer to a betting_market_rules_id_type");
|
||||
_rules_id = resolved_rules_id;
|
||||
FC_ASSERT(d.find_object(_rules_id), "Invalid rules specified");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW((op)) }
|
||||
|
||||
object_id_type betting_market_group_create_evaluator::do_apply(const betting_market_group_create_operation& op)
|
||||
{ try {
|
||||
const betting_market_group_object& new_betting_market_group =
|
||||
db().create<betting_market_group_object>([&](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.event_id = _event_id;
|
||||
betting_market_group_obj.rules_id = _rules_id;
|
||||
betting_market_group_obj.description = op.description;
|
||||
betting_market_group_obj.asset_id = op.asset_id;
|
||||
betting_market_group_obj.never_in_play = op.never_in_play;
|
||||
betting_market_group_obj.delay_before_settling = op.delay_before_settling;
|
||||
});
|
||||
return new_betting_market_group.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result betting_market_group_update_evaluator::do_evaluate(const betting_market_group_update_operation& op)
|
||||
{ try {
|
||||
database& d = db();
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
_betting_market_group = &op.betting_market_group_id(d);
|
||||
|
||||
FC_ASSERT(op.new_description || op.new_rules_id || op.status, "nothing to change");
|
||||
|
||||
if (op.new_rules_id)
|
||||
{
|
||||
// the rules_id in the operation can be a relative id. If it is,
|
||||
// resolve it and verify that it is truly rules
|
||||
object_id_type resolved_rules_id = *op.new_rules_id;
|
||||
if (is_relative(*op.new_rules_id))
|
||||
resolved_rules_id = get_relative_id(*op.new_rules_id);
|
||||
|
||||
FC_ASSERT(resolved_rules_id.space() == betting_market_rules_id_type::space_id &&
|
||||
resolved_rules_id.type() == betting_market_rules_id_type::type_id,
|
||||
"rules_id must refer to a betting_market_rules_id_type");
|
||||
_rules_id = resolved_rules_id;
|
||||
FC_ASSERT(d.find_object(_rules_id), "invalid rules specified");
|
||||
}
|
||||
|
||||
if (op.status)
|
||||
FC_ASSERT(_betting_market_group->get_status() != *op.status, "status would not change the state of the betting market group");
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result betting_market_group_update_evaluator::do_apply(const betting_market_group_update_operation& op)
|
||||
{ try {
|
||||
database& d = db();
|
||||
d.modify(*_betting_market_group, [&](betting_market_group_object& betting_market_group) {
|
||||
if (op.new_description)
|
||||
betting_market_group.description = *op.new_description;
|
||||
if (op.new_rules_id)
|
||||
betting_market_group.rules_id = _rules_id;
|
||||
|
||||
bool bets_were_delayed = betting_market_group.bets_are_delayed();
|
||||
if (op.status)
|
||||
betting_market_group.dispatch_new_status(d, *op.status);
|
||||
|
||||
bool bets_are_delayed = betting_market_group.bets_are_delayed();
|
||||
|
||||
// if we have transitioned from in-play to not-in-play-but-still-accepting-bets,
|
||||
// place all delayed bets now
|
||||
if (betting_market_group.bets_are_allowed() &&
|
||||
bets_were_delayed && !bets_are_delayed)
|
||||
{
|
||||
const auto& bet_odds_idx = d.get_index_type<bet_object_index>().indices().get<by_odds>();
|
||||
auto bet_iter = bet_odds_idx.begin();
|
||||
bool last = bet_iter == bet_odds_idx.end() || !bet_iter->end_of_delay;
|
||||
while (!last)
|
||||
{
|
||||
const bet_object& delayed_bet = *bet_iter;
|
||||
++bet_iter;
|
||||
last = bet_iter == bet_odds_idx.end() || !bet_iter->end_of_delay;
|
||||
|
||||
const betting_market_object& betting_market = delayed_bet.betting_market_id(d);
|
||||
if (betting_market.group_id == op.betting_market_group_id)
|
||||
{
|
||||
d.modify(delayed_bet, [](bet_object& bet_obj) {
|
||||
// clear the end_of_delay, which will re-sort the bet into its place in the book
|
||||
bet_obj.end_of_delay.reset();
|
||||
});
|
||||
|
||||
d.place_bet(delayed_bet);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result betting_market_create_evaluator::do_evaluate(const betting_market_create_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
|
||||
// the betting_market_group_id in the operation can be a relative id. If it is,
|
||||
// resolve it and verify that it is truly an betting_market_group
|
||||
object_id_type resolved_betting_market_group_id = op.group_id;
|
||||
if (is_relative(op.group_id))
|
||||
resolved_betting_market_group_id = get_relative_id(op.group_id);
|
||||
|
||||
FC_ASSERT(resolved_betting_market_group_id.space() == betting_market_group_id_type::space_id &&
|
||||
resolved_betting_market_group_id.type() == betting_market_group_id_type::type_id,
|
||||
"betting_market_group_id must refer to a betting_market_group_id_type");
|
||||
_group_id = resolved_betting_market_group_id;
|
||||
FC_ASSERT(db().find_object(_group_id), "Invalid betting_market_group specified");
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type betting_market_create_evaluator::do_apply(const betting_market_create_operation& op)
|
||||
{ try {
|
||||
const betting_market_object& new_betting_market =
|
||||
db().create<betting_market_object>([&](betting_market_object& betting_market_obj) {
|
||||
betting_market_obj.group_id = _group_id;
|
||||
betting_market_obj.description = op.description;
|
||||
betting_market_obj.payout_condition = op.payout_condition;
|
||||
});
|
||||
return new_betting_market.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result betting_market_update_evaluator::do_evaluate(const betting_market_update_operation& op)
|
||||
{ try {
|
||||
database& d = db();
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
_betting_market = &op.betting_market_id(d);
|
||||
FC_ASSERT(op.new_group_id.valid() || op.new_description.valid() || op.new_payout_condition.valid(), "nothing to change");
|
||||
|
||||
if (op.new_group_id.valid())
|
||||
{
|
||||
// the betting_market_group_id in the operation can be a relative id. If it is,
|
||||
// resolve it and verify that it is truly an betting_market_group
|
||||
object_id_type resolved_betting_market_group_id = *op.new_group_id;
|
||||
if (is_relative(*op.new_group_id))
|
||||
resolved_betting_market_group_id = get_relative_id(*op.new_group_id);
|
||||
|
||||
FC_ASSERT(resolved_betting_market_group_id.space() == betting_market_group_id_type::space_id &&
|
||||
resolved_betting_market_group_id.type() == betting_market_group_id_type::type_id,
|
||||
"betting_market_group_id must refer to a betting_market_group_id_type");
|
||||
_group_id = resolved_betting_market_group_id;
|
||||
FC_ASSERT(d.find_object(_group_id), "invalid betting_market_group specified");
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result betting_market_update_evaluator::do_apply(const betting_market_update_operation& op)
|
||||
{ try {
|
||||
db().modify(*_betting_market, [&](betting_market_object& betting_market) {
|
||||
if (op.new_group_id.valid())
|
||||
betting_market.group_id = _group_id;
|
||||
if (op.new_payout_condition.valid())
|
||||
betting_market.payout_condition = *op.new_payout_condition;
|
||||
if (op.new_description.valid())
|
||||
betting_market.description = *op.new_description;
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result bet_place_evaluator::do_evaluate(const bet_place_operation& op)
|
||||
{ try {
|
||||
const database& d = db();
|
||||
|
||||
_betting_market = &op.betting_market_id(d);
|
||||
_betting_market_group = &_betting_market->group_id(d);
|
||||
|
||||
FC_ASSERT( op.amount_to_bet.asset_id == _betting_market_group->asset_id,
|
||||
"Asset type bet does not match the market's asset type" );
|
||||
|
||||
ddump((_betting_market_group->get_status()));
|
||||
FC_ASSERT( _betting_market_group->get_status() != betting_market_group_status::frozen,
|
||||
"Unable to place bets while the market is frozen" );
|
||||
|
||||
_asset = &_betting_market_group->asset_id(d);
|
||||
FC_ASSERT( is_authorized_asset( d, *fee_paying_account, *_asset ) );
|
||||
|
||||
_current_params = &d.get_global_properties().parameters;
|
||||
|
||||
// are their odds valid
|
||||
FC_ASSERT( op.backer_multiplier >= _current_params->min_bet_multiplier &&
|
||||
op.backer_multiplier <= _current_params->max_bet_multiplier,
|
||||
"Bet odds are outside the blockchain's limits" );
|
||||
if (!_current_params->permitted_betting_odds_increments.empty())
|
||||
{
|
||||
bet_multiplier_type allowed_increment;
|
||||
const auto iter = _current_params->permitted_betting_odds_increments.upper_bound(op.backer_multiplier);
|
||||
if (iter == _current_params->permitted_betting_odds_increments.end())
|
||||
allowed_increment = std::prev(_current_params->permitted_betting_odds_increments.end())->second;
|
||||
else
|
||||
allowed_increment = iter->second;
|
||||
FC_ASSERT(op.backer_multiplier % allowed_increment == 0, "Bet odds must be a multiple of ${allowed_increment}", ("allowed_increment", allowed_increment));
|
||||
}
|
||||
|
||||
FC_ASSERT(op.amount_to_bet.amount > share_type(), "Cannot place a bet with zero amount");
|
||||
|
||||
// do they have enough in their account to place the bet
|
||||
FC_ASSERT( d.get_balance( *fee_paying_account, *_asset ).amount >= op.amount_to_bet.amount, "insufficient balance",
|
||||
("balance", d.get_balance(*fee_paying_account, *_asset))("amount_to_bet", op.amount_to_bet.amount) );
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type bet_place_evaluator::do_apply(const bet_place_operation& op)
|
||||
{ try {
|
||||
database& d = db();
|
||||
const bet_object& new_bet =
|
||||
d.create<bet_object>([&](bet_object& bet_obj) {
|
||||
bet_obj.bettor_id = op.bettor_id;
|
||||
bet_obj.betting_market_id = op.betting_market_id;
|
||||
bet_obj.amount_to_bet = op.amount_to_bet;
|
||||
bet_obj.backer_multiplier = op.backer_multiplier;
|
||||
bet_obj.back_or_lay = op.back_or_lay;
|
||||
if (_betting_market_group->bets_are_delayed()) {
|
||||
// the bet will be included in the block at time `head_block_time() + block_interval`, so make the delay relative
|
||||
// to the time it's included in a block
|
||||
bet_obj.end_of_delay = d.head_block_time() + _current_params->block_interval + _current_params->live_betting_delay_time;
|
||||
}
|
||||
});
|
||||
|
||||
bet_id_type new_bet_id = new_bet.id; // save the bet id here, new_bet may be deleted during place_bet()
|
||||
|
||||
d.adjust_balance(fee_paying_account->id, -op.amount_to_bet);
|
||||
|
||||
ddump((_betting_market_group->bets_are_delayed())(_current_params->live_betting_delay_time));
|
||||
if (!_betting_market_group->bets_are_delayed() || _current_params->live_betting_delay_time <= 0)
|
||||
d.place_bet(new_bet);
|
||||
|
||||
return new_bet_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result bet_cancel_evaluator::do_evaluate(const bet_cancel_operation& op)
|
||||
{ try {
|
||||
const database& d = db();
|
||||
_bet_to_cancel = &op.bet_to_cancel(d);
|
||||
FC_ASSERT( op.bettor_id == _bet_to_cancel->bettor_id, "You can only cancel your own bets" );
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result bet_cancel_evaluator::do_apply(const bet_cancel_operation& op)
|
||||
{ try {
|
||||
db().cancel_bet(*_bet_to_cancel);
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result betting_market_group_resolve_evaluator::do_evaluate(const betting_market_group_resolve_operation& op)
|
||||
{ try {
|
||||
database& d = db();
|
||||
_betting_market_group = &op.betting_market_group_id(d);
|
||||
d.validate_betting_market_group_resolutions(*_betting_market_group, op.resolutions);
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result betting_market_group_resolve_evaluator::do_apply(const betting_market_group_resolve_operation& op)
|
||||
{ try {
|
||||
db().resolve_betting_market_group(*_betting_market_group, op.resolutions);
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result betting_market_group_cancel_unmatched_bets_evaluator::do_evaluate(const betting_market_group_cancel_unmatched_bets_operation& op)
|
||||
{ try {
|
||||
_betting_market_group = &op.betting_market_group_id(db());
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result betting_market_group_cancel_unmatched_bets_evaluator::do_apply(const betting_market_group_cancel_unmatched_bets_operation& op)
|
||||
{ try {
|
||||
db().cancel_all_unmatched_bets_on_betting_market_group(*_betting_market_group);
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
541
libraries/chain/betting_market_group_object.cpp
Normal file
541
libraries/chain/betting_market_group_object.cpp
Normal file
|
|
@ -0,0 +1,541 @@
|
|||
#define DEFAULT_LOGGER "betting"
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <boost/math/common_factor_rt.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>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
enum class betting_market_group_state {
|
||||
upcoming,
|
||||
frozen_upcoming,
|
||||
in_play,
|
||||
frozen_in_play,
|
||||
closed,
|
||||
graded,
|
||||
canceled,
|
||||
settled
|
||||
};
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::betting_market_group_state,
|
||||
(upcoming)
|
||||
(frozen_upcoming)
|
||||
(in_play)
|
||||
(frozen_in_play)
|
||||
(closed)
|
||||
(graded)
|
||||
(canceled)
|
||||
(settled))
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
namespace msm = boost::msm;
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
// betting market object implementation
|
||||
namespace
|
||||
{
|
||||
// Events -- most events happen when the witnesses publish an update operation with a new
|
||||
// status, so if they publish an event with the status set to `frozen`, we'll generate a `frozen_event`
|
||||
struct upcoming_event
|
||||
{
|
||||
database& db;
|
||||
upcoming_event(database& db) : db(db) {}
|
||||
};
|
||||
struct frozen_event
|
||||
{
|
||||
database& db;
|
||||
frozen_event(database& db) : db(db) {}
|
||||
};
|
||||
struct in_play_event
|
||||
{
|
||||
database& db;
|
||||
in_play_event(database& db) : db(db) {}
|
||||
};
|
||||
struct closed_event
|
||||
{
|
||||
database& db;
|
||||
bool closed_by_event;
|
||||
closed_event(database& db, bool closed_by_event) : db(db), closed_by_event(closed_by_event) {}
|
||||
};
|
||||
struct graded_event
|
||||
{
|
||||
database& db;
|
||||
graded_event(database& db) : db(db) {}
|
||||
};
|
||||
struct re_grading_event
|
||||
{
|
||||
database& db;
|
||||
re_grading_event(database& db) : db(db) {}
|
||||
};
|
||||
struct settled_event
|
||||
{
|
||||
database& db;
|
||||
settled_event(database& db) : db(db) {}
|
||||
};
|
||||
struct canceled_event
|
||||
{
|
||||
database& db;
|
||||
|
||||
// true if this was triggered by setting event to canceled state,
|
||||
// false if this was triggered directly on this betting market group
|
||||
bool canceled_by_event;
|
||||
|
||||
canceled_event(database& db, bool canceled_by_event = false) : db(db), canceled_by_event(canceled_by_event) {}
|
||||
};
|
||||
|
||||
// Events
|
||||
struct betting_market_group_state_machine_ : public msm::front::state_machine_def<betting_market_group_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 upcoming : public msm::front::state<>
|
||||
{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> upcoming", ("id", fsm.betting_market_group_obj->id));
|
||||
// when a betting market group goes from frozen -> upcoming, transition the markets from frozen -> unresolved
|
||||
auto& betting_market_index = event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.on_unresolved_event(event.db);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
}
|
||||
}
|
||||
};
|
||||
struct frozen_upcoming : public msm::front::state<>
|
||||
{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> frozen_upcoming", ("id", fsm.betting_market_group_obj->id));
|
||||
|
||||
auto& betting_market_index = event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.on_frozen_event(event.db);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
struct in_play : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> in_play", ("id", fsm.betting_market_group_obj->id));
|
||||
// when an event goes in-play, cancel all unmatched bets in its betting markets
|
||||
auto& betting_market_index = event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.cancel_all_unmatched_bets(event.db);
|
||||
try {
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.on_unresolved_event(event.db);
|
||||
});
|
||||
} catch (const graphene::chain::no_transition&) {
|
||||
// if this wasn't a transition from frozen state, this wasn't necessary
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
struct frozen_in_play : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> frozen_in_play", ("id", fsm.betting_market_group_obj->id));
|
||||
auto& betting_market_index = event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.on_frozen_event(event.db);
|
||||
});
|
||||
}
|
||||
};
|
||||
struct closed : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& fsm_event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> closed", ("id", fsm.betting_market_group_obj->id));
|
||||
auto& betting_market_index = fsm_event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
fsm_event.db.modify(betting_market, [&fsm_event](betting_market_object& betting_market) {
|
||||
betting_market.cancel_all_unmatched_bets(fsm_event.db);
|
||||
betting_market.on_closed_event(fsm_event.db);
|
||||
});
|
||||
|
||||
// then notify the event that this betting market is now closed so it can change its status accordingly
|
||||
if (!fsm_event.closed_by_event) {
|
||||
const event_object& event = fsm.betting_market_group_obj->event_id(fsm_event.db);
|
||||
fsm_event.db.modify(event, [&fsm_event,&fsm](event_object& event_obj) {
|
||||
event_obj.on_betting_market_group_closed(fsm_event.db, fsm.betting_market_group_obj->id);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
struct graded : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> graded", ("id", fsm.betting_market_group_obj->id));
|
||||
fsm.betting_market_group_obj->settling_time = event.db.head_block_time() + fsm.betting_market_group_obj->delay_before_settling;
|
||||
dlog("grading complete, setting settling time to ${settling_time}", ("settling_time", fsm.betting_market_group_obj->settling_time));
|
||||
}
|
||||
};
|
||||
struct re_grading : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> re_grading", ("id", fsm.betting_market_group_obj->id));
|
||||
}
|
||||
};
|
||||
struct settled : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& fsm_event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> settled", ("id", fsm.betting_market_group_obj->id));
|
||||
// TODO: what triggers this? I guess it will be the blockchain when its settling delay expires. So in that case, it should
|
||||
// trigger the payout in the betting markets
|
||||
auto& betting_market_index = fsm_event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
fsm_event.db.modify(betting_market, [&fsm_event](betting_market_object& betting_market) {
|
||||
betting_market.on_settled_event(fsm_event.db);
|
||||
});
|
||||
|
||||
// then notify the event that this betting market is now resolved so it can change its status accordingly
|
||||
const event_object& event = fsm.betting_market_group_obj->event_id(fsm_event.db);
|
||||
fsm_event.db.modify(event, [&fsm_event,&fsm](event_object& event_obj) {
|
||||
event_obj.on_betting_market_group_resolved(fsm_event.db, fsm.betting_market_group_obj->id, false);
|
||||
});
|
||||
}
|
||||
};
|
||||
struct canceled : public msm::front::state<>{
|
||||
void on_entry(const canceled_event& fsm_event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> canceled", ("id", fsm.betting_market_group_obj->id));
|
||||
auto& betting_market_index = fsm_event.db.get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
||||
auto betting_markets_in_group = boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id));
|
||||
|
||||
for (const betting_market_object& betting_market : betting_markets_in_group)
|
||||
fsm_event.db.modify(betting_market, [&fsm_event](betting_market_object& betting_market_obj) {
|
||||
betting_market_obj.on_canceled_event(fsm_event.db);
|
||||
});
|
||||
|
||||
if (!fsm_event.canceled_by_event) {
|
||||
const event_object& event = fsm.betting_market_group_obj->event_id(fsm_event.db);
|
||||
fsm_event.db.modify(event, [&fsm_event,&fsm](event_object& event_obj) {
|
||||
event_obj.on_betting_market_group_resolved(fsm_event.db, fsm.betting_market_group_obj->id, true);
|
||||
});
|
||||
}
|
||||
|
||||
fsm.betting_market_group_obj->settling_time = fsm_event.db.head_block_time();
|
||||
dlog("cancel complete, setting settling time to ${settling_time}", ("settling_time", fsm.betting_market_group_obj->settling_time));
|
||||
}
|
||||
};
|
||||
|
||||
typedef upcoming initial_state;
|
||||
|
||||
// actions
|
||||
void cancel_all_unmatched_bets(const in_play_event& event) {
|
||||
event.db.cancel_all_unmatched_bets_on_betting_market_group(*betting_market_group_obj);
|
||||
}
|
||||
|
||||
// guards
|
||||
bool in_play_is_allowed(const in_play_event& event) {
|
||||
return !betting_market_group_obj->never_in_play;
|
||||
}
|
||||
|
||||
typedef betting_market_group_state_machine_ x; // makes transition table cleaner
|
||||
|
||||
// Transition table for betting market
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < upcoming, frozen_event, frozen_upcoming >,
|
||||
row < upcoming, in_play_event, in_play, &x::cancel_all_unmatched_bets, &x::in_play_is_allowed >,
|
||||
_row < upcoming, closed_event, closed >,
|
||||
_row < upcoming, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < frozen_upcoming, upcoming_event, upcoming >,
|
||||
_row < frozen_upcoming, in_play_event, upcoming >,
|
||||
row < frozen_upcoming, in_play_event, in_play, &x::cancel_all_unmatched_bets, &x::in_play_is_allowed >,
|
||||
_row < frozen_upcoming, closed_event, closed >,
|
||||
_row < frozen_upcoming, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < in_play, frozen_event, frozen_in_play >,
|
||||
_row < in_play, closed_event, closed >,
|
||||
_row < in_play, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < frozen_in_play, in_play_event, in_play >,
|
||||
_row < frozen_in_play, closed_event, closed >,
|
||||
_row < frozen_in_play, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < closed, graded_event, graded >,
|
||||
_row < closed, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
//_row < graded re_grading_event, re_grading >,
|
||||
_row < graded, settled_event, settled >,
|
||||
_row < graded, canceled_event, canceled >
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
//_row < re_grading, graded_event, graded >,
|
||||
//_row < re_grading, canceled_event, canceled >
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
> {};
|
||||
|
||||
template <class Fsm,class Event>
|
||||
void no_transition(Event const& e, Fsm& ,int state)
|
||||
{
|
||||
FC_THROW_EXCEPTION(graphene::chain::no_transition, "No transition");
|
||||
}
|
||||
|
||||
betting_market_group_object* betting_market_group_obj;
|
||||
betting_market_group_state_machine_(betting_market_group_object* betting_market_group_obj) : betting_market_group_obj(betting_market_group_obj) {}
|
||||
};
|
||||
typedef msm::back::state_machine<betting_market_group_state_machine_> betting_market_group_state_machine;
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
class betting_market_group_object::impl {
|
||||
public:
|
||||
betting_market_group_state_machine state_machine;
|
||||
|
||||
impl(betting_market_group_object* self) : state_machine(self) {}
|
||||
};
|
||||
|
||||
betting_market_group_object::betting_market_group_object() :
|
||||
my(new impl(this))
|
||||
{
|
||||
dlog("betting_market_group_object ctor");
|
||||
}
|
||||
|
||||
betting_market_group_object::betting_market_group_object(const betting_market_group_object& rhs) :
|
||||
graphene::db::abstract_object<betting_market_group_object>(rhs),
|
||||
description(rhs.description),
|
||||
event_id(rhs.event_id),
|
||||
rules_id(rhs.rules_id),
|
||||
asset_id(rhs.asset_id),
|
||||
total_matched_bets_amount(rhs.total_matched_bets_amount),
|
||||
never_in_play(rhs.never_in_play),
|
||||
delay_before_settling(rhs.delay_before_settling),
|
||||
settling_time(rhs.settling_time),
|
||||
my(new impl(this))
|
||||
{
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.betting_market_group_obj = this;
|
||||
}
|
||||
|
||||
betting_market_group_object& betting_market_group_object::operator=(const betting_market_group_object& rhs)
|
||||
{
|
||||
//graphene::db::abstract_object<betting_market_group_object>::operator=(rhs);
|
||||
id = rhs.id;
|
||||
description = rhs.description;
|
||||
event_id = rhs.event_id;
|
||||
rules_id = rhs.rules_id;
|
||||
asset_id = rhs.asset_id;
|
||||
total_matched_bets_amount = rhs.total_matched_bets_amount;
|
||||
never_in_play = rhs.never_in_play;
|
||||
delay_before_settling = rhs.delay_before_settling;
|
||||
settling_time = rhs.settling_time;
|
||||
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.betting_market_group_obj = this;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
betting_market_group_object::~betting_market_group_object()
|
||||
{
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool verify_betting_market_group_status_constants()
|
||||
{
|
||||
unsigned error_count = 0;
|
||||
typedef msm::back::generate_state_set<betting_market_group_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<betting_market_group_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<betting_market_group_state>::to_string((betting_market_group_state)i);
|
||||
if (!strcmp(fc_reflected_value_name, filled_state_names[i]))
|
||||
fc_elog(fc::logger::get("default"),
|
||||
"Error, state string mismatch 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("default"),
|
||||
"Error, no reflection for value ${int_value} in enum betting_market_group_status",
|
||||
("int_value", i));
|
||||
++error_count;
|
||||
}
|
||||
}
|
||||
dlog("Done checking constants");
|
||||
|
||||
return error_count == 0;
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
betting_market_group_status betting_market_group_object::get_status() const
|
||||
{
|
||||
static bool state_constants_are_correct = verify_betting_market_group_status_constants();
|
||||
(void)&state_constants_are_correct;
|
||||
betting_market_group_state state = (betting_market_group_state)my->state_machine.current_state()[0];
|
||||
|
||||
ddump((state));
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case betting_market_group_state::upcoming:
|
||||
return betting_market_group_status::upcoming;
|
||||
case betting_market_group_state::frozen_upcoming:
|
||||
return betting_market_group_status::frozen;
|
||||
case betting_market_group_state::in_play:
|
||||
return betting_market_group_status::in_play;
|
||||
case betting_market_group_state::frozen_in_play:
|
||||
return betting_market_group_status::frozen;
|
||||
case betting_market_group_state::closed:
|
||||
return betting_market_group_status::closed;
|
||||
case betting_market_group_state::graded:
|
||||
return betting_market_group_status::graded;
|
||||
case betting_market_group_state::canceled:
|
||||
return betting_market_group_status::canceled;
|
||||
case betting_market_group_state::settled:
|
||||
return betting_market_group_status::settled;
|
||||
default:
|
||||
FC_THROW("Unexpected betting market group state");
|
||||
};
|
||||
}
|
||||
|
||||
void betting_market_group_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 betting_market_group_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;
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_upcoming_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(upcoming_event(db));
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_in_play_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(in_play_event(db));
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_frozen_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(frozen_event(db));
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_closed_event(database& db, bool closed_by_event)
|
||||
{
|
||||
my->state_machine.process_event(closed_event(db, closed_by_event));
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_graded_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(graded_event(db));
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_re_grading_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(re_grading_event(db));
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_settled_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(settled_event(db));
|
||||
}
|
||||
|
||||
void betting_market_group_object::on_canceled_event(database& db, bool canceled_by_event)
|
||||
{
|
||||
my->state_machine.process_event(canceled_event(db, canceled_by_event));
|
||||
}
|
||||
|
||||
// These are the only statuses that can be explicitly set by witness operations.
|
||||
// Other states can only be reached indirectly (i.e., settling happens a fixed
|
||||
// delay after grading)
|
||||
void betting_market_group_object::dispatch_new_status(database& db, betting_market_group_status new_status)
|
||||
{
|
||||
switch (new_status) {
|
||||
case betting_market_group_status::upcoming: // by witnesses to unfreeze a bmg
|
||||
on_upcoming_event(db);
|
||||
break;
|
||||
case betting_market_group_status::in_play: // by witnesses to make a bmg in-play
|
||||
on_in_play_event(db);
|
||||
break;
|
||||
case betting_market_group_status::closed: // by witnesses to close a bmg
|
||||
on_closed_event(db, false);
|
||||
break;
|
||||
case betting_market_group_status::frozen: // by witnesses to freeze a bmg
|
||||
on_frozen_event(db);
|
||||
break;
|
||||
case betting_market_group_status::canceled: // by witnesses to cancel a bmg
|
||||
on_canceled_event(db, false);
|
||||
break;
|
||||
default:
|
||||
FC_THROW("The status ${new_status} cannot be set directly", ("new_status", new_status));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
namespace fc {
|
||||
// Manually reflect betting_market_group_object to variant to properly reflect "state"
|
||||
void to_variant(const graphene::chain::betting_market_group_object& betting_market_group_obj, fc::variant& v)
|
||||
{
|
||||
fc::mutable_variant_object o;
|
||||
o("id", betting_market_group_obj.id)
|
||||
("description", betting_market_group_obj.description)
|
||||
("event_id", betting_market_group_obj.event_id)
|
||||
("rules_id", betting_market_group_obj.rules_id)
|
||||
("asset_id", betting_market_group_obj.asset_id)
|
||||
("total_matched_bets_amount", betting_market_group_obj.total_matched_bets_amount)
|
||||
("never_in_play", betting_market_group_obj.never_in_play)
|
||||
("delay_before_settling", betting_market_group_obj.delay_before_settling)
|
||||
("settling_time", betting_market_group_obj.settling_time)
|
||||
("status", betting_market_group_obj.get_status());
|
||||
|
||||
v = o;
|
||||
}
|
||||
|
||||
// Manually reflect betting_market_group_object to variant to properly reflect "state"
|
||||
void from_variant(const fc::variant& v, graphene::chain::betting_market_group_object& betting_market_group_obj)
|
||||
{
|
||||
betting_market_group_obj.id = v["id"].as<graphene::chain::betting_market_group_id_type>();
|
||||
betting_market_group_obj.description = v["description"].as<graphene::chain::internationalized_string_type>();
|
||||
betting_market_group_obj.event_id = v["event_id"].as<graphene::chain::event_id_type>();
|
||||
betting_market_group_obj.asset_id = v["asset_id"].as<graphene::chain::asset_id_type>();
|
||||
betting_market_group_obj.total_matched_bets_amount = v["total_matched_bets_amount"].as<graphene::chain::share_type>();
|
||||
betting_market_group_obj.never_in_play = v["never_in_play"].as<bool>();
|
||||
betting_market_group_obj.delay_before_settling = v["delay_before_settling"].as<uint32_t>();
|
||||
betting_market_group_obj.settling_time = v["settling_time"].as<fc::optional<fc::time_point_sec>>();
|
||||
graphene::chain::betting_market_group_status status = v["status"].as<graphene::chain::betting_market_group_status>();
|
||||
const_cast<int*>(betting_market_group_obj.my->state_machine.current_state())[0] = (int)status;
|
||||
}
|
||||
} //end namespace fc
|
||||
|
||||
441
libraries/chain/betting_market_object.cpp
Normal file
441
libraries/chain/betting_market_object.cpp
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
#define DEFAULT_LOGGER "betting"
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <boost/math/common_factor_rt.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>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
enum class betting_market_state {
|
||||
unresolved,
|
||||
frozen,
|
||||
closed,
|
||||
canceled,
|
||||
graded,
|
||||
settled
|
||||
};
|
||||
} }
|
||||
FC_REFLECT_ENUM(graphene::chain::betting_market_state,
|
||||
(unresolved)
|
||||
(frozen)
|
||||
(closed)
|
||||
(canceled)
|
||||
(graded)
|
||||
(settled))
|
||||
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
namespace msm = boost::msm;
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
/* static */ share_type bet_object::get_approximate_matching_amount(share_type bet_amount, bet_multiplier_type backer_multiplier, bet_type back_or_lay, bool round_up /* = false */)
|
||||
{
|
||||
fc::uint128_t amount_to_match_128 = bet_amount.value;
|
||||
|
||||
if (back_or_lay == bet_type::back)
|
||||
{
|
||||
amount_to_match_128 *= backer_multiplier - GRAPHENE_BETTING_ODDS_PRECISION;
|
||||
if (round_up)
|
||||
amount_to_match_128 += GRAPHENE_BETTING_ODDS_PRECISION - 1;
|
||||
amount_to_match_128 /= GRAPHENE_BETTING_ODDS_PRECISION;
|
||||
}
|
||||
else
|
||||
{
|
||||
amount_to_match_128 *= GRAPHENE_BETTING_ODDS_PRECISION;
|
||||
if (round_up)
|
||||
amount_to_match_128 += backer_multiplier - GRAPHENE_BETTING_ODDS_PRECISION - 1;
|
||||
amount_to_match_128 /= backer_multiplier - GRAPHENE_BETTING_ODDS_PRECISION;
|
||||
}
|
||||
return amount_to_match_128.to_uint64();
|
||||
}
|
||||
|
||||
share_type bet_object::get_approximate_matching_amount(bool round_up /* = false */) const
|
||||
{
|
||||
return get_approximate_matching_amount(amount_to_bet.amount, backer_multiplier, back_or_lay, round_up);
|
||||
}
|
||||
|
||||
/* static */ share_type bet_object::get_exact_matching_amount(share_type bet_amount, bet_multiplier_type backer_multiplier, bet_type back_or_lay)
|
||||
{
|
||||
share_type back_ratio;
|
||||
share_type lay_ratio;
|
||||
std::tie(back_ratio, lay_ratio) = get_ratio(backer_multiplier);
|
||||
if (back_or_lay == bet_type::back)
|
||||
return bet_amount / back_ratio * lay_ratio;
|
||||
else
|
||||
return bet_amount / lay_ratio * back_ratio;
|
||||
}
|
||||
|
||||
share_type bet_object::get_exact_matching_amount() const
|
||||
{
|
||||
return get_exact_matching_amount(amount_to_bet.amount, backer_multiplier, back_or_lay);
|
||||
}
|
||||
|
||||
/* static */ std::pair<share_type, share_type> bet_object::get_ratio(bet_multiplier_type backer_multiplier)
|
||||
{
|
||||
share_type gcd = boost::math::gcd<bet_multiplier_type>(GRAPHENE_BETTING_ODDS_PRECISION, backer_multiplier - GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
return std::make_pair(GRAPHENE_BETTING_ODDS_PRECISION / gcd, (backer_multiplier - GRAPHENE_BETTING_ODDS_PRECISION) / gcd);
|
||||
}
|
||||
|
||||
std::pair<share_type, share_type> bet_object::get_ratio() const
|
||||
{
|
||||
return get_ratio(backer_multiplier);
|
||||
}
|
||||
|
||||
share_type bet_object::get_minimum_matchable_amount() const
|
||||
{
|
||||
share_type gcd = boost::math::gcd<bet_multiplier_type>(GRAPHENE_BETTING_ODDS_PRECISION, backer_multiplier - GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
return (back_or_lay == bet_type::back ? GRAPHENE_BETTING_ODDS_PRECISION : backer_multiplier - GRAPHENE_BETTING_ODDS_PRECISION) / gcd;
|
||||
}
|
||||
|
||||
share_type bet_object::get_minimum_matching_amount() const
|
||||
{
|
||||
share_type gcd = boost::math::gcd<bet_multiplier_type>(GRAPHENE_BETTING_ODDS_PRECISION, backer_multiplier - GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
return (back_or_lay == bet_type::lay ? GRAPHENE_BETTING_ODDS_PRECISION : backer_multiplier - GRAPHENE_BETTING_ODDS_PRECISION) / gcd;
|
||||
}
|
||||
|
||||
|
||||
share_type betting_market_position_object::reduce()
|
||||
{
|
||||
share_type additional_not_cancel_balance = std::min(pay_if_payout_condition, pay_if_not_payout_condition);
|
||||
if (additional_not_cancel_balance == 0)
|
||||
return 0;
|
||||
pay_if_payout_condition -= additional_not_cancel_balance;
|
||||
pay_if_not_payout_condition -= additional_not_cancel_balance;
|
||||
pay_if_not_canceled += additional_not_cancel_balance;
|
||||
|
||||
share_type immediate_winnings = std::min(pay_if_canceled, pay_if_not_canceled);
|
||||
if (immediate_winnings == 0)
|
||||
return 0;
|
||||
pay_if_canceled -= immediate_winnings;
|
||||
pay_if_not_canceled -= immediate_winnings;
|
||||
return immediate_winnings;
|
||||
}
|
||||
|
||||
// betting market object implementation
|
||||
namespace
|
||||
{
|
||||
// Events -- most events happen when the witnesses publish an update operation with a new
|
||||
// status, so if they publish an event with the status set to `frozen`, we'll generate a `frozen_event`
|
||||
struct unresolved_event
|
||||
{
|
||||
database& db;
|
||||
unresolved_event(database& db) : db(db) {}
|
||||
};
|
||||
struct frozen_event
|
||||
{
|
||||
database& db;
|
||||
frozen_event(database& db) : db(db) {}
|
||||
};
|
||||
struct closed_event
|
||||
{
|
||||
database& db;
|
||||
closed_event(database& db) : db(db) {}
|
||||
};
|
||||
struct graded_event
|
||||
{
|
||||
database& db;
|
||||
betting_market_resolution_type new_grading;
|
||||
graded_event(database& db, betting_market_resolution_type new_grading) : db(db), new_grading(new_grading) {}
|
||||
};
|
||||
struct settled_event
|
||||
{
|
||||
database& db;
|
||||
settled_event(database& db) : db(db) {}
|
||||
};
|
||||
struct canceled_event
|
||||
{
|
||||
database& db;
|
||||
canceled_event(database& db) : db(db) {}
|
||||
};
|
||||
|
||||
// Events
|
||||
struct betting_market_state_machine_ : public msm::front::state_machine_def<betting_market_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 unresolved : public msm::front::state<>{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_state_machine_& fsm) {
|
||||
dlog("betting market ${id} -> unresolved", ("id", fsm.betting_market_obj->id));
|
||||
}
|
||||
};
|
||||
struct frozen : public msm::front::state<>{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_state_machine_& fsm) {
|
||||
dlog("betting market ${id} -> frozen", ("id", fsm.betting_market_obj->id));
|
||||
}
|
||||
};
|
||||
struct closed : public msm::front::state<>{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_state_machine_& fsm) {
|
||||
dlog("betting market ${id} -> closed", ("id", fsm.betting_market_obj->id));
|
||||
}
|
||||
};
|
||||
struct graded : public msm::front::state<>{
|
||||
void on_entry(const graded_event& event, betting_market_state_machine_& fsm) {
|
||||
dlog("betting market ${id} -> graded", ("id", fsm.betting_market_obj->id));
|
||||
fsm.betting_market_obj->resolution = event.new_grading;
|
||||
}
|
||||
};
|
||||
struct settled : public msm::front::state<>{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_state_machine_& fsm) {
|
||||
dlog("betting market ${id} -> settled", ("id", fsm.betting_market_obj->id));
|
||||
}
|
||||
};
|
||||
struct canceled : public msm::front::state<>{
|
||||
void on_entry(const canceled_event& event, betting_market_state_machine_& fsm) {
|
||||
dlog("betting market ${id} -> canceled", ("id", fsm.betting_market_obj->id));
|
||||
fsm.betting_market_obj->resolution = betting_market_resolution_type::cancel;
|
||||
}
|
||||
};
|
||||
|
||||
typedef unresolved initial_state;
|
||||
typedef betting_market_state_machine_ x; // makes transition table cleaner
|
||||
|
||||
|
||||
// Transition table for betting market
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < unresolved, frozen_event, frozen >,
|
||||
_row < unresolved, closed_event, closed >,
|
||||
_row < unresolved, canceled_event, canceled >,
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < frozen, unresolved_event, unresolved >,
|
||||
_row < frozen, closed_event, closed >,
|
||||
_row < frozen, canceled_event, canceled >,
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < closed, graded_event, graded >,
|
||||
_row < closed, canceled_event, canceled >,
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < graded, settled_event, settled >,
|
||||
_row < graded, canceled_event, canceled >
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
> {};
|
||||
|
||||
template <class Fsm,class Event>
|
||||
void no_transition(Event const& e, Fsm& ,int state)
|
||||
{
|
||||
FC_THROW_EXCEPTION(graphene::chain::no_transition, "No transition");
|
||||
}
|
||||
|
||||
betting_market_object* betting_market_obj;
|
||||
betting_market_state_machine_(betting_market_object* betting_market_obj) : betting_market_obj(betting_market_obj) {}
|
||||
};
|
||||
typedef msm::back::state_machine<betting_market_state_machine_> betting_market_state_machine;
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
class betting_market_object::impl {
|
||||
public:
|
||||
betting_market_state_machine state_machine;
|
||||
|
||||
impl(betting_market_object* self) : state_machine(self) {}
|
||||
};
|
||||
|
||||
betting_market_object::betting_market_object() :
|
||||
my(new impl(this))
|
||||
{
|
||||
}
|
||||
|
||||
betting_market_object::betting_market_object(const betting_market_object& rhs) :
|
||||
graphene::db::abstract_object<betting_market_object>(rhs),
|
||||
group_id(rhs.group_id),
|
||||
description(rhs.description),
|
||||
payout_condition(rhs.payout_condition),
|
||||
my(new impl(this))
|
||||
{
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.betting_market_obj = this;
|
||||
}
|
||||
|
||||
betting_market_object& betting_market_object::operator=(const betting_market_object& rhs)
|
||||
{
|
||||
//graphene::db::abstract_object<betting_market_object>::operator=(rhs);
|
||||
id = rhs.id;
|
||||
group_id = rhs.group_id;
|
||||
description = rhs.description;
|
||||
payout_condition = rhs.payout_condition;
|
||||
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.betting_market_obj = this;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
betting_market_object::~betting_market_object()
|
||||
{
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
bool verify_betting_market_status_constants()
|
||||
{
|
||||
unsigned error_count = 0;
|
||||
typedef msm::back::generate_state_set<betting_market_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<betting_market_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<betting_market_state>::to_string((betting_market_state)i);
|
||||
if (!strcmp(fc_reflected_value_name, filled_state_names[i]))
|
||||
fc_elog(fc::logger::get("default"),
|
||||
"Error, state string mismatch 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("default"),
|
||||
"Error, no reflection for value ${int_value} in enum betting_market_status",
|
||||
("int_value", i));
|
||||
++error_count;
|
||||
}
|
||||
}
|
||||
dlog("Done checking constants");
|
||||
|
||||
return error_count == 0;
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
betting_market_status betting_market_object::get_status() const
|
||||
{
|
||||
static bool state_constants_are_correct = verify_betting_market_status_constants();
|
||||
(void)&state_constants_are_correct;
|
||||
betting_market_state state = (betting_market_state)my->state_machine.current_state()[0];
|
||||
|
||||
edump((state));
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case betting_market_state::unresolved:
|
||||
return betting_market_status::unresolved;
|
||||
case betting_market_state::frozen:
|
||||
return betting_market_status::frozen;
|
||||
case betting_market_state::closed:
|
||||
return betting_market_status::unresolved;
|
||||
case betting_market_state::canceled:
|
||||
return betting_market_status::canceled;
|
||||
case betting_market_state::graded:
|
||||
return betting_market_status::graded;
|
||||
case betting_market_state::settled:
|
||||
return betting_market_status::settled;
|
||||
default:
|
||||
FC_THROW("Unexpected betting market state");
|
||||
};
|
||||
}
|
||||
|
||||
void betting_market_object::cancel_all_unmatched_bets(database& db) const
|
||||
{
|
||||
const auto& bet_odds_idx = db.get_index_type<bet_object_index>().indices().get<by_odds>();
|
||||
|
||||
// first, cancel all bets on the active books
|
||||
auto book_itr = bet_odds_idx.lower_bound(std::make_tuple(id));
|
||||
auto book_end = bet_odds_idx.upper_bound(std::make_tuple(id));
|
||||
while (book_itr != book_end)
|
||||
{
|
||||
auto old_book_itr = book_itr;
|
||||
++book_itr;
|
||||
db.cancel_bet(*old_book_itr, true);
|
||||
}
|
||||
|
||||
// then, cancel any delayed bets on that market. We don't have an index for
|
||||
// that, so walk through all delayed bets
|
||||
book_itr = bet_odds_idx.begin();
|
||||
while (book_itr != bet_odds_idx.end() &&
|
||||
book_itr->end_of_delay)
|
||||
{
|
||||
auto old_book_itr = book_itr;
|
||||
++book_itr;
|
||||
if (old_book_itr->betting_market_id == id)
|
||||
db.cancel_bet(*old_book_itr, true);
|
||||
}
|
||||
}
|
||||
|
||||
void betting_market_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 betting_market_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;
|
||||
}
|
||||
|
||||
void betting_market_object::on_unresolved_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(unresolved_event(db));
|
||||
}
|
||||
|
||||
void betting_market_object::on_frozen_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(frozen_event(db));
|
||||
}
|
||||
|
||||
void betting_market_object::on_closed_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(closed_event(db));
|
||||
}
|
||||
|
||||
void betting_market_object::on_graded_event(database& db, betting_market_resolution_type new_grading)
|
||||
{
|
||||
my->state_machine.process_event(graded_event(db, new_grading));
|
||||
}
|
||||
|
||||
void betting_market_object::on_settled_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(settled_event(db));
|
||||
}
|
||||
|
||||
void betting_market_object::on_canceled_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(canceled_event(db));
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
namespace fc {
|
||||
// Manually reflect betting_market_object to variant to properly reflect "state"
|
||||
void to_variant(const graphene::chain::betting_market_object& event_obj, fc::variant& v)
|
||||
{
|
||||
fc::mutable_variant_object o;
|
||||
o("id", event_obj.id)
|
||||
("group_id", event_obj.group_id)
|
||||
("description", event_obj.description)
|
||||
("payout_condition", event_obj.payout_condition)
|
||||
("resolution", event_obj.resolution)
|
||||
("status", event_obj.get_status());
|
||||
|
||||
v = o;
|
||||
}
|
||||
|
||||
// Manually reflect betting_market_object to variant to properly reflect "state"
|
||||
void from_variant(const fc::variant& v, graphene::chain::betting_market_object& event_obj)
|
||||
{
|
||||
event_obj.id = v["id"].as<graphene::chain::betting_market_id_type>();
|
||||
event_obj.group_id = v["name"].as<graphene::chain::betting_market_group_id_type>();
|
||||
event_obj.description = v["description"].as<graphene::chain::internationalized_string_type>();
|
||||
event_obj.payout_condition = v["payout_condition"].as<graphene::chain::internationalized_string_type>();
|
||||
event_obj.resolution = v["resolution"].as<fc::optional<graphene::chain::betting_market_resolution_type>>();
|
||||
graphene::chain::betting_market_status status = v["status"].as<graphene::chain::betting_market_status>();
|
||||
const_cast<int*>(event_obj.my->state_machine.current_state())[0] = (int)status;
|
||||
}
|
||||
} //end namespace fc
|
||||
|
||||
|
|
@ -29,6 +29,8 @@
|
|||
#include <graphene/chain/fba_accumulator_id.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_operation& o )
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include "db_balance.cpp"
|
||||
#include "db_bet.cpp"
|
||||
#include "db_block.cpp"
|
||||
#include "db_debug.cpp"
|
||||
#include "db_getter.cpp"
|
||||
|
|
@ -32,3 +33,4 @@
|
|||
#include "db_market.cpp"
|
||||
#include "db_update.cpp"
|
||||
#include "db_witness_schedule.cpp"
|
||||
#include "db_notify.cpp"
|
||||
509
libraries/chain/db_bet.cpp
Normal file
509
libraries/chain/db_bet.cpp
Normal file
|
|
@ -0,0 +1,509 @@
|
|||
#define DEFAULT_LOGGER "betting"
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <boost/range/combine.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void database::cancel_bet( const bet_object& bet, bool create_virtual_op )
|
||||
{
|
||||
asset amount_to_refund = bet.amount_to_bet;
|
||||
//TODO: update global statistics
|
||||
adjust_balance(bet.bettor_id, amount_to_refund);
|
||||
if (create_virtual_op)
|
||||
{
|
||||
bet_canceled_operation bet_canceled_virtual_op(bet.bettor_id, bet.id,
|
||||
bet.amount_to_bet);
|
||||
//idump((bet_canceled_virtual_op));
|
||||
push_applied_operation(std::move(bet_canceled_virtual_op));
|
||||
}
|
||||
remove(bet);
|
||||
}
|
||||
|
||||
void database::cancel_all_unmatched_bets_on_betting_market(const betting_market_object& betting_market)
|
||||
{
|
||||
const auto& bet_odds_idx = get_index_type<bet_object_index>().indices().get<by_odds>();
|
||||
|
||||
// first, cancel all bets on the active books
|
||||
auto book_itr = bet_odds_idx.lower_bound(std::make_tuple(betting_market.id));
|
||||
auto book_end = bet_odds_idx.upper_bound(std::make_tuple(betting_market.id));
|
||||
while (book_itr != book_end)
|
||||
{
|
||||
auto old_book_itr = book_itr;
|
||||
++book_itr;
|
||||
cancel_bet(*old_book_itr, true);
|
||||
}
|
||||
|
||||
// then, cancel any delayed bets on that market. We don't have an index for
|
||||
// that, so walk through all delayed bets
|
||||
book_itr = bet_odds_idx.begin();
|
||||
while (book_itr != bet_odds_idx.end() &&
|
||||
book_itr->end_of_delay)
|
||||
{
|
||||
auto old_book_itr = book_itr;
|
||||
++book_itr;
|
||||
if (old_book_itr->betting_market_id == betting_market.id)
|
||||
cancel_bet(*old_book_itr, true);
|
||||
}
|
||||
}
|
||||
|
||||
void database::validate_betting_market_group_resolutions(const betting_market_group_object& betting_market_group,
|
||||
const std::map<betting_market_id_type, betting_market_resolution_type>& resolutions)
|
||||
{
|
||||
auto& betting_market_index = get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
||||
auto betting_markets_in_group = boost::make_iterator_range(betting_market_index.equal_range(betting_market_group.id));
|
||||
|
||||
// we must have one resolution for each betting market
|
||||
FC_ASSERT(resolutions.size() == boost::size(betting_markets_in_group),
|
||||
"You must publish resolutions for all ${size} markets in the group, you published ${published}", ("size", boost::size(betting_markets_in_group))("published", resolutions.size()));
|
||||
|
||||
// both are sorted by id, we can walk through both and verify that they match
|
||||
unsigned number_of_wins = 0;
|
||||
unsigned number_of_cancels = 0;
|
||||
for (const auto& zipped : boost::combine(resolutions, betting_markets_in_group))
|
||||
{
|
||||
const auto& resolution = boost::get<0>(zipped);
|
||||
const auto& betting_market = boost::get<1>(zipped);
|
||||
FC_ASSERT(resolution.first == betting_market.id, "Missing resolution for betting market ${id}", ("id", betting_market.id));
|
||||
if (resolution.second == betting_market_resolution_type::cancel)
|
||||
++number_of_cancels;
|
||||
else if (resolution.second == betting_market_resolution_type::win)
|
||||
++number_of_wins;
|
||||
else
|
||||
FC_ASSERT(resolution.second == betting_market_resolution_type::not_win);
|
||||
}
|
||||
|
||||
if (number_of_cancels != 0)
|
||||
FC_ASSERT(number_of_cancels == resolutions.size(), "You must cancel all betting markets or none of the betting markets in the group");
|
||||
else
|
||||
FC_ASSERT(number_of_wins == 1, "There must be exactly one winning market");
|
||||
}
|
||||
|
||||
void database::cancel_all_unmatched_bets_on_betting_market_group(const betting_market_group_object& betting_market_group)
|
||||
{
|
||||
auto& betting_market_index = get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
||||
auto betting_market_itr = betting_market_index.lower_bound(betting_market_group.id);
|
||||
while (betting_market_itr != betting_market_index.end() && betting_market_itr->group_id == betting_market_group.id)
|
||||
{
|
||||
const betting_market_object& betting_market = *betting_market_itr;
|
||||
++betting_market_itr;
|
||||
cancel_all_unmatched_bets_on_betting_market(betting_market);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void database::resolve_betting_market_group(const betting_market_group_object& betting_market_group,
|
||||
const std::map<betting_market_id_type, betting_market_resolution_type>& resolutions)
|
||||
{
|
||||
auto& betting_market_index = get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
||||
auto betting_markets_in_group = boost::make_iterator_range(betting_market_index.equal_range(betting_market_group.id));
|
||||
|
||||
bool group_was_canceled = resolutions.begin()->second == betting_market_resolution_type::cancel;
|
||||
|
||||
if (group_was_canceled)
|
||||
modify(betting_market_group, [group_was_canceled,this](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_canceled_event(*this, false); // this cancels the betting markets
|
||||
});
|
||||
else {
|
||||
// TODO: this should be pushed into the bmg's on_graded_event
|
||||
|
||||
// both are sorted by id, we can walk through both and verify that they match
|
||||
for (const auto& zipped : boost::combine(resolutions, betting_markets_in_group))
|
||||
{
|
||||
const auto& resolution = boost::get<0>(zipped);
|
||||
const auto& betting_market = boost::get<1>(zipped);
|
||||
|
||||
modify(betting_market, [this,&resolution](betting_market_object& betting_market_obj) {
|
||||
betting_market_obj.on_graded_event(*this, resolution.second);
|
||||
});
|
||||
}
|
||||
|
||||
modify(betting_market_group, [group_was_canceled,this](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_graded_event(*this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database::settle_betting_market_group(const betting_market_group_object& betting_market_group)
|
||||
{
|
||||
ilog("Settling betting market group ${id}", ("id", betting_market_group.id));
|
||||
// we pay the rake fee to the dividend distribution account for the core asset, go ahead
|
||||
// and look up that account now
|
||||
fc::optional<account_id_type> rake_account_id;
|
||||
const asset_object& core_asset_obj = asset_id_type(0)(*this);
|
||||
if (core_asset_obj.dividend_data_id)
|
||||
{
|
||||
const asset_dividend_data_object& core_asset_dividend_data_obj = (*core_asset_obj.dividend_data_id)(*this);
|
||||
rake_account_id = core_asset_dividend_data_obj.dividend_distribution_account;
|
||||
}
|
||||
|
||||
// collect the resolutions of all markets in the BMG: they were previously published and
|
||||
// stored in the individual betting markets
|
||||
std::map<betting_market_id_type, betting_market_resolution_type> resolutions_by_market_id;
|
||||
|
||||
// collecting bettors and their positions
|
||||
std::map<account_id_type, std::vector<const betting_market_position_object*> > bettor_positions_map;
|
||||
|
||||
auto& betting_market_index = get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
||||
// [ROL] it seems to be my mistake - wrong index used
|
||||
//auto& position_index = get_index_type<betting_market_position_index>().indices().get<by_bettor_betting_market>();
|
||||
auto& position_index = get_index_type<betting_market_position_index>().indices().get<by_betting_market_bettor>();
|
||||
auto betting_market_itr = betting_market_index.lower_bound(betting_market_group.id);
|
||||
while (betting_market_itr != betting_market_index.end() && betting_market_itr->group_id == betting_market_group.id)
|
||||
{
|
||||
const betting_market_object& betting_market = *betting_market_itr;
|
||||
FC_ASSERT(betting_market_itr->resolution, "Unexpected error settling betting market ${market_id}: no published resolution",
|
||||
("market_id", betting_market_itr->id));
|
||||
resolutions_by_market_id.emplace(betting_market.id, *betting_market_itr->resolution);
|
||||
|
||||
++betting_market_itr;
|
||||
cancel_all_unmatched_bets_on_betting_market(betting_market);
|
||||
|
||||
auto position_itr = position_index.lower_bound(betting_market.id);
|
||||
|
||||
while (position_itr != position_index.end() && position_itr->betting_market_id == betting_market.id)
|
||||
{
|
||||
const betting_market_position_object& position = *position_itr;
|
||||
++position_itr;
|
||||
|
||||
bettor_positions_map[position.bettor_id].push_back(&position);
|
||||
}
|
||||
}
|
||||
|
||||
// walking through bettors' positions and collecting winings and fees respecting asset_id
|
||||
for (const auto& bettor_positions_pair: bettor_positions_map)
|
||||
{
|
||||
uint16_t rake_fee_percentage = get_global_properties().parameters.betting_rake_fee_percentage;
|
||||
share_type net_profits;
|
||||
share_type payout_amounts;
|
||||
account_id_type bettor_id = bettor_positions_pair.first;
|
||||
const std::vector<const betting_market_position_object*>& bettor_positions = bettor_positions_pair.second;
|
||||
|
||||
for (const betting_market_position_object* position : bettor_positions)
|
||||
{
|
||||
betting_market_resolution_type resolution;
|
||||
try
|
||||
{
|
||||
resolution = resolutions_by_market_id.at(position->betting_market_id);
|
||||
}
|
||||
catch (std::out_of_range&)
|
||||
{
|
||||
FC_THROW_EXCEPTION(fc::key_not_found_exception, "Unexpected betting market ID, shouldn't happen");
|
||||
}
|
||||
|
||||
///if (cancel)
|
||||
/// resolution = betting_market_resolution_type::cancel;
|
||||
///else
|
||||
///{
|
||||
/// // checked in evaluator, should never happen, see above
|
||||
/// assert(resolutions.count(position->betting_market_id));
|
||||
/// resolution = resolutions.at(position->betting_market_id);
|
||||
///}
|
||||
|
||||
|
||||
switch (resolution)
|
||||
{
|
||||
case betting_market_resolution_type::win:
|
||||
{
|
||||
share_type total_payout = position->pay_if_payout_condition + position->pay_if_not_canceled;
|
||||
payout_amounts += total_payout;
|
||||
net_profits += total_payout - position->pay_if_canceled;
|
||||
break;
|
||||
}
|
||||
case betting_market_resolution_type::not_win:
|
||||
{
|
||||
share_type total_payout = position->pay_if_not_payout_condition + position->pay_if_not_canceled;
|
||||
payout_amounts += total_payout;
|
||||
net_profits += total_payout - position->pay_if_canceled;
|
||||
break;
|
||||
}
|
||||
case betting_market_resolution_type::cancel:
|
||||
payout_amounts += position->pay_if_canceled;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
remove(*position);
|
||||
}
|
||||
|
||||
// pay the fees to the dividend-distribution account if net profit
|
||||
share_type rake_amount;
|
||||
if (net_profits.value > 0 && rake_account_id)
|
||||
{
|
||||
rake_amount = ((fc::uint128_t(net_profits.value) * rake_fee_percentage + GRAPHENE_100_PERCENT - 1) / GRAPHENE_100_PERCENT).to_uint64();
|
||||
if (rake_amount.value)
|
||||
adjust_balance(*rake_account_id, asset(rake_amount, betting_market_group.asset_id));
|
||||
}
|
||||
|
||||
// pay winning - rake
|
||||
adjust_balance(bettor_id, asset(payout_amounts - rake_amount, betting_market_group.asset_id));
|
||||
// [ROL]
|
||||
//idump((payout_amounts)(net_profits.value)(rake_amount.value));
|
||||
|
||||
push_applied_operation(betting_market_group_resolved_operation(bettor_id,
|
||||
betting_market_group.id,
|
||||
resolutions_by_market_id,
|
||||
payout_amounts,
|
||||
rake_amount));
|
||||
}
|
||||
|
||||
// At this point, the betting market group will either be in the "graded" or "canceled" state,
|
||||
// if it was graded, mark it as settled. if it's canceled, let it remain canceled.
|
||||
|
||||
bool was_canceled = betting_market_group.get_status() == betting_market_group_status::canceled;
|
||||
|
||||
if (!was_canceled)
|
||||
modify(betting_market_group, [&](betting_market_group_object& group) {
|
||||
group.on_settled_event(*this);
|
||||
});
|
||||
|
||||
betting_market_itr = betting_market_index.lower_bound(betting_market_group.id);
|
||||
while (betting_market_itr != betting_market_index.end() && betting_market_itr->group_id == betting_market_group.id) {
|
||||
const betting_market_object& betting_market = *betting_market_itr;
|
||||
|
||||
++betting_market_itr;
|
||||
dlog("removing betting market ${id}", ("id", betting_market.id));
|
||||
remove(betting_market);
|
||||
}
|
||||
|
||||
const event_object& event = betting_market_group.event_id(*this);
|
||||
|
||||
dlog("removing betting market group ${id}", ("id", betting_market_group.id));
|
||||
remove(betting_market_group);
|
||||
|
||||
if (event.get_status() == event_status::canceled ||
|
||||
event.get_status() == event_status::settled) {
|
||||
dlog("removing event ${id}", ("id", event.id));
|
||||
remove(event);
|
||||
}
|
||||
}
|
||||
|
||||
share_type adjust_betting_position(database& db, account_id_type bettor_id, betting_market_id_type betting_market_id, bet_type back_or_lay, share_type bet_amount, share_type matched_amount)
|
||||
{ try {
|
||||
assert(bet_amount >= 0);
|
||||
|
||||
share_type guaranteed_winnings_returned = 0;
|
||||
|
||||
if (bet_amount == 0)
|
||||
return guaranteed_winnings_returned;
|
||||
|
||||
auto& index = db.get_index_type<betting_market_position_index>().indices().get<by_bettor_betting_market>();
|
||||
auto itr = index.find(boost::make_tuple(bettor_id, betting_market_id));
|
||||
if (itr == index.end())
|
||||
{
|
||||
db.create<betting_market_position_object>([&](betting_market_position_object& position) {
|
||||
position.bettor_id = bettor_id;
|
||||
position.betting_market_id = betting_market_id;
|
||||
position.pay_if_payout_condition = back_or_lay == bet_type::back ? bet_amount + matched_amount : 0;
|
||||
position.pay_if_not_payout_condition = back_or_lay == bet_type::lay ? bet_amount + matched_amount : 0;
|
||||
position.pay_if_canceled = bet_amount;
|
||||
position.pay_if_not_canceled = 0;
|
||||
// this should not be reducible
|
||||
});
|
||||
} else {
|
||||
db.modify(*itr, [&](betting_market_position_object& position) {
|
||||
assert(position.bettor_id == bettor_id);
|
||||
assert(position.betting_market_id == betting_market_id);
|
||||
position.pay_if_payout_condition += back_or_lay == bet_type::back ? bet_amount + matched_amount : 0;
|
||||
position.pay_if_not_payout_condition += back_or_lay == bet_type::lay ? bet_amount + matched_amount : 0;
|
||||
position.pay_if_canceled += bet_amount;
|
||||
|
||||
guaranteed_winnings_returned = position.reduce();
|
||||
});
|
||||
}
|
||||
return guaranteed_winnings_returned;
|
||||
} FC_CAPTURE_AND_RETHROW((bettor_id)(betting_market_id)(bet_amount)) }
|
||||
|
||||
|
||||
// called twice when a bet is matched, once for the taker, once for the maker
|
||||
bool bet_was_matched(database& db, const bet_object& bet,
|
||||
share_type amount_bet, share_type amount_matched,
|
||||
bet_multiplier_type actual_multiplier,
|
||||
bool refund_unmatched_portion)
|
||||
{
|
||||
// record their bet, modifying their position, and return any winnings
|
||||
share_type guaranteed_winnings_returned = adjust_betting_position(db, bet.bettor_id, bet.betting_market_id,
|
||||
bet.back_or_lay, amount_bet, amount_matched);
|
||||
db.adjust_balance(bet.bettor_id, asset(guaranteed_winnings_returned, bet.amount_to_bet.asset_id));
|
||||
|
||||
// generate a virtual "match" op
|
||||
asset asset_amount_bet(amount_bet, bet.amount_to_bet.asset_id);
|
||||
|
||||
bet_matched_operation bet_matched_virtual_op(bet.bettor_id, bet.id,
|
||||
asset_amount_bet,
|
||||
actual_multiplier,
|
||||
guaranteed_winnings_returned);
|
||||
//edump((bet_matched_virtual_op));
|
||||
db.push_applied_operation(std::move(bet_matched_virtual_op));
|
||||
|
||||
// update the bet on the books
|
||||
if (asset_amount_bet == bet.amount_to_bet)
|
||||
{
|
||||
db.remove(bet);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
db.modify(bet, [&](bet_object& bet_obj) {
|
||||
bet_obj.amount_to_bet -= asset_amount_bet;
|
||||
});
|
||||
|
||||
if (refund_unmatched_portion)
|
||||
{
|
||||
db.cancel_bet(bet);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the two orders,
|
||||
*
|
||||
* @return a bit field indicating which orders were filled (and thus removed)
|
||||
*
|
||||
* 0 - no bet was matched (this will never happen)
|
||||
* 1 - taker_bet was filled and removed from the books
|
||||
* 2 - maker_bet was filled and removed from the books
|
||||
* 3 - both were filled and removed from the books
|
||||
*/
|
||||
int match_bet(database& db, const bet_object& taker_bet, const bet_object& maker_bet )
|
||||
{
|
||||
assert(taker_bet.amount_to_bet.asset_id == maker_bet.amount_to_bet.asset_id);
|
||||
assert(taker_bet.amount_to_bet.amount > 0 && maker_bet.amount_to_bet.amount > 0);
|
||||
assert(taker_bet.back_or_lay == bet_type::back ? taker_bet.backer_multiplier <= maker_bet.backer_multiplier : taker_bet.backer_multiplier >= maker_bet.backer_multiplier);
|
||||
assert(taker_bet.back_or_lay != maker_bet.back_or_lay);
|
||||
|
||||
int result = 0;
|
||||
//idump((taker_bet)(maker_bet));
|
||||
|
||||
// using the maker's odds, figure out how much of the maker's bet we would match, rounding down
|
||||
// go ahead and get look up the ratio for the bet (a bet with odds 1.92 will have a ratio 25:23)
|
||||
share_type back_odds_ratio;
|
||||
share_type lay_odds_ratio;
|
||||
std::tie(back_odds_ratio, lay_odds_ratio) = maker_bet.get_ratio();
|
||||
|
||||
// and make some shortcuts to get to the maker's and taker's side of the ratio
|
||||
const share_type& maker_odds_ratio = maker_bet.back_or_lay == bet_type::back ? back_odds_ratio : lay_odds_ratio;
|
||||
const share_type& taker_odds_ratio = maker_bet.back_or_lay == bet_type::back ? lay_odds_ratio : back_odds_ratio;
|
||||
//idump((back_odds_ratio)(lay_odds_ratio));
|
||||
//idump((maker_odds_ratio)(taker_odds_ratio));
|
||||
|
||||
// now figure out how much of the maker bet we'll consume. We don't yet know whether the maker or taker
|
||||
// will be the limiting factor.
|
||||
share_type maximum_taker_factor = taker_bet.amount_to_bet.amount / taker_odds_ratio;
|
||||
share_type maximum_maker_factor = maker_bet.amount_to_bet.amount / maker_odds_ratio;
|
||||
share_type maximum_factor = std::min(maximum_taker_factor, maximum_maker_factor);
|
||||
share_type maker_amount_to_match = maximum_factor * maker_odds_ratio;
|
||||
share_type taker_amount_to_match = maximum_factor * taker_odds_ratio;
|
||||
//idump((maker_amount_to_match)(taker_amount_to_match));
|
||||
|
||||
// TODO: analyze whether maximum_maker_amount_to_match can ever be zero here
|
||||
assert(maker_amount_to_match != 0);
|
||||
if (maker_amount_to_match == 0)
|
||||
return 0;
|
||||
|
||||
#ifndef NDEBUG
|
||||
assert(taker_amount_to_match <= taker_bet.amount_to_bet.amount);
|
||||
assert(taker_amount_to_match / taker_odds_ratio * taker_odds_ratio == taker_amount_to_match);
|
||||
{
|
||||
// verify we're getting the odds we expect
|
||||
fc::uint128_t payout_128 = maker_amount_to_match.value;
|
||||
payout_128 += taker_amount_to_match.value;
|
||||
payout_128 *= GRAPHENE_BETTING_ODDS_PRECISION;
|
||||
payout_128 /= maker_bet.back_or_lay == bet_type::back ? maker_amount_to_match.value : taker_amount_to_match.value;
|
||||
assert(payout_128.to_uint64() == maker_bet.backer_multiplier);
|
||||
}
|
||||
#endif
|
||||
|
||||
//idump((taker_amount_to_match)(maker_amount_to_match));
|
||||
|
||||
// maker bets will always be an exact multiple of maker_odds_ratio, so they will either completely match or remain on the books
|
||||
bool maker_bet_will_completely_match = maker_amount_to_match == maker_bet.amount_to_bet.amount;
|
||||
|
||||
// if the maker bet stays on the books, we need to make sure the taker bet is removed from the books (either it fills completely,
|
||||
// or any un-filled amount is canceled)
|
||||
result |= bet_was_matched(db, taker_bet, taker_amount_to_match, maker_amount_to_match, maker_bet.backer_multiplier, !maker_bet_will_completely_match);
|
||||
result |= bet_was_matched(db, maker_bet, maker_amount_to_match, taker_amount_to_match, maker_bet.backer_multiplier, false) << 1;
|
||||
|
||||
assert(result != 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// called from the bet_place_evaluator
|
||||
bool database::place_bet(const bet_object& new_bet_object)
|
||||
{
|
||||
const auto& bet_odds_idx = get_index_type<bet_object_index>().indices().get<by_odds>();
|
||||
|
||||
bet_type bet_type_to_match = new_bet_object.back_or_lay == bet_type::back ? bet_type::lay : bet_type::back;
|
||||
auto book_itr = bet_odds_idx.lower_bound(std::make_tuple(new_bet_object.betting_market_id, bet_type_to_match));
|
||||
auto book_end = bet_odds_idx.upper_bound(std::make_tuple(new_bet_object.betting_market_id, bet_type_to_match, new_bet_object.backer_multiplier));
|
||||
|
||||
// ilog("");
|
||||
// ilog("------------ order book ------------------");
|
||||
// for (auto itr = book_itr; itr != book_end; ++itr)
|
||||
// idump((*itr));
|
||||
// ilog("------------ order book ------------------");
|
||||
|
||||
int orders_matched_flags = 0;
|
||||
bool finished = false;
|
||||
while (!finished && book_itr != book_end)
|
||||
{
|
||||
auto old_book_itr = book_itr;
|
||||
++book_itr;
|
||||
|
||||
orders_matched_flags = match_bet(*this, new_bet_object, *old_book_itr);
|
||||
|
||||
// we continue if the maker bet was completely consumed AND the taker bet was not
|
||||
finished = orders_matched_flags != 2;
|
||||
}
|
||||
|
||||
if (!(orders_matched_flags & 1))
|
||||
{
|
||||
// if the new (taker) bet was not completely consumed, we need to put whatever remains
|
||||
// of it on the books. But we only allow bets that can be exactly matched
|
||||
// on the books, so round the amount down if necessary
|
||||
share_type minimum_matchable_amount = new_bet_object.get_minimum_matchable_amount();
|
||||
share_type scale_factor = new_bet_object.amount_to_bet.amount / minimum_matchable_amount;
|
||||
share_type rounded_bet_amount = scale_factor * minimum_matchable_amount;
|
||||
//idump((new_bet_object.amount_to_bet.amount)(rounded_bet_amount)(minimum_matchable_amount)(scale_factor));
|
||||
|
||||
if (rounded_bet_amount == share_type())
|
||||
{
|
||||
// the remainder of the bet was too small to match, cancel the bet
|
||||
cancel_bet(new_bet_object, true);
|
||||
return true;
|
||||
}
|
||||
else if (rounded_bet_amount != new_bet_object.amount_to_bet.amount)
|
||||
{
|
||||
asset stake_returned = new_bet_object.amount_to_bet;
|
||||
stake_returned.amount -= rounded_bet_amount;
|
||||
|
||||
modify(new_bet_object, [&rounded_bet_amount](bet_object& modified_bet_object) {
|
||||
modified_bet_object.amount_to_bet.amount = rounded_bet_amount;
|
||||
});
|
||||
|
||||
adjust_balance(new_bet_object.bettor_id, stake_returned);
|
||||
// TODO: update global statistics
|
||||
bet_adjusted_operation bet_adjusted_op(new_bet_object.bettor_id, new_bet_object.id,
|
||||
stake_returned);
|
||||
// idump((bet_adjusted_op)(new_bet_object));
|
||||
push_applied_operation(std::move(bet_adjusted_op));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
@ -29,6 +29,7 @@
|
|||
#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>
|
||||
|
|
@ -36,6 +37,8 @@
|
|||
#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
|
||||
|
|
@ -239,7 +242,7 @@ processed_transaction database::_push_transaction( const signed_transaction& trx
|
|||
auto processed_trx = _apply_transaction( trx );
|
||||
_pending_tx.push_back(processed_trx);
|
||||
|
||||
notify_changed_objects();
|
||||
// notify_changed_objects();
|
||||
// The transaction applied successfully. Merge its changes into the pending block session.
|
||||
temp_session.merge();
|
||||
|
||||
|
|
@ -283,13 +286,13 @@ processed_transaction database::push_proposal(const proposal_object& proposal)
|
|||
{
|
||||
_applied_ops.resize( old_applied_ops_size );
|
||||
}
|
||||
elog( "e", ("e",e.to_detail_string() ) );
|
||||
edump((e));
|
||||
throw;
|
||||
}
|
||||
|
||||
ptrx.operation_results = std::move(eval_state.operation_results);
|
||||
return ptrx;
|
||||
} FC_CAPTURE_AND_RETHROW( (proposal) ) }
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
signed_block database::generate_block(
|
||||
fc::time_point_sec when,
|
||||
|
|
@ -394,6 +397,22 @@ signed_block database::_generate_block(
|
|||
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 );
|
||||
|
||||
|
|
@ -460,6 +479,10 @@ const vector<optional< operation_history_object > >& database::get_applied_opera
|
|||
return _applied_ops;
|
||||
}
|
||||
|
||||
vector<optional< operation_history_object > >& database::get_applied_operations()
|
||||
{
|
||||
return _applied_ops;
|
||||
}
|
||||
//////////////////// private methods ////////////////////
|
||||
|
||||
void database::apply_block( const signed_block& next_block, uint32_t skip )
|
||||
|
|
@ -506,10 +529,12 @@ void database::_apply_block( const signed_block& next_block )
|
|||
* for transactions when validating broadcast transactions or
|
||||
* when building a block.
|
||||
*/
|
||||
apply_transaction( trx, skip | skip_transaction_signatures );
|
||||
apply_transaction( trx, skip );
|
||||
++_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();
|
||||
|
|
@ -519,11 +544,14 @@ void database::_apply_block( const signed_block& next_block )
|
|||
perform_chain_maintenance(next_block, global_props);
|
||||
|
||||
create_block_summary(next_block);
|
||||
place_delayed_bets(); // must happen after update_global_dynamic_data() updates the time
|
||||
clear_expired_transactions();
|
||||
clear_expired_proposals();
|
||||
clear_expired_orders();
|
||||
update_expired_feeds();
|
||||
update_withdraw_permissions();
|
||||
update_tournaments();
|
||||
update_betting_markets(next_block.timestamp);
|
||||
|
||||
// n.b., update_maintenance_flag() happens this late
|
||||
// because get_slot_time() / get_slot_at_time() is needed above
|
||||
|
|
@ -531,7 +559,10 @@ void database::_apply_block( const signed_block& next_block )
|
|||
// update_global_dynamic_data() as perhaps these methods only need
|
||||
// to be called for header validation?
|
||||
update_maintenance_flag( maint_needed );
|
||||
update_witness_schedule();
|
||||
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
|
||||
|
|
@ -540,24 +571,7 @@ void database::_apply_block( const signed_block& next_block )
|
|||
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)
|
||||
{
|
||||
|
|
@ -662,6 +676,9 @@ const witness_object& database::validate_block_header( uint32_t skip, const sign
|
|||
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);
|
||||
//DLN: TODO: Temporarily commented out to test shuffle vs RNG scheduling algorithm for witnesses, this was causing shuffle agorithm to fail during create_witness test. This should be re-enabled for RNG, and maybe for shuffle too, don't really know for sure.
|
||||
// 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 ) );
|
||||
|
|
|
|||
|
|
@ -95,4 +95,107 @@ void database::debug_dump()
|
|||
*/
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ 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
|
||||
const dynamic_global_property_object& database::get_dynamic_global_properties() const
|
||||
{
|
||||
return get( dynamic_global_property_id_type() );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,16 @@
|
|||
#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/sport_object.hpp>
|
||||
#include <graphene/chain/event_group_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/global_betting_statistics_object.hpp>
|
||||
|
||||
#include <graphene/chain/account_evaluator.hpp>
|
||||
#include <graphene/chain/asset_evaluator.hpp>
|
||||
|
|
@ -61,6 +71,11 @@
|
|||
#include <graphene/chain/withdraw_permission_evaluator.hpp>
|
||||
#include <graphene/chain/witness_evaluator.hpp>
|
||||
#include <graphene/chain/worker_evaluator.hpp>
|
||||
#include <graphene/chain/sport_evaluator.hpp>
|
||||
#include <graphene/chain/event_group_evaluator.hpp>
|
||||
#include <graphene/chain/event_evaluator.hpp>
|
||||
#include <graphene/chain/betting_market_evaluator.hpp>
|
||||
#include <graphene/chain/tournament_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
|
||||
|
|
@ -126,6 +141,33 @@ const uint8_t witness_object::type_id;
|
|||
const uint8_t worker_object::space_id;
|
||||
const uint8_t worker_object::type_id;
|
||||
|
||||
const uint8_t sport_object::space_id;
|
||||
const uint8_t sport_object::type_id;
|
||||
|
||||
const uint8_t event_group_object::space_id;
|
||||
const uint8_t event_group_object::type_id;
|
||||
|
||||
const uint8_t event_object::space_id;
|
||||
const uint8_t event_object::type_id;
|
||||
|
||||
const uint8_t betting_market_rules_object::space_id;
|
||||
const uint8_t betting_market_rules_object::type_id;
|
||||
|
||||
const uint8_t betting_market_group_object::space_id;
|
||||
const uint8_t betting_market_group_object::type_id;
|
||||
|
||||
const uint8_t betting_market_object::space_id;
|
||||
const uint8_t betting_market_object::type_id;
|
||||
|
||||
const uint8_t bet_object::space_id;
|
||||
const uint8_t bet_object::type_id;
|
||||
|
||||
const uint8_t betting_market_position_object::space_id;
|
||||
const uint8_t betting_market_position_object::type_id;
|
||||
|
||||
const uint8_t global_betting_statistics_object::space_id;
|
||||
const uint8_t global_betting_statistics_object::type_id;
|
||||
|
||||
|
||||
void database::initialize_evaluators()
|
||||
{
|
||||
|
|
@ -143,6 +185,7 @@ void database::initialize_evaluators()
|
|||
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>();
|
||||
|
|
@ -171,6 +214,27 @@ void database::initialize_evaluators()
|
|||
register_evaluator<transfer_from_blind_evaluator>();
|
||||
register_evaluator<blind_transfer_evaluator>();
|
||||
register_evaluator<asset_claim_fees_evaluator>();
|
||||
register_evaluator<sport_create_evaluator>();
|
||||
register_evaluator<sport_update_evaluator>();
|
||||
register_evaluator<event_group_create_evaluator>();
|
||||
register_evaluator<event_group_update_evaluator>();
|
||||
register_evaluator<event_create_evaluator>();
|
||||
register_evaluator<event_update_evaluator>();
|
||||
register_evaluator<event_update_status_evaluator>();
|
||||
register_evaluator<betting_market_rules_create_evaluator>();
|
||||
register_evaluator<betting_market_rules_update_evaluator>();
|
||||
register_evaluator<betting_market_group_create_evaluator>();
|
||||
register_evaluator<betting_market_group_update_evaluator>();
|
||||
register_evaluator<betting_market_create_evaluator>();
|
||||
register_evaluator<betting_market_update_evaluator>();
|
||||
register_evaluator<bet_place_evaluator>();
|
||||
register_evaluator<bet_cancel_evaluator>();
|
||||
register_evaluator<betting_market_group_resolve_evaluator>();
|
||||
register_evaluator<betting_market_group_cancel_unmatched_bets_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()
|
||||
|
|
@ -199,11 +263,25 @@ void database::initialize_indexes()
|
|||
add_index< primary_index<worker_index> >();
|
||||
add_index< primary_index<balance_index> >();
|
||||
add_index< primary_index<blinded_balance_index> >();
|
||||
add_index< primary_index<sport_object_index > >();
|
||||
add_index< primary_index<event_group_object_index > >();
|
||||
add_index< primary_index<event_object_index > >();
|
||||
add_index< primary_index<betting_market_rules_object_index > >();
|
||||
add_index< primary_index<betting_market_group_object_index > >();
|
||||
add_index< primary_index<betting_market_object_index > >();
|
||||
add_index< primary_index<bet_object_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 >> >();
|
||||
|
|
@ -214,8 +292,13 @@ void database::initialize_indexes()
|
|||
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< betting_market_position_index > >();
|
||||
add_index< primary_index< global_betting_statistics_object_index > >();
|
||||
//add_index< primary_index<pending_dividend_payout_balance_object_index > >();
|
||||
//add_index< primary_index<distributed_dividend_balance_object_index > >();
|
||||
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)
|
||||
|
|
@ -310,7 +393,18 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
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 = "default-dividend-distribution";
|
||||
//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() == GRAPHENE_RAKE_FEE_ACCOUNT_ID);
|
||||
//}).get_id() == TOURNAMENT_RAKE_FEE_ACCOUNT_ID);
|
||||
// Create more special accounts
|
||||
while( true )
|
||||
{
|
||||
|
|
@ -336,6 +430,19 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
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::hours(1);
|
||||
a.options.payout_interval = 7*24*60*60;
|
||||
a.dividend_distribution_account = GRAPHENE_RAKE_FEE_ACCOUNT_ID;
|
||||
//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;
|
||||
|
|
@ -343,15 +450,54 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;
|
||||
a.options.flags = 0;
|
||||
a.options.issuer_permissions = 0;
|
||||
a.issuer = GRAPHENE_NULL_ACCOUNT;
|
||||
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 = GRAPHENE_RAKE_FEE_ACCOUNT_ID;
|
||||
//a.dividend_distribution_account = TOURNAMENT_RAKE_FEE_ACCOUNT_ID;
|
||||
});
|
||||
|
||||
const asset_object& default_asset =
|
||||
create<asset_object>( [&]( asset_object& a ) {
|
||||
a.symbol = "DEF";
|
||||
//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 = GRAPHENE_RAKE_FEE_ACCOUNT_ID;
|
||||
//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 )
|
||||
{
|
||||
|
|
@ -395,6 +541,9 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
p.witness_budget = 0;
|
||||
p.recent_slots_filled = fc::uint128::max_value();
|
||||
});
|
||||
create<global_betting_statistics_object>([&](global_betting_statistics_object& betting_statistics) {
|
||||
betting_statistics.number_of_active_events = 0;
|
||||
});
|
||||
|
||||
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" );
|
||||
|
|
@ -406,6 +555,19 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
} );
|
||||
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 )
|
||||
{
|
||||
|
|
@ -444,6 +606,32 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
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) {
|
||||
|
|
@ -529,6 +717,46 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
});
|
||||
}
|
||||
|
||||
// Create balances for all bts accounts
|
||||
for( const auto& account : genesis_state.initial_bts_accounts ) {
|
||||
if (account.core_balance != share_type()) {
|
||||
total_supplies[asset_id_type()] += account.core_balance;
|
||||
|
||||
create<account_balance_object>([&](account_balance_object& b) {
|
||||
b.owner = get_account_id(account.name);
|
||||
b.balance = account.core_balance;
|
||||
});
|
||||
}
|
||||
|
||||
// create any vesting balances for this account
|
||||
if (account.vesting_balances)
|
||||
for (const auto& vesting_balance : *account.vesting_balances) {
|
||||
create<vesting_balance_object>([&](vesting_balance_object& vbo) {
|
||||
vbo.owner = get_account_id(account.name);
|
||||
vbo.balance = asset(vesting_balance.amount, get_asset_id(vesting_balance.asset_symbol));
|
||||
if (vesting_balance.policy_type == "linear") {
|
||||
auto initial_linear_vesting_policy = vesting_balance.policy.as<genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy>();
|
||||
linear_vesting_policy new_vesting_policy;
|
||||
new_vesting_policy.begin_timestamp = initial_linear_vesting_policy.begin_timestamp;
|
||||
new_vesting_policy.vesting_cliff_seconds = initial_linear_vesting_policy.vesting_cliff_seconds;
|
||||
new_vesting_policy.vesting_duration_seconds = initial_linear_vesting_policy.vesting_duration_seconds;
|
||||
new_vesting_policy.begin_balance = initial_linear_vesting_policy.begin_balance;
|
||||
vbo.policy = new_vesting_policy;
|
||||
} else if (vesting_balance.policy_type == "cdd") {
|
||||
auto initial_cdd_vesting_policy = vesting_balance.policy.as<genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy>();
|
||||
cdd_vesting_policy new_vesting_policy;
|
||||
new_vesting_policy.vesting_seconds = initial_cdd_vesting_policy.vesting_seconds;
|
||||
new_vesting_policy.coin_seconds_earned = initial_cdd_vesting_policy.coin_seconds_earned;
|
||||
new_vesting_policy.start_claim = initial_cdd_vesting_policy.start_claim;
|
||||
new_vesting_policy.coin_seconds_earned_last_update = initial_cdd_vesting_policy.coin_seconds_earned_last_update;
|
||||
vbo.policy = new_vesting_policy;
|
||||
}
|
||||
total_supplies[get_asset_id(vesting_balance.asset_symbol)] += vesting_balance.amount;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create initial balances
|
||||
share_type total_allocation;
|
||||
for( const auto& handout : genesis_state.initial_balances )
|
||||
|
|
@ -552,7 +780,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
|
||||
linear_vesting_policy policy;
|
||||
policy.begin_timestamp = vest.begin_timestamp;
|
||||
policy.vesting_cliff_seconds = 0;
|
||||
policy.vesting_cliff_seconds = vest.vesting_cliff_seconds ? *vest.vesting_cliff_seconds : 0;
|
||||
policy.vesting_duration_seconds = vest.vesting_duration_seconds;
|
||||
policy.begin_balance = vest.begin_balance;
|
||||
|
||||
|
|
@ -570,6 +798,12 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
{
|
||||
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();
|
||||
|
|
@ -589,6 +823,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
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)
|
||||
);
|
||||
|
|
@ -620,6 +855,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
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);
|
||||
|
|
@ -656,17 +892,47 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
}
|
||||
});
|
||||
|
||||
// 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<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 )
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/fba_accumulator_id.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/is_authorized_asset.hpp>
|
||||
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
|
|
@ -44,8 +45,11 @@
|
|||
#include <graphene/chain/vesting_balance_object.hpp>
|
||||
#include <graphene/chain/vote_count.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/witness_schedule_object.hpp>
|
||||
#include <graphene/chain/worker_object.hpp>
|
||||
|
||||
#define USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX // vesting_balance_object by_asset_balance index needed
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
template<class Index>
|
||||
|
|
@ -102,11 +106,12 @@ void database::update_worker_votes()
|
|||
{
|
||||
auto& idx = get_index_type<worker_index>();
|
||||
auto itr = idx.indices().get<by_account>().begin();
|
||||
bool allow_negative_votes = (head_block_time() < HARDFORK_607_TIME);
|
||||
while( itr != idx.indices().get<by_account>().end() )
|
||||
{
|
||||
modify( *itr, [&]( worker_object& obj ){
|
||||
obj.total_votes_for = _vote_tally_buffer[obj.vote_for];
|
||||
obj.total_votes_against = _vote_tally_buffer[obj.vote_against];
|
||||
obj.total_votes_against = allow_negative_votes ? _vote_tally_buffer[obj.vote_against] : 0;
|
||||
});
|
||||
++itr;
|
||||
}
|
||||
|
|
@ -238,6 +243,11 @@ void database::update_active_witnesses()
|
|||
});
|
||||
});
|
||||
|
||||
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
|
||||
modify(wso, [&](witness_schedule_object& _wso)
|
||||
{
|
||||
_wso.scheduler.update(gpo.active_witnesses);
|
||||
});
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::update_active_committee_members()
|
||||
|
|
@ -684,6 +694,531 @@ void create_buyback_orders( database& db )
|
|||
return;
|
||||
}
|
||||
|
||||
void deprecate_annual_members( database& db )
|
||||
{
|
||||
const auto& account_idx = db.get_index_type<account_index>().indices().get<by_id>();
|
||||
fc::time_point_sec now = db.head_block_time();
|
||||
for( const account_object& acct : account_idx )
|
||||
{
|
||||
try
|
||||
{
|
||||
transaction_evaluation_state upgrade_context(&db);
|
||||
upgrade_context.skip_fee_schedule_check = true;
|
||||
|
||||
if( acct.is_annual_member( now ) )
|
||||
{
|
||||
account_upgrade_operation upgrade_vop;
|
||||
upgrade_vop.fee = asset( 0, asset_id_type() );
|
||||
upgrade_vop.account_to_upgrade = acct.id;
|
||||
upgrade_vop.upgrade_to_lifetime_member = true;
|
||||
db.apply_operation( upgrade_context, upgrade_vop );
|
||||
}
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
// we can in fact get here, e.g. if asset issuer of buy/sell asset blacklists/whitelists the buyback account
|
||||
wlog( "Skipping annual member deprecate processing for account ${a} (${an}) at block ${n}; exception was ${e}",
|
||||
("a", acct.id)("an", acct.name)("n", db.head_block_num())("e", e.to_detail_string()) );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Schedules payouts from a dividend distribution account to the current holders of the
|
||||
// dividend-paying asset. This takes any deposits made to the dividend distribution account
|
||||
// since the last time it was called, and distributes them to the current owners of the
|
||||
// dividend-paying asset according to the amount they own.
|
||||
void schedule_pending_dividend_balances(database& db,
|
||||
const asset_object& dividend_holder_asset_obj,
|
||||
const asset_dividend_data_object& dividend_data,
|
||||
const fc::time_point_sec& current_head_block_time,
|
||||
const account_balance_index& balance_index,
|
||||
const vesting_balance_index& vesting_index,
|
||||
const total_distributed_dividend_balance_object_index& distributed_dividend_balance_index,
|
||||
const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index)
|
||||
{
|
||||
dlog("Processing dividend payments for dividend holder asset type ${holder_asset} at time ${t}",
|
||||
("holder_asset", dividend_holder_asset_obj.symbol)("t", db.head_block_time()));
|
||||
auto current_distribution_account_balance_range =
|
||||
balance_index.indices().get<by_account_asset>().equal_range(boost::make_tuple(dividend_data.dividend_distribution_account));
|
||||
auto previous_distribution_account_balance_range =
|
||||
distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().equal_range(boost::make_tuple(dividend_holder_asset_obj.id));
|
||||
// the current range is now all current balances for the distribution account, sorted by asset_type
|
||||
// the previous range is now all previous balances for this account, sorted by asset type
|
||||
|
||||
const auto& gpo = db.get_global_properties();
|
||||
|
||||
// get the list of accounts that hold nonzero balances of the dividend asset
|
||||
auto holder_balances_begin =
|
||||
balance_index.indices().get<by_asset_balance>().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id));
|
||||
auto holder_balances_end =
|
||||
balance_index.indices().get<by_asset_balance>().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, share_type()));
|
||||
uint32_t holder_account_count = std::distance(holder_balances_begin, holder_balances_end);
|
||||
uint64_t distribution_base_fee = gpo.parameters.current_fees->get<asset_dividend_distribution_operation>().distribution_base_fee;
|
||||
uint32_t distribution_fee_per_holder = gpo.parameters.current_fees->get<asset_dividend_distribution_operation>().distribution_fee_per_holder;
|
||||
// the fee, in BTS, for distributing each asset in the account
|
||||
uint64_t total_fee_per_asset_in_core = distribution_base_fee + holder_account_count * (uint64_t)distribution_fee_per_holder;
|
||||
|
||||
std::map<account_id_type, share_type> vesting_amounts;
|
||||
#ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX
|
||||
// get only once a collection of accounts that hold nonzero vesting balances of the dividend asset
|
||||
auto vesting_balances_begin =
|
||||
vesting_index.indices().get<by_asset_balance>().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id));
|
||||
auto vesting_balances_end =
|
||||
vesting_index.indices().get<by_asset_balance>().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, share_type()));
|
||||
for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end))
|
||||
{
|
||||
vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount;
|
||||
dlog("Vesting balance for account: ${owner}, amount: ${amount}",
|
||||
("owner", vesting_balance_obj.owner(db).name)
|
||||
("amount", vesting_balance_obj.balance.amount));
|
||||
}
|
||||
#else
|
||||
// get only once a collection of accounts that hold nonzero vesting balances of the dividend asset
|
||||
const auto& vesting_balances = vesting_index.indices().get<by_id>();
|
||||
for (const vesting_balance_object& vesting_balance_obj : vesting_balances)
|
||||
{
|
||||
if (vesting_balance_obj.balance.asset_id == dividend_holder_asset_obj.id && vesting_balance_obj.balance.amount)
|
||||
{
|
||||
vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount;
|
||||
dlog("Vesting balance for account: ${owner}, amount: ${amount}",
|
||||
("owner", vesting_balance_obj.owner(db).name)
|
||||
("amount", vesting_balance_obj.balance.amount));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
auto current_distribution_account_balance_iter = current_distribution_account_balance_range.first;
|
||||
auto previous_distribution_account_balance_iter = previous_distribution_account_balance_range.first;
|
||||
dlog("Current balances in distribution account: ${current}, Previous balances: ${previous}",
|
||||
("current", std::distance(current_distribution_account_balance_range.first, current_distribution_account_balance_range.second))
|
||||
("previous", std::distance(previous_distribution_account_balance_range.first, previous_distribution_account_balance_range.second)));
|
||||
|
||||
// when we pay out the dividends to the holders, we need to know the total balance of the dividend asset in all
|
||||
// accounts other than the distribution account (it would be silly to distribute dividends back to
|
||||
// the distribution account)
|
||||
share_type total_balance_of_dividend_asset;
|
||||
for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_balances_begin, holder_balances_end))
|
||||
if (holder_balance_object.owner != dividend_data.dividend_distribution_account)
|
||||
{
|
||||
total_balance_of_dividend_asset += holder_balance_object.balance;
|
||||
auto itr = vesting_amounts.find(holder_balance_object.owner);
|
||||
if (itr != vesting_amounts.end())
|
||||
total_balance_of_dividend_asset += itr->second;
|
||||
}
|
||||
// loop through all of the assets currently or previously held in the distribution account
|
||||
while (current_distribution_account_balance_iter != current_distribution_account_balance_range.second ||
|
||||
previous_distribution_account_balance_iter != previous_distribution_account_balance_range.second)
|
||||
{
|
||||
try
|
||||
{
|
||||
// First, figure out how much the balance on this asset has changed since the last sharing out
|
||||
share_type current_balance;
|
||||
share_type previous_balance;
|
||||
asset_id_type payout_asset_type;
|
||||
|
||||
if (previous_distribution_account_balance_iter == previous_distribution_account_balance_range.second ||
|
||||
current_distribution_account_balance_iter->asset_type < previous_distribution_account_balance_iter->dividend_payout_asset_type)
|
||||
{
|
||||
// there are no more previous balances or there is no previous balance for this particular asset type
|
||||
payout_asset_type = current_distribution_account_balance_iter->asset_type;
|
||||
current_balance = current_distribution_account_balance_iter->balance;
|
||||
idump((payout_asset_type)(current_balance));
|
||||
}
|
||||
else if (current_distribution_account_balance_iter == current_distribution_account_balance_range.second ||
|
||||
previous_distribution_account_balance_iter->dividend_payout_asset_type < current_distribution_account_balance_iter->asset_type)
|
||||
{
|
||||
// there are no more current balances or there is no current balance for this particular previous asset type
|
||||
payout_asset_type = previous_distribution_account_balance_iter->dividend_payout_asset_type;
|
||||
previous_balance = previous_distribution_account_balance_iter->balance_at_last_maintenance_interval;
|
||||
idump((payout_asset_type)(previous_balance));
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have both a previous and a current balance for this asset type
|
||||
payout_asset_type = current_distribution_account_balance_iter->asset_type;
|
||||
current_balance = current_distribution_account_balance_iter->balance;
|
||||
previous_balance = previous_distribution_account_balance_iter->balance_at_last_maintenance_interval;
|
||||
idump((payout_asset_type)(current_balance)(previous_balance));
|
||||
}
|
||||
|
||||
share_type delta_balance = current_balance - previous_balance;
|
||||
|
||||
// Next, figure out if we want to share this out -- if the amount added to the distribution
|
||||
// account since last payout is too small, we won't bother.
|
||||
|
||||
share_type total_fee_per_asset_in_payout_asset;
|
||||
const asset_object* payout_asset_object = nullptr;
|
||||
if (payout_asset_type == asset_id_type())
|
||||
{
|
||||
payout_asset_object = &db.get_core_asset();
|
||||
total_fee_per_asset_in_payout_asset = total_fee_per_asset_in_core;
|
||||
dlog("Fee for distributing ${payout_asset_type}: ${fee}",
|
||||
("payout_asset_type", asset_id_type()(db).symbol)
|
||||
("fee", asset(total_fee_per_asset_in_core, asset_id_type())));
|
||||
}
|
||||
else
|
||||
{
|
||||
// figure out what the total fee is in terms of the payout asset
|
||||
const asset_index& asset_object_index = db.get_index_type<asset_index>();
|
||||
auto payout_asset_object_iter = asset_object_index.indices().find(payout_asset_type);
|
||||
FC_ASSERT(payout_asset_object_iter != asset_object_index.indices().end());
|
||||
|
||||
payout_asset_object = &*payout_asset_object_iter;
|
||||
asset total_fee_per_asset = asset(total_fee_per_asset_in_core, asset_id_type()) * payout_asset_object->options.core_exchange_rate;
|
||||
FC_ASSERT(total_fee_per_asset.asset_id == payout_asset_type);
|
||||
|
||||
total_fee_per_asset_in_payout_asset = total_fee_per_asset.amount;
|
||||
dlog("Fee for distributing ${payout_asset_type}: ${fee}",
|
||||
("payout_asset_type", payout_asset_type(db).symbol)("fee", total_fee_per_asset_in_payout_asset));
|
||||
}
|
||||
|
||||
share_type minimum_shares_to_distribute;
|
||||
if (dividend_data.options.minimum_fee_percentage)
|
||||
{
|
||||
fc::uint128_t minimum_amount_to_distribute = total_fee_per_asset_in_payout_asset.value;
|
||||
minimum_amount_to_distribute *= 100 * GRAPHENE_1_PERCENT;
|
||||
minimum_amount_to_distribute /= dividend_data.options.minimum_fee_percentage;
|
||||
wdump((total_fee_per_asset_in_payout_asset)(dividend_data.options));
|
||||
minimum_shares_to_distribute = minimum_amount_to_distribute.to_uint64();
|
||||
}
|
||||
|
||||
dlog("Processing dividend payments of asset type ${payout_asset_type}, delta balance is ${delta_balance}", ("payout_asset_type", payout_asset_type(db).symbol)("delta_balance", delta_balance));
|
||||
if (delta_balance > 0)
|
||||
{
|
||||
if (delta_balance >= minimum_shares_to_distribute)
|
||||
{
|
||||
// first, pay the fee for scheduling these dividend payments
|
||||
if (payout_asset_type == asset_id_type())
|
||||
{
|
||||
// pay fee to network
|
||||
db.modify(asset_dynamic_data_id_type()(db), [total_fee_per_asset_in_core](asset_dynamic_data_object& d) {
|
||||
d.accumulated_fees += total_fee_per_asset_in_core;
|
||||
});
|
||||
db.adjust_balance(dividend_data.dividend_distribution_account,
|
||||
asset(-total_fee_per_asset_in_core, asset_id_type()));
|
||||
delta_balance -= total_fee_per_asset_in_core;
|
||||
}
|
||||
else
|
||||
{
|
||||
const asset_dynamic_data_object& dynamic_data = payout_asset_object->dynamic_data(db);
|
||||
if (dynamic_data.fee_pool < total_fee_per_asset_in_core)
|
||||
FC_THROW("Not distributing dividends for ${holder_asset_type} in asset ${payout_asset_type} "
|
||||
"because insufficient funds in fee pool (need: ${need}, have: ${have})",
|
||||
("holder_asset_type", dividend_holder_asset_obj.symbol)
|
||||
("payout_asset_type", payout_asset_object->symbol)
|
||||
("need", asset(total_fee_per_asset_in_core, asset_id_type()))
|
||||
("have", asset(dynamic_data.fee_pool, payout_asset_type)));
|
||||
// deduct the fee from the dividend distribution account
|
||||
db.adjust_balance(dividend_data.dividend_distribution_account,
|
||||
asset(-total_fee_per_asset_in_payout_asset, payout_asset_type));
|
||||
// convert it to core
|
||||
db.modify(payout_asset_object->dynamic_data(db), [total_fee_per_asset_in_core, total_fee_per_asset_in_payout_asset](asset_dynamic_data_object& d) {
|
||||
d.fee_pool -= total_fee_per_asset_in_core;
|
||||
d.accumulated_fees += total_fee_per_asset_in_payout_asset;
|
||||
});
|
||||
// and pay it to the network
|
||||
db.modify(asset_dynamic_data_id_type()(db), [total_fee_per_asset_in_core](asset_dynamic_data_object& d) {
|
||||
d.accumulated_fees += total_fee_per_asset_in_core;
|
||||
});
|
||||
delta_balance -= total_fee_per_asset_in_payout_asset;
|
||||
}
|
||||
|
||||
dlog("There are ${count} holders of the dividend-paying asset, with a total balance of ${total}",
|
||||
("count", holder_account_count)
|
||||
("total", total_balance_of_dividend_asset));
|
||||
share_type remaining_amount_to_distribute = delta_balance;
|
||||
|
||||
// credit each account with their portion, don't send any back to the dividend distribution account
|
||||
for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_balances_begin, holder_balances_end))
|
||||
{
|
||||
if (holder_balance_object.owner == dividend_data.dividend_distribution_account) continue;
|
||||
|
||||
auto holder_balance = holder_balance_object.balance;
|
||||
|
||||
auto itr = vesting_amounts.find(holder_balance_object.owner);
|
||||
if (itr != vesting_amounts.end())
|
||||
holder_balance += itr->second;
|
||||
|
||||
fc::uint128_t amount_to_credit(delta_balance.value);
|
||||
amount_to_credit *= holder_balance.value;
|
||||
amount_to_credit /= total_balance_of_dividend_asset.value;
|
||||
share_type shares_to_credit((int64_t)amount_to_credit.to_uint64());
|
||||
if (shares_to_credit.value)
|
||||
{
|
||||
wdump((delta_balance.value)(holder_balance)(total_balance_of_dividend_asset));
|
||||
|
||||
remaining_amount_to_distribute -= shares_to_credit;
|
||||
|
||||
dlog("Crediting account ${account} with ${amount}",
|
||||
("account", holder_balance_object.owner(db).name)
|
||||
("amount", asset(shares_to_credit, payout_asset_type)));
|
||||
auto pending_payout_iter =
|
||||
pending_payout_balance_index.indices().get<by_dividend_payout_account>().find(boost::make_tuple(dividend_holder_asset_obj.id, payout_asset_type, holder_balance_object.owner));
|
||||
if (pending_payout_iter == pending_payout_balance_index.indices().get<by_dividend_payout_account>().end())
|
||||
db.create<pending_dividend_payout_balance_for_holder_object>( [&]( pending_dividend_payout_balance_for_holder_object& obj ){
|
||||
obj.owner = holder_balance_object.owner;
|
||||
obj.dividend_holder_asset_type = dividend_holder_asset_obj.id;
|
||||
obj.dividend_payout_asset_type = payout_asset_type;
|
||||
obj.pending_balance = shares_to_credit;
|
||||
});
|
||||
else
|
||||
db.modify(*pending_payout_iter, [&]( pending_dividend_payout_balance_for_holder_object& pending_balance ){
|
||||
pending_balance.pending_balance += shares_to_credit;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& pending_payout : pending_payout_balance_index.indices())
|
||||
if (pending_payout.pending_balance.value)
|
||||
dlog("Pending payout: ${account_name} -> ${amount}",
|
||||
("account_name", pending_payout.owner(db).name)
|
||||
("amount", asset(pending_payout.pending_balance, pending_payout.dividend_payout_asset_type)));
|
||||
dlog("Remaining balance not paid out: ${amount}",
|
||||
("amount", asset(remaining_amount_to_distribute, payout_asset_type)));
|
||||
|
||||
share_type distributed_amount = delta_balance - remaining_amount_to_distribute;
|
||||
if (previous_distribution_account_balance_iter == previous_distribution_account_balance_range.second ||
|
||||
previous_distribution_account_balance_iter->dividend_payout_asset_type != payout_asset_type)
|
||||
db.create<total_distributed_dividend_balance_object>( [&]( total_distributed_dividend_balance_object& obj ){
|
||||
obj.dividend_holder_asset_type = dividend_holder_asset_obj.id;
|
||||
obj.dividend_payout_asset_type = payout_asset_type;
|
||||
obj.balance_at_last_maintenance_interval = distributed_amount;
|
||||
});
|
||||
else
|
||||
db.modify(*previous_distribution_account_balance_iter, [&]( total_distributed_dividend_balance_object& obj ){
|
||||
obj.balance_at_last_maintenance_interval += distributed_amount;
|
||||
});
|
||||
}
|
||||
else
|
||||
FC_THROW("Not distributing dividends for ${holder_asset_type} in asset ${payout_asset_type} "
|
||||
"because amount ${delta_balance} is too small an amount to distribute.",
|
||||
("holder_asset_type", dividend_holder_asset_obj.symbol)
|
||||
("payout_asset_type", payout_asset_object->symbol)
|
||||
("delta_balance", asset(delta_balance, payout_asset_type)));
|
||||
}
|
||||
else if (delta_balance < 0)
|
||||
{
|
||||
// some amount of the asset has been withdrawn from the dividend_distribution_account,
|
||||
// meaning the current pending payout balances will add up to more than our current balance.
|
||||
// This should be extremely rare (caused by an override transfer by the asset owner).
|
||||
// Reduce all pending payouts proportionally
|
||||
share_type total_pending_balances;
|
||||
auto pending_payouts_range =
|
||||
pending_payout_balance_index.indices().get<by_dividend_payout_account>().equal_range(boost::make_tuple(dividend_holder_asset_obj.id, payout_asset_type));
|
||||
|
||||
for (const pending_dividend_payout_balance_for_holder_object& pending_balance_object : boost::make_iterator_range(pending_payouts_range.first, pending_payouts_range.second))
|
||||
total_pending_balances += pending_balance_object.pending_balance;
|
||||
|
||||
share_type remaining_amount_to_recover = -delta_balance;
|
||||
share_type remaining_pending_balances = total_pending_balances;
|
||||
for (const pending_dividend_payout_balance_for_holder_object& pending_balance_object : boost::make_iterator_range(pending_payouts_range.first, pending_payouts_range.second))
|
||||
{
|
||||
fc::uint128_t amount_to_debit(remaining_amount_to_recover.value);
|
||||
amount_to_debit *= pending_balance_object.pending_balance.value;
|
||||
amount_to_debit /= remaining_pending_balances.value;
|
||||
share_type shares_to_debit((int64_t)amount_to_debit.to_uint64());
|
||||
|
||||
remaining_amount_to_recover -= shares_to_debit;
|
||||
remaining_pending_balances -= pending_balance_object.pending_balance;
|
||||
|
||||
db.modify(pending_balance_object, [&]( pending_dividend_payout_balance_for_holder_object& pending_balance ){
|
||||
pending_balance.pending_balance -= shares_to_debit;
|
||||
});
|
||||
}
|
||||
|
||||
// if we're here, we know there must be a previous balance, so just adjust it by the
|
||||
// amount we just reclaimed
|
||||
db.modify(*previous_distribution_account_balance_iter, [&]( total_distributed_dividend_balance_object& obj ){
|
||||
obj.balance_at_last_maintenance_interval += delta_balance;
|
||||
assert(obj.balance_at_last_maintenance_interval == current_balance);
|
||||
});
|
||||
} // end if deposit was large enough to distribute
|
||||
}
|
||||
catch (const fc::exception& e)
|
||||
{
|
||||
dlog("${e}", ("e", e));
|
||||
}
|
||||
|
||||
// iterate
|
||||
if (previous_distribution_account_balance_iter == previous_distribution_account_balance_range.second ||
|
||||
current_distribution_account_balance_iter->asset_type < previous_distribution_account_balance_iter->dividend_payout_asset_type)
|
||||
++current_distribution_account_balance_iter;
|
||||
else if (current_distribution_account_balance_iter == current_distribution_account_balance_range.second ||
|
||||
previous_distribution_account_balance_iter->dividend_payout_asset_type < current_distribution_account_balance_iter->asset_type)
|
||||
++previous_distribution_account_balance_iter;
|
||||
else
|
||||
{
|
||||
++current_distribution_account_balance_iter;
|
||||
++previous_distribution_account_balance_iter;
|
||||
}
|
||||
}
|
||||
db.modify(dividend_data, [current_head_block_time](asset_dividend_data_object& dividend_data_obj) {
|
||||
dividend_data_obj.last_scheduled_distribution_time = current_head_block_time;
|
||||
dividend_data_obj.last_distribution_time = current_head_block_time;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void process_dividend_assets(database& db)
|
||||
{
|
||||
ilog("In process_dividend_assets time ${time}", ("time", db.head_block_time()));
|
||||
|
||||
const account_balance_index& balance_index = db.get_index_type<account_balance_index>();
|
||||
const vesting_balance_index& vbalance_index = db.get_index_type<vesting_balance_index>();
|
||||
const total_distributed_dividend_balance_object_index& distributed_dividend_balance_index = db.get_index_type<total_distributed_dividend_balance_object_index>();
|
||||
const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index = db.get_index_type<pending_dividend_payout_balance_for_holder_object_index>();
|
||||
|
||||
// TODO: switch to iterating over only dividend assets (generalize the by_type index)
|
||||
for( const asset_object& dividend_holder_asset_obj : db.get_index_type<asset_index>().indices() )
|
||||
if (dividend_holder_asset_obj.dividend_data_id)
|
||||
{
|
||||
const asset_dividend_data_object& dividend_data = dividend_holder_asset_obj.dividend_data(db);
|
||||
const account_object& dividend_distribution_account_object = dividend_data.dividend_distribution_account(db);
|
||||
|
||||
fc::time_point_sec current_head_block_time = db.head_block_time();
|
||||
|
||||
schedule_pending_dividend_balances(db, dividend_holder_asset_obj, dividend_data, current_head_block_time,
|
||||
balance_index, vbalance_index, distributed_dividend_balance_index, pending_payout_balance_index);
|
||||
if (dividend_data.options.next_payout_time &&
|
||||
db.head_block_time() >= *dividend_data.options.next_payout_time)
|
||||
{
|
||||
dlog("Dividend payout time has arrived for asset ${holder_asset}",
|
||||
("holder_asset", dividend_holder_asset_obj.symbol));
|
||||
|
||||
#ifndef NDEBUG
|
||||
// dump balances before the payouts for debugging
|
||||
const auto& balance_idx = db.get_index_type<account_balance_index>().indices().get<by_account_asset>();
|
||||
auto holder_account_balance_range = balance_idx.equal_range(boost::make_tuple(dividend_data.dividend_distribution_account));
|
||||
for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_account_balance_range.first, holder_account_balance_range.second))
|
||||
ilog(" Current balance: ${asset}", ("asset", asset(holder_balance_object.balance, holder_balance_object.asset_type)));
|
||||
#endif
|
||||
|
||||
// when we do the payouts, we first increase the balances in all of the receiving accounts
|
||||
// and use this map to keep track of the total amount of each asset paid out.
|
||||
// Afterwards, we decrease the distribution account's balance by the total amount paid out,
|
||||
// and modify the distributed_balances accordingly
|
||||
std::map<asset_id_type, share_type> amounts_paid_out_by_asset;
|
||||
|
||||
auto pending_payouts_range =
|
||||
pending_payout_balance_index.indices().get<by_dividend_account_payout>().equal_range(boost::make_tuple(dividend_holder_asset_obj.id));
|
||||
// the pending_payouts_range is all payouts for this dividend asset, sorted by the holder's account
|
||||
// we iterate in this order so we can build up a list of payouts for each account to put in the
|
||||
// virtual op
|
||||
flat_set<asset> payouts_for_this_holder;
|
||||
fc::optional<account_id_type> last_holder_account_id;
|
||||
|
||||
// cache the assets the distribution account is approved to send, we will be asking
|
||||
// for these often
|
||||
flat_map<asset_id_type, bool> approved_assets; // assets that the dividend distribution account is authorized to send/receive
|
||||
auto is_asset_approved_for_distribution_account = [&](const asset_id_type& asset_id) {
|
||||
auto approved_assets_iter = approved_assets.find(asset_id);
|
||||
if (approved_assets_iter != approved_assets.end())
|
||||
return approved_assets_iter->second;
|
||||
bool is_approved = is_authorized_asset(db, dividend_distribution_account_object,
|
||||
asset_id(db));
|
||||
approved_assets[asset_id] = is_approved;
|
||||
return is_approved;
|
||||
};
|
||||
|
||||
for (auto pending_balance_object_iter = pending_payouts_range.first; pending_balance_object_iter != pending_payouts_range.second; )
|
||||
{
|
||||
const pending_dividend_payout_balance_for_holder_object& pending_balance_object = *pending_balance_object_iter;
|
||||
|
||||
if (last_holder_account_id && *last_holder_account_id != pending_balance_object.owner && payouts_for_this_holder.size())
|
||||
{
|
||||
// we've moved on to a new account, generate the dividend payment virtual op for the previous one
|
||||
db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id,
|
||||
*last_holder_account_id,
|
||||
payouts_for_this_holder));
|
||||
dlog("Just pushed virtual op for payout to ${account}", ("account", (*last_holder_account_id)(db).name));
|
||||
payouts_for_this_holder.clear();
|
||||
last_holder_account_id.reset();
|
||||
}
|
||||
|
||||
|
||||
if (pending_balance_object.pending_balance.value &&
|
||||
is_authorized_asset(db, pending_balance_object.owner(db), pending_balance_object.dividend_payout_asset_type(db)) &&
|
||||
is_asset_approved_for_distribution_account(pending_balance_object.dividend_payout_asset_type))
|
||||
{
|
||||
dlog("Processing payout of ${asset} to account ${account}",
|
||||
("asset", asset(pending_balance_object.pending_balance, pending_balance_object.dividend_payout_asset_type))
|
||||
("account", pending_balance_object.owner(db).name));
|
||||
|
||||
db.adjust_balance(pending_balance_object.owner,
|
||||
asset(pending_balance_object.pending_balance,
|
||||
pending_balance_object.dividend_payout_asset_type));
|
||||
payouts_for_this_holder.insert(asset(pending_balance_object.pending_balance,
|
||||
pending_balance_object.dividend_payout_asset_type));
|
||||
last_holder_account_id = pending_balance_object.owner;
|
||||
amounts_paid_out_by_asset[pending_balance_object.dividend_payout_asset_type] += pending_balance_object.pending_balance;
|
||||
|
||||
db.modify(pending_balance_object, [&]( pending_dividend_payout_balance_for_holder_object& pending_balance ){
|
||||
pending_balance.pending_balance = 0;
|
||||
});
|
||||
}
|
||||
|
||||
++pending_balance_object_iter;
|
||||
}
|
||||
// we will always be left with the last holder's data, generate the virtual op for it now.
|
||||
if (last_holder_account_id && payouts_for_this_holder.size())
|
||||
{
|
||||
// we've moved on to a new account, generate the dividend payment virtual op for the previous one
|
||||
db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id,
|
||||
*last_holder_account_id,
|
||||
payouts_for_this_holder));
|
||||
dlog("Just pushed virtual op for payout to ${account}", ("account", (*last_holder_account_id)(db).name));
|
||||
}
|
||||
|
||||
// now debit the total amount of dividends paid out from the distribution account
|
||||
// and reduce the distributed_balances accordingly
|
||||
|
||||
for (const auto& value : amounts_paid_out_by_asset)
|
||||
{
|
||||
const asset_id_type& asset_paid_out = value.first;
|
||||
const share_type& amount_paid_out = value.second;
|
||||
|
||||
db.adjust_balance(dividend_data.dividend_distribution_account,
|
||||
asset(-amount_paid_out,
|
||||
asset_paid_out));
|
||||
auto distributed_balance_iter =
|
||||
distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().find(boost::make_tuple(dividend_holder_asset_obj.id,
|
||||
asset_paid_out));
|
||||
assert(distributed_balance_iter != distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().end());
|
||||
if (distributed_balance_iter != distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().end())
|
||||
db.modify(*distributed_balance_iter, [&]( total_distributed_dividend_balance_object& obj ){
|
||||
obj.balance_at_last_maintenance_interval -= amount_paid_out; // now they've been paid out, reset to zero
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// now schedule the next payout time
|
||||
db.modify(dividend_data, [current_head_block_time](asset_dividend_data_object& dividend_data_obj) {
|
||||
dividend_data_obj.last_scheduled_payout_time = dividend_data_obj.options.next_payout_time;
|
||||
dividend_data_obj.last_payout_time = current_head_block_time;
|
||||
fc::optional<fc::time_point_sec> next_payout_time;
|
||||
if (dividend_data_obj.options.payout_interval)
|
||||
{
|
||||
// if there was a previous payout, make our next payment one interval
|
||||
uint32_t current_time_sec = current_head_block_time.sec_since_epoch();
|
||||
fc::time_point_sec reference_time = *dividend_data_obj.last_scheduled_payout_time;
|
||||
uint32_t next_possible_time_sec = dividend_data_obj.last_scheduled_payout_time->sec_since_epoch();
|
||||
do
|
||||
next_possible_time_sec += *dividend_data_obj.options.payout_interval;
|
||||
while (next_possible_time_sec <= current_time_sec);
|
||||
|
||||
next_payout_time = next_possible_time_sec;
|
||||
}
|
||||
dividend_data_obj.options.next_payout_time = next_payout_time;
|
||||
idump((dividend_data_obj.last_scheduled_payout_time)
|
||||
(dividend_data_obj.last_payout_time)
|
||||
(dividend_data_obj.options.next_payout_time));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)
|
||||
{
|
||||
const auto& gpo = get_global_properties();
|
||||
|
|
@ -691,9 +1226,12 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
distribute_fba_balances(*this);
|
||||
create_buyback_orders(*this);
|
||||
|
||||
process_dividend_assets(*this);
|
||||
|
||||
struct vote_tally_helper {
|
||||
database& d;
|
||||
const global_property_object& props;
|
||||
std::map<account_id_type, share_type> vesting_amounts;
|
||||
|
||||
vote_tally_helper(database& d, const global_property_object& gpo)
|
||||
: d(d), props(gpo)
|
||||
|
|
@ -702,6 +1240,33 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
d._witness_count_histogram_buffer.resize(props.parameters.maximum_witness_count / 2 + 1);
|
||||
d._committee_count_histogram_buffer.resize(props.parameters.maximum_committee_count / 2 + 1);
|
||||
d._total_voting_stake = 0;
|
||||
|
||||
const vesting_balance_index& vesting_index = d.get_index_type<vesting_balance_index>();
|
||||
#ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX
|
||||
auto vesting_balances_begin =
|
||||
vesting_index.indices().get<by_asset_balance>().lower_bound(boost::make_tuple(asset_id_type()));
|
||||
auto vesting_balances_end =
|
||||
vesting_index.indices().get<by_asset_balance>().upper_bound(boost::make_tuple(asset_id_type(), share_type()));
|
||||
for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end))
|
||||
{
|
||||
vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount;
|
||||
dlog("Vesting balance for account: ${owner}, amount: ${amount}",
|
||||
("owner", vesting_balance_obj.owner(d).name)
|
||||
("amount", vesting_balance_obj.balance.amount));
|
||||
}
|
||||
#else
|
||||
const auto& vesting_balances = vesting_index.indices().get<by_id>();
|
||||
for (const vesting_balance_object& vesting_balance_obj : vesting_balances)
|
||||
{
|
||||
if (vesting_balance_obj.balance.asset_id == asset_id_type() && vesting_balance_obj.balance.amount)
|
||||
{
|
||||
vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount;
|
||||
dlog("Vesting balance for account: ${owner}, amount: ${amount}",
|
||||
("owner", vesting_balance_obj.owner(d).name)
|
||||
("amount", vesting_balance_obj.balance.amount));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void operator()(const account_object& stake_account) {
|
||||
|
|
@ -720,6 +1285,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
+ (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value: 0)
|
||||
+ d.get_balance(stake_account.get_id(), asset_id_type()).amount.value;
|
||||
|
||||
auto itr = vesting_amounts.find(stake_account.id);
|
||||
if (itr != vesting_amounts.end())
|
||||
voting_stake += itr->second.value;
|
||||
for( vote_id_type id : opinion_account.options.votes )
|
||||
{
|
||||
uint32_t offset = id.instance();
|
||||
|
|
@ -830,14 +1398,20 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
}
|
||||
}
|
||||
|
||||
modify(get_dynamic_global_properties(), [next_maintenance_time](dynamic_global_property_object& d) {
|
||||
const dynamic_global_property_object& dgpo = get_dynamic_global_properties();
|
||||
|
||||
if( (dgpo.next_maintenance_time < HARDFORK_613_TIME) && (next_maintenance_time >= HARDFORK_613_TIME) )
|
||||
deprecate_annual_members(*this);
|
||||
|
||||
modify(dgpo, [next_maintenance_time](dynamic_global_property_object& d) {
|
||||
d.next_maintenance_time = next_maintenance_time;
|
||||
d.accounts_registered_this_interval = 0;
|
||||
});
|
||||
|
||||
// Reset all BitAsset force settlement volumes to zero
|
||||
for( const asset_bitasset_data_object* d : get_index_type<asset_bitasset_data_index>() )
|
||||
modify(*d, [](asset_bitasset_data_object& d) { d.force_settled_volume = 0; });
|
||||
//for( const asset_bitasset_data_object* d : get_index_type<asset_bitasset_data_index>() )
|
||||
for( const auto& d : get_index_type<asset_bitasset_data_index>().indices() )
|
||||
modify( d, [](asset_bitasset_data_object& o) { o.force_settled_volume = 0; });
|
||||
|
||||
// process_budget needs to run at the bottom because
|
||||
// it needs to know the next_maintenance_time
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@
|
|||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
database::database()
|
||||
database::database() :
|
||||
_random_number_generator(fc::ripemd160().data())
|
||||
{
|
||||
initialize_indexes();
|
||||
initialize_evaluators();
|
||||
|
|
@ -63,10 +64,16 @@ void database::reindex(fc::path data_dir, const genesis_state_type& initial_allo
|
|||
const auto last_block_num = last_block->block_num();
|
||||
|
||||
ilog( "Replaying blocks..." );
|
||||
_undo_db.disable();
|
||||
// Right now, we leave undo_db enabled when replaying when the bookie plugin is
|
||||
// enabled. It depends on new/changed/removed object notifications, and those are
|
||||
// only fired when the undo_db is enabled
|
||||
if (!_slow_replays)
|
||||
_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";
|
||||
if( i == 1 ||
|
||||
i % 10000 == 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() )
|
||||
{
|
||||
|
|
@ -87,14 +94,24 @@ void database::reindex(fc::path data_dir, const genesis_state_type& initial_allo
|
|||
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);
|
||||
if (_slow_replays)
|
||||
push_block(*block, skip_fork_db |
|
||||
skip_witness_signature |
|
||||
skip_transaction_signatures |
|
||||
skip_transaction_dupe_check |
|
||||
skip_tapos_check |
|
||||
skip_witness_schedule_check |
|
||||
skip_authority_check);
|
||||
else
|
||||
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();
|
||||
if (!_slow_replays)
|
||||
_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) ) }
|
||||
|
|
@ -165,8 +182,9 @@ void database::close(bool rewind)
|
|||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
catch ( const fc::exception& e )
|
||||
{
|
||||
wlog( "Database close unexpected exception: ${e}", ("e", e) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -184,4 +202,10 @@ void database::close(bool rewind)
|
|||
_fork_db.reset();
|
||||
}
|
||||
|
||||
void database::force_slow_replays()
|
||||
{
|
||||
ilog("enabling slow replays");
|
||||
_slow_replays = true;
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ bool maybe_cull_small_order( database& db, const limit_order_object& order )
|
|||
*/
|
||||
if( order.amount_to_receive().amount == 0 )
|
||||
{
|
||||
ilog( "applied epsilon logic" );
|
||||
//ilog( "applied epsilon logic" );
|
||||
db.cancel_order(order);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
444
libraries/chain/db_notify.cpp
Normal file
444
libraries/chain/db_notify.cpp
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
#include <fc/container/flat.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/authority.hpp>
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/chain/protocol/transaction.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/withdraw_permission_object.hpp>
|
||||
#include <graphene/chain/worker_object.hpp>
|
||||
#include <graphene/chain/confidential_object.hpp>
|
||||
#include <graphene/chain/market_object.hpp>
|
||||
#include <graphene/chain/committee_member_object.hpp>
|
||||
|
||||
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 sport_create_operation&){}
|
||||
void operator()(const sport_update_operation&){}
|
||||
void operator()(const event_group_create_operation&){}
|
||||
void operator()(const event_group_update_operation& op ) {}
|
||||
void operator()(const event_create_operation&){}
|
||||
void operator()(const event_update_operation& op ) {}
|
||||
void operator()(const event_update_status_operation& op ) {}
|
||||
void operator()(const betting_market_rules_create_operation&){}
|
||||
void operator()(const betting_market_rules_update_operation& op ) {}
|
||||
void operator()(const betting_market_group_create_operation&){}
|
||||
void operator()(const betting_market_group_update_operation& op ) {}
|
||||
void operator()(const betting_market_create_operation&){}
|
||||
void operator()(const betting_market_update_operation&){}
|
||||
void operator()(const bet_place_operation&){}
|
||||
void operator()(const betting_market_group_resolve_operation&){}
|
||||
void operator()(const betting_market_group_resolved_operation &){}
|
||||
void operator()(const betting_market_group_cancel_unmatched_bets_operation&){}
|
||||
void operator()(const bet_matched_operation &){}
|
||||
void operator()(const bet_cancel_operation&){}
|
||||
void operator()(const bet_canceled_operation &){}
|
||||
void operator()(const bet_adjusted_operation &){}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accounts )
|
||||
{
|
||||
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;
|
||||
case account_object_type:{
|
||||
accounts.insert( obj->id );
|
||||
break;
|
||||
} case asset_object_type:{
|
||||
const auto& aobj = dynamic_cast<const asset_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->issuer );
|
||||
break;
|
||||
} case force_settlement_object_type:{
|
||||
const auto& aobj = dynamic_cast<const force_settlement_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->owner );
|
||||
break;
|
||||
} case committee_member_object_type:{
|
||||
const auto& aobj = dynamic_cast<const committee_member_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->committee_member_account );
|
||||
break;
|
||||
} case witness_object_type:{
|
||||
const auto& aobj = dynamic_cast<const witness_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->witness_account );
|
||||
break;
|
||||
} case limit_order_object_type:{
|
||||
const auto& aobj = dynamic_cast<const limit_order_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->seller );
|
||||
break;
|
||||
} case call_order_object_type:{
|
||||
const auto& aobj = dynamic_cast<const call_order_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->borrower );
|
||||
break;
|
||||
} case custom_object_type:{
|
||||
break;
|
||||
} case proposal_object_type:{
|
||||
const auto& aobj = dynamic_cast<const proposal_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
transaction_get_impacted_accounts( aobj->proposed_transaction, accounts );
|
||||
break;
|
||||
} case operation_history_object_type:{
|
||||
const auto& aobj = dynamic_cast<const operation_history_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
operation_get_impacted_accounts( aobj->op, accounts );
|
||||
break;
|
||||
} case withdraw_permission_object_type:{
|
||||
const auto& aobj = dynamic_cast<const withdraw_permission_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->withdraw_from_account );
|
||||
accounts.insert( aobj->authorized_account );
|
||||
break;
|
||||
} case vesting_balance_object_type:{
|
||||
const auto& aobj = dynamic_cast<const vesting_balance_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->owner );
|
||||
break;
|
||||
} case worker_object_type:{
|
||||
const auto& aobj = dynamic_cast<const worker_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->worker_account );
|
||||
break;
|
||||
} case balance_object_type:{
|
||||
/** these are free from any accounts */
|
||||
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 );
|
||||
accounts.insert( aobj->owner );
|
||||
break;
|
||||
} case impl_account_statistics_object_type:{
|
||||
const auto& aobj = dynamic_cast<const account_statistics_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->owner );
|
||||
break;
|
||||
} case impl_transaction_object_type:{
|
||||
const auto& aobj = dynamic_cast<const transaction_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
transaction_get_impacted_accounts( aobj->trx, accounts );
|
||||
break;
|
||||
} case impl_blinded_balance_object_type:{
|
||||
const auto& aobj = dynamic_cast<const blinded_balance_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
for( const auto& a : aobj->owner.account_auths )
|
||||
accounts.insert( 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;
|
||||
}
|
||||
}
|
||||
} // end get_relevant_accounts( const object* obj, flat_set<account_id_type>& accounts )
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void database::notify_changed_objects()
|
||||
{ try {
|
||||
if( _undo_db.enabled() )
|
||||
{
|
||||
const auto& head_undo = _undo_db.head();
|
||||
|
||||
// New
|
||||
if( !new_objects.empty() )
|
||||
{
|
||||
vector<object_id_type> new_ids; new_ids.reserve(head_undo.new_ids.size());
|
||||
flat_set<account_id_type> new_accounts_impacted;
|
||||
for( const auto& item : head_undo.new_ids )
|
||||
{
|
||||
new_ids.push_back(item);
|
||||
auto obj = find_object(item);
|
||||
if(obj != nullptr)
|
||||
get_relevant_accounts(obj, new_accounts_impacted);
|
||||
}
|
||||
|
||||
new_objects(new_ids, new_accounts_impacted);
|
||||
}
|
||||
|
||||
// Changed
|
||||
if( !changed_objects.empty() )
|
||||
{
|
||||
vector<object_id_type> changed_ids; changed_ids.reserve(head_undo.old_values.size());
|
||||
flat_set<account_id_type> changed_accounts_impacted;
|
||||
for( const auto& item : head_undo.old_values )
|
||||
{
|
||||
changed_ids.push_back(item.first);
|
||||
get_relevant_accounts(item.second.get(), changed_accounts_impacted);
|
||||
}
|
||||
|
||||
changed_objects(changed_ids, changed_accounts_impacted);
|
||||
}
|
||||
|
||||
// Removed
|
||||
if( !removed_objects.empty() )
|
||||
{
|
||||
vector<object_id_type> removed_ids; removed_ids.reserve( head_undo.removed.size() );
|
||||
vector<const object*> removed; removed.reserve( head_undo.removed.size() );
|
||||
flat_set<account_id_type> removed_accounts_impacted;
|
||||
for( const auto& item : head_undo.removed )
|
||||
{
|
||||
removed_ids.emplace_back( item.first );
|
||||
auto obj = item.second.get();
|
||||
removed.emplace_back( obj );
|
||||
get_relevant_accounts(obj, removed_accounts_impacted);
|
||||
}
|
||||
|
||||
removed_objects(removed_ids, removed, removed_accounts_impacted);
|
||||
}
|
||||
}
|
||||
} FC_CAPTURE_AND_LOG( (0) ) }
|
||||
|
||||
} }
|
||||
|
|
@ -27,11 +27,15 @@
|
|||
|
||||
#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/betting_market_object.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
|
||||
|
|
@ -41,32 +45,51 @@ 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 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 );
|
||||
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) );
|
||||
*/
|
||||
#endif
|
||||
// bad if-condition, this code needs to execute for both shuffled and rng algorithms
|
||||
// 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++;
|
||||
});
|
||||
}
|
||||
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() )
|
||||
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;
|
||||
|
|
@ -117,6 +140,8 @@ void database::update_signing_witness(const witness_object& signing_witness, con
|
|||
{
|
||||
_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;
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
@ -154,15 +179,60 @@ void database::update_last_irreversible_block()
|
|||
} );
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
while( (!dedupe_index.empty()) && (head_block_time() > dedupe_index.begin()->trx.expiration) )
|
||||
transaction_idx.remove(*dedupe_index.begin());
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::place_delayed_bets()
|
||||
{ try {
|
||||
// If any bets have been placed during live betting where bets are delayed for a few seconds, see if there are
|
||||
// any bets whose delays have expired.
|
||||
|
||||
// Delayed bets are sorted to the beginning of the order book, so if there are any bets that need placing,
|
||||
// they're right at the front of the book
|
||||
const auto& bet_odds_idx = get_index_type<bet_object_index>().indices().get<by_odds>();
|
||||
auto iter = bet_odds_idx.begin();
|
||||
|
||||
// we use an awkward looping mechanism here because there's a case where we are processing the
|
||||
// last delayed bet before the "real" order book starts and `iter` was pointing at the first
|
||||
// real order. The place_bet() call can cause the that real order to be deleted, so we need
|
||||
// to decide whether this is the last delayed bet before `place_bet` is called.
|
||||
bool last = iter == bet_odds_idx.end() ||
|
||||
!iter->end_of_delay ||
|
||||
*iter->end_of_delay > head_block_time();
|
||||
while (!last)
|
||||
{
|
||||
const bet_object& bet_to_place = *iter;
|
||||
++iter;
|
||||
|
||||
last = iter == bet_odds_idx.end() ||
|
||||
!iter->end_of_delay ||
|
||||
*iter->end_of_delay > head_block_time();
|
||||
|
||||
// it's possible that the betting market was active when the bet was placed,
|
||||
// but has been frozen before the delay expired. If that's the case here,
|
||||
// don't try to match the bet.
|
||||
// Since this check happens every block, this could impact performance if a
|
||||
// market with many delayed bets is frozen for a long time.
|
||||
// Our current understanding is that the witnesses will typically cancel all unmatched
|
||||
// bets on frozen markets to avoid this.
|
||||
const betting_market_object& betting_market = bet_to_place.betting_market_id(*this);
|
||||
if (betting_market.get_status() == betting_market_status::unresolved)
|
||||
{
|
||||
modify(bet_to_place, [](bet_object& bet_obj) {
|
||||
// clear the end_of_delay, which will re-sort the bet into its place in the book
|
||||
bet_obj.end_of_delay.reset();
|
||||
});
|
||||
|
||||
place_bet(bet_to_place);
|
||||
}
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::clear_expired_proposals()
|
||||
|
|
@ -434,7 +504,12 @@ void database::update_expired_feeds()
|
|||
assert( a.is_market_issued() );
|
||||
|
||||
const asset_bitasset_data_object& b = a.bitasset_data(*this);
|
||||
if( b.feed_is_expired(head_block_time()) )
|
||||
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());
|
||||
|
|
@ -468,4 +543,140 @@ void database::update_withdraw_permissions()
|
|||
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);
|
||||
}
|
||||
|
||||
void process_settled_betting_markets(database& db, fc::time_point_sec current_block_time)
|
||||
{
|
||||
// after a betting market is graded, it goes through a delay period in which it
|
||||
// can be flagged for re-grading. If it isn't flagged during this interval,
|
||||
// it is automatically settled (paid). Process these now.
|
||||
const auto& betting_market_group_index = db.get_index_type<betting_market_group_object_index>().indices().get<by_settling_time>();
|
||||
|
||||
// this index will be sorted with all bmgs with no settling time set first, followed by
|
||||
// ones with the settling time set by increasing time. Start at the first bmg with a time set
|
||||
auto betting_market_group_iter = betting_market_group_index.upper_bound(fc::optional<fc::time_point_sec>());
|
||||
while (betting_market_group_iter != betting_market_group_index.end() &&
|
||||
*betting_market_group_iter->settling_time <= current_block_time)
|
||||
{
|
||||
auto next_iter = std::next(betting_market_group_iter);
|
||||
db.settle_betting_market_group(*betting_market_group_iter);
|
||||
betting_market_group_iter = next_iter;
|
||||
}
|
||||
}
|
||||
|
||||
void database::update_betting_markets(fc::time_point_sec current_block_time)
|
||||
{
|
||||
process_settled_betting_markets(*this, current_block_time);
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -33,10 +33,43 @@ using boost::container::flat_set;
|
|||
|
||||
witness_id_type database::get_scheduled_witness( uint32_t slot_num )const
|
||||
{
|
||||
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() ];
|
||||
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
|
||||
|
|
@ -72,17 +105,12 @@ fc::time_point_sec database::get_slot_time(uint32_t slot_num)const
|
|||
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;
|
||||
}
|
||||
|
||||
uint32_t database::witness_participation_rate()const
|
||||
{
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
return uint64_t(GRAPHENE_100_PERCENT) * dpo.recent_slots_filled.popcount() / 128;
|
||||
}
|
||||
|
||||
void database::update_witness_schedule()
|
||||
{
|
||||
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
|
||||
|
|
@ -118,4 +146,100 @@ void database::update_witness_schedule()
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -128,4 +128,18 @@ database& generic_evaluator::db()const { return trx_state->db(); }
|
|||
db().adjust_balance(fee_payer, fee_from_account);
|
||||
}
|
||||
|
||||
object_id_type generic_evaluator::get_relative_id( object_id_type rel_id )const
|
||||
{
|
||||
if (!is_relative(rel_id))
|
||||
FC_THROW("get_relative_id() called for non-relative id ${id}", ("id", rel_id));
|
||||
if (rel_id.instance() >= trx_state->operation_results.size())
|
||||
FC_THROW("get_relative_id() asked for id of operation ${op_num} (zero-based), but we only have ${count} operations",
|
||||
("op_num", rel_id.instance())("count", trx_state->operation_results.size()));
|
||||
if (trx_state->operation_results[rel_id.instance()].which() != operation_result::tag<object_id_type>::value)
|
||||
FC_THROW("get_relative_id() asked for the result of operation ${op_num}, but that operation did not return an object_id",
|
||||
("op_num", rel_id.instance()));
|
||||
return trx_state->operation_results[rel_id.instance()].get<object_id_type>();
|
||||
}
|
||||
|
||||
|
||||
} }
|
||||
|
|
|
|||
137
libraries/chain/event_evaluator.cpp
Normal file
137
libraries/chain/event_evaluator.cpp
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.
|
||||
*/
|
||||
#include <graphene/chain/event_evaluator.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/event_group_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 <graphene/chain/global_betting_statistics_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result event_create_evaluator::do_evaluate(const event_create_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
|
||||
//database& d = db();
|
||||
// the event_group_id in the operation can be a relative id. If it is,
|
||||
// resolve it and verify that it is truly an event_group
|
||||
object_id_type resolved_event_group_id = op.event_group_id;
|
||||
if (is_relative(op.event_group_id))
|
||||
resolved_event_group_id = get_relative_id(op.event_group_id);
|
||||
|
||||
FC_ASSERT(resolved_event_group_id.space() == event_group_id_type::space_id &&
|
||||
resolved_event_group_id.type() == event_group_id_type::type_id,
|
||||
"event_group_id must refer to a event_group_id_type");
|
||||
event_group_id = resolved_event_group_id;
|
||||
//const event_group_object& event_group = event_group_id(d);
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type event_create_evaluator::do_apply(const event_create_operation& op)
|
||||
{ try {
|
||||
database& d = db();
|
||||
const event_object& new_event =
|
||||
d.create<event_object>( [&]( event_object& event_obj ) {
|
||||
event_obj.name = op.name;
|
||||
event_obj.season = op.season;
|
||||
event_obj.start_time = op.start_time;
|
||||
event_obj.event_group_id = event_group_id;
|
||||
});
|
||||
//increment number of active events in global betting statistics object
|
||||
const global_betting_statistics_object& betting_statistics = global_betting_statistics_id_type()(d);
|
||||
d.modify( betting_statistics, [&](global_betting_statistics_object& bso) {
|
||||
bso.number_of_active_events += 1;
|
||||
});
|
||||
return new_event.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result event_update_evaluator::do_evaluate(const event_update_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
FC_ASSERT(op.new_event_group_id || op.new_name || op.new_season ||
|
||||
op.new_start_time || op.new_status, "nothing to change");
|
||||
|
||||
if (op.new_event_group_id)
|
||||
{
|
||||
object_id_type resolved_event_group_id = *op.new_event_group_id;
|
||||
if (is_relative(*op.new_event_group_id))
|
||||
resolved_event_group_id = get_relative_id(*op.new_event_group_id);
|
||||
|
||||
FC_ASSERT(resolved_event_group_id.space() == event_group_id_type::space_id &&
|
||||
resolved_event_group_id.type() == event_group_id_type::type_id,
|
||||
"event_group_id must refer to a event_group_id_type");
|
||||
event_group_id = resolved_event_group_id;
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result event_update_evaluator::do_apply(const event_update_operation& op)
|
||||
{ try {
|
||||
database& _db = db();
|
||||
_db.modify(_db.get(op.event_id),
|
||||
[&](event_object& eo) {
|
||||
if( op.new_name )
|
||||
eo.name = *op.new_name;
|
||||
if( op.new_season )
|
||||
eo.season = *op.new_season;
|
||||
if( op.new_start_time )
|
||||
eo.start_time = *op.new_start_time;
|
||||
if( op.new_event_group_id )
|
||||
eo.event_group_id = event_group_id;
|
||||
if( op.new_status )
|
||||
eo.dispatch_new_status(_db, *op.new_status);
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result event_update_status_evaluator::do_evaluate(const event_update_status_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
|
||||
database& d = db();
|
||||
//check that the event to update exists
|
||||
_event_to_update = &op.event_id(d);
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result event_update_status_evaluator::do_apply(const event_update_status_operation& op)
|
||||
{ try {
|
||||
database& d = db();
|
||||
|
||||
d.modify( *_event_to_update, [&](event_object& event_obj) {
|
||||
if (_event_to_update->get_status() != op.status)
|
||||
event_obj.dispatch_new_status(d, op.status);
|
||||
event_obj.scores = op.scores;
|
||||
});
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // graphene::chain
|
||||
98
libraries/chain/event_group_evaluator.cpp
Normal file
98
libraries/chain/event_group_evaluator.cpp
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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/event_group_evaluator.hpp>
|
||||
#include <graphene/chain/event_group_object.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/is_authorized_asset.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result event_group_create_evaluator::do_evaluate(const event_group_create_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
|
||||
// the sport id in the operation can be a relative id. If it is,
|
||||
// resolve it and verify that it is truly a sport
|
||||
object_id_type resolved_id = op.sport_id;
|
||||
if (is_relative(op.sport_id))
|
||||
resolved_id = get_relative_id(op.sport_id);
|
||||
|
||||
FC_ASSERT(resolved_id.space() == sport_id_type::space_id &&
|
||||
resolved_id.type() == sport_id_type::type_id, "sport_id must refer to a sport_id_type");
|
||||
sport_id = resolved_id;
|
||||
|
||||
FC_ASSERT( db().find_object(sport_id), "Invalid sport specified" );
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type event_group_create_evaluator::do_apply(const event_group_create_operation& op)
|
||||
{ try {
|
||||
const event_group_object& new_event_group =
|
||||
db().create<event_group_object>( [&]( event_group_object& event_group_obj ) {
|
||||
event_group_obj.name = op.name;
|
||||
event_group_obj.sport_id = sport_id;
|
||||
});
|
||||
return new_event_group.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result event_group_update_evaluator::do_evaluate(const event_group_update_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
FC_ASSERT(op.new_sport_id.valid() || op.new_name.valid(), "nothing to change");
|
||||
if( op.new_sport_id.valid() )
|
||||
{
|
||||
object_id_type resolved_id = *op.new_sport_id;
|
||||
if (is_relative(*op.new_sport_id))
|
||||
resolved_id = get_relative_id(*op.new_sport_id);
|
||||
|
||||
FC_ASSERT(resolved_id.space() == sport_id_type::space_id &&
|
||||
resolved_id.type() == sport_id_type::type_id, "sport_id must refer to a sport_id_type");
|
||||
sport_id = resolved_id;
|
||||
|
||||
FC_ASSERT( db().find_object(sport_id), "invalid sport specified" );
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result event_group_update_evaluator::do_apply(const event_group_update_operation& op)
|
||||
{ try {
|
||||
database& _db = db();
|
||||
_db.modify(
|
||||
_db.get(op.event_group_id),
|
||||
[&]( event_group_object& ego )
|
||||
{
|
||||
if( op.new_name.valid() )
|
||||
ego.name = *op.new_name;
|
||||
if( op.new_sport_id.valid() )
|
||||
ego.sport_id = sport_id;
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
547
libraries/chain/event_object.cpp
Normal file
547
libraries/chain/event_object.cpp
Normal file
|
|
@ -0,0 +1,547 @@
|
|||
#define DEFAULT_LOGGER "betting"
|
||||
#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
|
||||
#define BOOST_MPL_LIMIT_VECTOR_SIZE 30
|
||||
#define BOOST_MPL_LIMIT_MAP_SIZE 30
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/betting_market_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>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
enum class event_state {
|
||||
upcoming,
|
||||
frozen_upcoming,
|
||||
in_progress,
|
||||
frozen_in_progress,
|
||||
finished,
|
||||
canceled,
|
||||
settled
|
||||
};
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::event_state,
|
||||
(upcoming)
|
||||
(frozen_upcoming)
|
||||
(in_progress)
|
||||
(frozen_in_progress)
|
||||
(finished)
|
||||
(canceled)
|
||||
(settled))
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
namespace msm = boost::msm;
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Events -- most events happen when the witnesses publish an event_update operation with a new
|
||||
// status, so if they publish an event with the status set to `frozen`, we'll generate a `frozen_event`
|
||||
struct upcoming_event
|
||||
{
|
||||
database& db;
|
||||
upcoming_event(database& db) : db(db) {}
|
||||
};
|
||||
struct in_progress_event
|
||||
{
|
||||
database& db;
|
||||
in_progress_event(database& db) : db(db) {}
|
||||
};
|
||||
struct frozen_event
|
||||
{
|
||||
database& db;
|
||||
frozen_event(database& db) : db(db) {}
|
||||
};
|
||||
struct finished_event
|
||||
{
|
||||
database& db;
|
||||
finished_event(database& db) : db(db) {}
|
||||
};
|
||||
struct canceled_event
|
||||
{
|
||||
database& db;
|
||||
canceled_event(database& db) : db(db) {}
|
||||
};
|
||||
|
||||
// event triggered when a betting market group in this event is resolved,
|
||||
// when we get this, check and see if all betting market groups are now
|
||||
// canceled/settled; if so, transition the event to canceled/settled,
|
||||
// otherwise remain in the current state
|
||||
struct betting_market_group_resolved_event
|
||||
{
|
||||
database& db;
|
||||
betting_market_group_id_type resolved_group;
|
||||
bool was_canceled;
|
||||
betting_market_group_resolved_event(database& db, betting_market_group_id_type resolved_group, bool was_canceled) : db(db), resolved_group(resolved_group), was_canceled(was_canceled) {}
|
||||
};
|
||||
|
||||
// event triggered when a betting market group is closed. When we get this,
|
||||
// if all child betting market groups are closed, transition to finished
|
||||
struct betting_market_group_closed_event
|
||||
{
|
||||
database& db;
|
||||
betting_market_group_id_type closed_group;
|
||||
betting_market_group_closed_event(database& db, betting_market_group_id_type closed_group) : db(db), closed_group(closed_group) {}
|
||||
};
|
||||
|
||||
// Events
|
||||
struct event_state_machine_ : public msm::front::state_machine_def<event_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 upcoming : public msm::front::state<> {
|
||||
void on_entry(const betting_market_group_resolved_event& event, event_state_machine_& fsm) {} // transition to self
|
||||
void on_entry(const upcoming_event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> upcoming", ("id", fsm.event_obj->id));
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(fsm.event_obj->id)))
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_upcoming_event(event.db);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
// it's possible a betting market group has already been closed or canceled,
|
||||
// in which case we can't freeze the group. just ignore the exception
|
||||
}
|
||||
}
|
||||
};
|
||||
struct in_progress : public msm::front::state<> {
|
||||
void on_entry(const betting_market_group_resolved_event& event, event_state_machine_& fsm) {} // transition to self
|
||||
void on_entry(const in_progress_event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> in_progress", ("id", fsm.event_obj->id));
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(fsm.event_obj->id)))
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_in_play_event(event.db);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
// it's possible a betting market group has already been closed or canceled,
|
||||
// in which case we can't freeze the group. just ignore the exception
|
||||
}
|
||||
}
|
||||
};
|
||||
struct frozen_upcoming : public msm::front::state<> {
|
||||
void on_entry(const betting_market_group_resolved_event& event, event_state_machine_& fsm) {} // transition to self
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> frozen_upcoming", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
struct frozen_in_progress : public msm::front::state<> {
|
||||
void on_entry(const betting_market_group_resolved_event& event, event_state_machine_& fsm) {} // transition to self
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> frozen_in_progress", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
struct finished : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> finished", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
struct settled : public msm::front::state<>{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> settled", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
struct canceled : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> canceled", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
|
||||
// actions
|
||||
void record_whether_group_settled_or_canceled(const betting_market_group_resolved_event& event) {
|
||||
if (!event.was_canceled)
|
||||
event_obj->at_least_one_betting_market_group_settled = true;
|
||||
}
|
||||
|
||||
void freeze_betting_market_groups(const frozen_event& event) {
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
{
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_frozen_event(event.db);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
// it's possible a betting market group has already been closed or canceled,
|
||||
// in which case we can't freeze the group. just ignore the exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void close_all_betting_market_groups(const finished_event& event) {
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
{
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_closed_event(event.db, true);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
// it's possible a betting market group has already been closed or canceled,
|
||||
// in which case we can't close the group. just ignore the exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cancel_all_betting_market_groups(const canceled_event& event) {
|
||||
auto& betting_market_group_index = event.db.template get_index_type<betting_market_group_object_index>().indices().template get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_canceled_event(event.db, true);
|
||||
});
|
||||
}
|
||||
|
||||
// Guards
|
||||
bool all_betting_market_groups_are_closed(const betting_market_group_closed_event& event)
|
||||
{
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
if (betting_market_group.id != event.closed_group)
|
||||
{
|
||||
betting_market_group_status status = betting_market_group.get_status();
|
||||
if (status != betting_market_group_status::closed &&
|
||||
status != betting_market_group_status::graded &&
|
||||
status != betting_market_group_status::re_grading &&
|
||||
status != betting_market_group_status::settled &&
|
||||
status != betting_market_group_status::canceled)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool all_betting_market_groups_are_canceled(const betting_market_group_resolved_event& event)
|
||||
{
|
||||
// if the bmg that just resolved was settled, obviously all didn't cancel
|
||||
if (!event.was_canceled)
|
||||
return false;
|
||||
// if a previously-resolved group was settled, all didn't cancel
|
||||
if (event_obj->at_least_one_betting_market_group_settled)
|
||||
return false;
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
if (betting_market_group.id != event.resolved_group)
|
||||
if (betting_market_group.get_status() != betting_market_group_status::canceled)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool all_betting_market_groups_are_resolved(const betting_market_group_resolved_event& event)
|
||||
{
|
||||
if (!event.was_canceled)
|
||||
event_obj->at_least_one_betting_market_group_settled = true;
|
||||
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id))) {
|
||||
if (betting_market_group.id != event.resolved_group) {
|
||||
betting_market_group_status status = betting_market_group.get_status();
|
||||
if (status != betting_market_group_status::canceled && status != betting_market_group_status::settled)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef upcoming initial_state;
|
||||
typedef event_state_machine_ x; // makes transition table cleaner
|
||||
|
||||
// Transition table for tournament
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
_row < upcoming, in_progress_event, in_progress >,
|
||||
a_row< upcoming, finished_event, finished, &x::close_all_betting_market_groups >,
|
||||
a_row< upcoming, frozen_event, frozen_upcoming, &x::freeze_betting_market_groups >,
|
||||
a_row< upcoming, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
a_row< upcoming, betting_market_group_resolved_event,upcoming, &x::record_whether_group_settled_or_canceled >,
|
||||
g_row< upcoming, betting_market_group_closed_event, finished, &x::all_betting_market_groups_are_closed >,
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
_row < frozen_upcoming, upcoming_event, upcoming >,
|
||||
_row < frozen_upcoming, in_progress_event, in_progress >,
|
||||
a_row< frozen_upcoming, finished_event, finished, &x::close_all_betting_market_groups >,
|
||||
a_row< frozen_upcoming, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
a_row< frozen_upcoming, betting_market_group_resolved_event,frozen_upcoming, &x::record_whether_group_settled_or_canceled >,
|
||||
g_row< frozen_upcoming, betting_market_group_closed_event, finished, &x::all_betting_market_groups_are_closed >,
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
a_row< in_progress, frozen_event, frozen_in_progress, &x::freeze_betting_market_groups >,
|
||||
a_row< in_progress, finished_event, finished, &x::close_all_betting_market_groups >,
|
||||
a_row< in_progress, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
a_row< in_progress, betting_market_group_resolved_event,in_progress, &x::record_whether_group_settled_or_canceled >,
|
||||
g_row< in_progress, betting_market_group_closed_event, finished, &x::all_betting_market_groups_are_closed >,
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
_row < frozen_in_progress, in_progress_event, in_progress >,
|
||||
a_row< frozen_in_progress, finished_event, finished, &x::close_all_betting_market_groups >,
|
||||
a_row< frozen_in_progress, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
a_row< frozen_in_progress, betting_market_group_resolved_event,frozen_in_progress, &x::record_whether_group_settled_or_canceled >,
|
||||
g_row< frozen_in_progress, betting_market_group_closed_event, finished, &x::all_betting_market_groups_are_closed >,
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
a_row< finished, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
g_row< finished, betting_market_group_resolved_event,settled, &x::all_betting_market_groups_are_resolved >,
|
||||
g_row< finished, betting_market_group_resolved_event,canceled, &x::all_betting_market_groups_are_canceled >
|
||||
> {};
|
||||
|
||||
template <class Fsm,class Event>
|
||||
void no_transition(Event const& e, Fsm& ,int state)
|
||||
{
|
||||
FC_THROW_EXCEPTION(graphene::chain::no_transition, "No transition");
|
||||
}
|
||||
|
||||
event_object* event_obj;
|
||||
event_state_machine_(event_object* event_obj) : event_obj(event_obj) {}
|
||||
};
|
||||
typedef msm::back::state_machine<event_state_machine_> event_state_machine;
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
class event_object::impl {
|
||||
public:
|
||||
event_state_machine state_machine;
|
||||
|
||||
impl(event_object* self) : state_machine(self) {}
|
||||
};
|
||||
|
||||
event_object::event_object() :
|
||||
at_least_one_betting_market_group_settled(false),
|
||||
my(new impl(this))
|
||||
{
|
||||
}
|
||||
|
||||
event_object::event_object(const event_object& rhs) :
|
||||
graphene::db::abstract_object<event_object>(rhs),
|
||||
name(rhs.name),
|
||||
season(rhs.season),
|
||||
start_time(rhs.start_time),
|
||||
event_group_id(rhs.event_group_id),
|
||||
at_least_one_betting_market_group_settled(rhs.at_least_one_betting_market_group_settled),
|
||||
scores(rhs.scores),
|
||||
my(new impl(this))
|
||||
{
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.event_obj = this;
|
||||
}
|
||||
|
||||
event_object& event_object::operator=(const event_object& rhs)
|
||||
{
|
||||
//graphene::db::abstract_object<event_object>::operator=(rhs);
|
||||
id = rhs.id;
|
||||
name = rhs.name;
|
||||
season = rhs.season;
|
||||
start_time = rhs.start_time;
|
||||
event_group_id = rhs.event_group_id;
|
||||
at_least_one_betting_market_group_settled = rhs.at_least_one_betting_market_group_settled;
|
||||
scores = rhs.scores;
|
||||
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.event_obj = this;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
event_object::~event_object()
|
||||
{
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool verify_event_status_constants()
|
||||
{
|
||||
unsigned error_count = 0;
|
||||
typedef msm::back::generate_state_set<event_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<event_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<event_state>::to_string((event_state)i);
|
||||
if (!strcmp(fc_reflected_value_name, filled_state_names[i]))
|
||||
fc_elog(fc::logger::get("default"),
|
||||
"Error, state string mismatch 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("default"),
|
||||
"Error, no reflection for value ${int_value} in enum event_status",
|
||||
("int_value", i));
|
||||
++error_count;
|
||||
}
|
||||
}
|
||||
dlog("Done checking constants");
|
||||
|
||||
return error_count == 0;
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
event_status event_object::get_status() const
|
||||
{
|
||||
static bool state_constants_are_correct = verify_event_status_constants();
|
||||
(void)&state_constants_are_correct;
|
||||
event_state state = (event_state)my->state_machine.current_state()[0];
|
||||
|
||||
ddump((state));
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case event_state::upcoming:
|
||||
return event_status::upcoming;
|
||||
case event_state::frozen_upcoming:
|
||||
case event_state::frozen_in_progress:
|
||||
return event_status::frozen;
|
||||
case event_state::in_progress:
|
||||
return event_status::in_progress;
|
||||
case event_state::finished:
|
||||
return event_status::finished;
|
||||
case event_state::canceled:
|
||||
return event_status::canceled;
|
||||
case event_state::settled:
|
||||
return event_status::settled;
|
||||
default:
|
||||
FC_THROW("Unexpected event state");
|
||||
};
|
||||
}
|
||||
|
||||
void event_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 event_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;
|
||||
}
|
||||
|
||||
void event_object::on_upcoming_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(upcoming_event(db));
|
||||
}
|
||||
|
||||
void event_object::on_in_progress_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(in_progress_event(db));
|
||||
}
|
||||
|
||||
void event_object::on_frozen_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(frozen_event(db));
|
||||
}
|
||||
|
||||
void event_object::on_finished_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(finished_event(db));
|
||||
}
|
||||
|
||||
void event_object::on_canceled_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(canceled_event(db));
|
||||
}
|
||||
|
||||
void event_object::on_betting_market_group_resolved(database& db, betting_market_group_id_type resolved_group, bool was_canceled)
|
||||
{
|
||||
my->state_machine.process_event(betting_market_group_resolved_event(db, resolved_group, was_canceled));
|
||||
}
|
||||
|
||||
void event_object::on_betting_market_group_closed(database& db, betting_market_group_id_type closed_group)
|
||||
{
|
||||
my->state_machine.process_event(betting_market_group_closed_event(db, closed_group));
|
||||
}
|
||||
|
||||
// These are the only statuses that can be explicitly set by witness operations. The missing
|
||||
// status, 'settled', is automatically set when all of the betting market groups have
|
||||
// settled/canceled
|
||||
void event_object::dispatch_new_status(database& db, event_status new_status)
|
||||
{
|
||||
switch (new_status) {
|
||||
case event_status::upcoming: // by witnesses to unfreeze a frozen event
|
||||
on_upcoming_event(db);
|
||||
break;
|
||||
case event_status::in_progress: // by witnesses when the event starts
|
||||
on_in_progress_event(db);
|
||||
break;
|
||||
case event_status::frozen: // by witnesses when the event needs to be frozen
|
||||
on_frozen_event(db);
|
||||
break;
|
||||
case event_status::finished: // by witnesses when the event is complete
|
||||
on_finished_event(db);
|
||||
break;
|
||||
case event_status::canceled: // by witnesses to cancel the event
|
||||
on_canceled_event(db);
|
||||
break;
|
||||
default:
|
||||
FC_THROW("Status ${new_status} cannot be explicitly set", ("new_status", new_status));
|
||||
}
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
namespace fc {
|
||||
// Manually reflect event_object to variant to properly reflect "state"
|
||||
void to_variant(const graphene::chain::event_object& event_obj, fc::variant& v)
|
||||
{
|
||||
fc::mutable_variant_object o;
|
||||
o("id", event_obj.id)
|
||||
("name", event_obj.name)
|
||||
("season", event_obj.season)
|
||||
("start_time", event_obj.start_time)
|
||||
("event_group_id", event_obj.event_group_id)
|
||||
("scores", event_obj.scores)
|
||||
("status", event_obj.get_status());
|
||||
|
||||
v = o;
|
||||
}
|
||||
|
||||
// Manually reflect event_object to variant to properly reflect "state"
|
||||
void from_variant(const fc::variant& v, graphene::chain::event_object& event_obj)
|
||||
{
|
||||
event_obj.id = v["id"].as<graphene::chain::event_id_type>();
|
||||
event_obj.name = v["name"].as<graphene::chain::internationalized_string_type>();
|
||||
event_obj.season = v["season"].as<graphene::chain::internationalized_string_type>();
|
||||
event_obj.start_time = v["start_time"].as<optional<time_point_sec> >();
|
||||
event_obj.event_group_id = v["event_group_id"].as<graphene::chain::event_group_id_type>();
|
||||
event_obj.scores = v["scores"].as<std::vector<std::string>>();
|
||||
graphene::chain::event_status status = v["status"].as<graphene::chain::event_status>();
|
||||
const_cast<int*>(event_obj.my->state_machine.current_state())[0] = (int)status;
|
||||
}
|
||||
} //end namespace fc
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
|
@ -96,6 +96,7 @@ fc::variant_object get_config()
|
|||
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;
|
||||
|
|
|
|||
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( 1459789200 ))
|
||||
#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( 1458752400 ))
|
||||
#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( 1458752400 ))
|
||||
#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( 1458752400 ))
|
||||
#endif
|
||||
|
|
@ -50,6 +50,10 @@ namespace graphene { namespace chain {
|
|||
* 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;
|
||||
/** Total operations related to this account. */
|
||||
uint32_t total_ops = 0;
|
||||
/** Total operations related to this account that has been removed from the database. */
|
||||
uint32_t removed_ops = 0;
|
||||
|
||||
/**
|
||||
* When calculating votes it is necessary to know how much is stored in orders (and thus unavailable for
|
||||
|
|
@ -308,6 +312,31 @@ namespace graphene { namespace chain {
|
|||
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;
|
||||
/**
|
||||
|
|
@ -363,6 +392,49 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
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,
|
||||
|
|
@ -385,8 +457,14 @@ FC_REFLECT_DERIVED( graphene::chain::account_statistics_object,
|
|||
(graphene::chain::object),
|
||||
(owner)
|
||||
(most_recent_op)
|
||||
(total_ops)(removed_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) )
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -82,6 +82,18 @@ namespace graphene { namespace chain {
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -132,6 +132,9 @@ namespace graphene { namespace chain {
|
|||
|
||||
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
|
||||
|
|
@ -148,6 +151,10 @@ namespace graphene { namespace chain {
|
|||
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); }
|
||||
|
|
@ -211,8 +218,10 @@ namespace graphene { namespace chain {
|
|||
|
||||
time_point_sec feed_expiration_time()const
|
||||
{ return current_feed_publication_time + options.feed_lifetime_sec; }
|
||||
bool feed_is_expired(time_point_sec current_time)const
|
||||
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);
|
||||
};
|
||||
|
||||
|
|
@ -226,15 +235,18 @@ namespace graphene { namespace chain {
|
|||
>
|
||||
>
|
||||
> asset_bitasset_data_object_multi_index_type;
|
||||
typedef flat_index<asset_bitasset_data_object> asset_bitasset_data_index;
|
||||
//typedef flat_index<asset_bitasset_data_object> asset_bitasset_data_index;
|
||||
typedef generic_index<asset_bitasset_data_object, asset_bitasset_data_object_multi_index_type> asset_bitasset_data_index;
|
||||
|
||||
struct by_symbol;
|
||||
struct by_type;
|
||||
struct by_issuer;
|
||||
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_non_unique< tag<by_issuer>, member<asset_object, account_id_type, &asset_object::issuer > >,
|
||||
ordered_unique< tag<by_type>,
|
||||
composite_key< asset_object,
|
||||
const_mem_fun<asset_object, bool, &asset_object::is_market_issued>,
|
||||
|
|
@ -245,6 +257,84 @@ namespace graphene { namespace chain {
|
|||
> 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),
|
||||
|
|
@ -261,6 +351,21 @@ FC_REFLECT_DERIVED( graphene::chain::asset_bitasset_data_object, (graphene::db::
|
|||
(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)
|
||||
|
|
@ -269,4 +374,5 @@ FC_REFLECT_DERIVED( graphene::chain::asset_object, (graphene::db::object),
|
|||
(dynamic_asset_data_id)
|
||||
(bitasset_data_id)
|
||||
(buyback_account)
|
||||
(dividend_data_id)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* 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 betting_market_rules_create_evaluator : public evaluator<betting_market_rules_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_rules_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const betting_market_rules_create_operation& o );
|
||||
object_id_type do_apply( const betting_market_rules_create_operation& o );
|
||||
};
|
||||
|
||||
class betting_market_rules_update_evaluator : public evaluator<betting_market_rules_update_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_rules_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const betting_market_rules_update_operation& o );
|
||||
void_result do_apply( const betting_market_rules_update_operation& o );
|
||||
private:
|
||||
const betting_market_rules_object* _rules;
|
||||
};
|
||||
|
||||
class betting_market_group_create_evaluator : public evaluator<betting_market_group_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_group_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const betting_market_group_create_operation& o);
|
||||
object_id_type do_apply(const betting_market_group_create_operation& o);
|
||||
private:
|
||||
event_id_type _event_id;
|
||||
betting_market_rules_id_type _rules_id;
|
||||
};
|
||||
|
||||
class betting_market_group_update_evaluator : public evaluator<betting_market_group_update_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_group_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const betting_market_group_update_operation& o);
|
||||
void_result do_apply(const betting_market_group_update_operation& o);
|
||||
private:
|
||||
betting_market_rules_id_type _rules_id;
|
||||
const betting_market_group_object* _betting_market_group;
|
||||
};
|
||||
|
||||
class betting_market_create_evaluator : public evaluator<betting_market_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const betting_market_create_operation& o );
|
||||
object_id_type do_apply( const betting_market_create_operation& o );
|
||||
private:
|
||||
betting_market_group_id_type _group_id;
|
||||
};
|
||||
|
||||
class betting_market_update_evaluator : public evaluator<betting_market_update_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const betting_market_update_operation& o );
|
||||
void_result do_apply( const betting_market_update_operation& o );
|
||||
private:
|
||||
const betting_market_object* _betting_market;
|
||||
betting_market_group_id_type _group_id;
|
||||
};
|
||||
|
||||
class bet_place_evaluator : public evaluator<bet_place_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef bet_place_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const bet_place_operation& o );
|
||||
object_id_type do_apply( const bet_place_operation& o );
|
||||
private:
|
||||
const betting_market_group_object* _betting_market_group;
|
||||
const betting_market_object* _betting_market;
|
||||
const chain_parameters* _current_params;
|
||||
const asset_object* _asset;
|
||||
share_type _stake_plus_fees;
|
||||
};
|
||||
|
||||
class bet_cancel_evaluator : public evaluator<bet_cancel_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef bet_cancel_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const bet_cancel_operation& o );
|
||||
void_result do_apply( const bet_cancel_operation& o );
|
||||
private:
|
||||
const bet_object* _bet_to_cancel;
|
||||
};
|
||||
|
||||
class betting_market_group_resolve_evaluator : public evaluator<betting_market_group_resolve_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_group_resolve_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const betting_market_group_resolve_operation& o );
|
||||
void_result do_apply( const betting_market_group_resolve_operation& o );
|
||||
private:
|
||||
const betting_market_group_object* _betting_market_group;
|
||||
};
|
||||
|
||||
class betting_market_group_cancel_unmatched_bets_evaluator : public evaluator<betting_market_group_cancel_unmatched_bets_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_group_cancel_unmatched_bets_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const betting_market_group_cancel_unmatched_bets_operation& o );
|
||||
void_result do_apply( const betting_market_group_cancel_unmatched_bets_operation& o );
|
||||
private:
|
||||
const betting_market_group_object* _betting_market_group;
|
||||
};
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
713
libraries/chain/include/graphene/chain/betting_market_object.hpp
Normal file
713
libraries/chain/include/graphene/chain/betting_market_object.hpp
Normal file
|
|
@ -0,0 +1,713 @@
|
|||
/*
|
||||
* 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>
|
||||
#include <graphene/chain/protocol/betting_market.hpp>
|
||||
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class betting_market_object;
|
||||
class betting_market_group_object;
|
||||
} }
|
||||
|
||||
namespace fc {
|
||||
void to_variant(const graphene::chain::betting_market_object& betting_market_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::betting_market_object& betting_market_obj);
|
||||
void to_variant(const graphene::chain::betting_market_group_object& betting_market_group_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::betting_market_group_object& betting_market_group_obj);
|
||||
} //end namespace fc
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
FC_DECLARE_EXCEPTION(no_transition, 100000, "Invalid state transition");
|
||||
class database;
|
||||
|
||||
struct by_event_id;
|
||||
struct by_settling_time;
|
||||
struct by_betting_market_group_id;
|
||||
|
||||
class betting_market_rules_object : public graphene::db::abstract_object< betting_market_rules_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = betting_market_rules_object_type;
|
||||
|
||||
internationalized_string_type name;
|
||||
|
||||
internationalized_string_type description;
|
||||
};
|
||||
|
||||
class betting_market_group_object : public graphene::db::abstract_object< betting_market_group_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = betting_market_group_object_type;
|
||||
|
||||
betting_market_group_object();
|
||||
betting_market_group_object(const betting_market_group_object& rhs);
|
||||
~betting_market_group_object();
|
||||
betting_market_group_object& operator=(const betting_market_group_object& rhs);
|
||||
|
||||
internationalized_string_type description;
|
||||
|
||||
event_id_type event_id;
|
||||
|
||||
betting_market_rules_id_type rules_id;
|
||||
|
||||
asset_id_type asset_id;
|
||||
|
||||
share_type total_matched_bets_amount;
|
||||
|
||||
bool never_in_play;
|
||||
|
||||
uint32_t delay_before_settling;
|
||||
|
||||
fc::optional<fc::time_point_sec> settling_time; // the time the payout will occur (set after grading)
|
||||
|
||||
bool bets_are_allowed() const {
|
||||
return get_status() == betting_market_group_status::upcoming ||
|
||||
get_status() == betting_market_group_status::in_play;
|
||||
}
|
||||
|
||||
bool bets_are_delayed() const {
|
||||
return get_status() == betting_market_group_status::in_play;
|
||||
}
|
||||
|
||||
betting_market_group_status get_status() const;
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const betting_market_group_object& betting_market_group_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, betting_market_group_object& betting_market_group_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::betting_market_group_object& betting_market_group_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::betting_market_group_object& betting_market_group_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
|
||||
void on_upcoming_event(database& db);
|
||||
void on_in_play_event(database& db);
|
||||
void on_frozen_event(database& db);
|
||||
void on_closed_event(database& db, bool closed_by_event);
|
||||
void on_graded_event(database& db);
|
||||
void on_re_grading_event(database& db);
|
||||
void on_settled_event(database& db);
|
||||
void on_canceled_event(database& db, bool canceled_by_event);
|
||||
void dispatch_new_status(database& db, betting_market_group_status new_status);
|
||||
|
||||
private:
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
class betting_market_object : public graphene::db::abstract_object< betting_market_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = betting_market_object_type;
|
||||
|
||||
betting_market_object();
|
||||
betting_market_object(const betting_market_object& rhs);
|
||||
~betting_market_object();
|
||||
betting_market_object& operator=(const betting_market_object& rhs);
|
||||
|
||||
betting_market_group_id_type group_id;
|
||||
|
||||
internationalized_string_type description;
|
||||
|
||||
internationalized_string_type payout_condition;
|
||||
|
||||
// once the market is graded, this holds the proposed grading
|
||||
// after settling/canceling, this is the actual grading
|
||||
fc::optional<betting_market_resolution_type> resolution;
|
||||
|
||||
betting_market_status get_status() const;
|
||||
|
||||
void cancel_all_unmatched_bets(database& db) const;
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const betting_market_object& betting_market_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, betting_market_object& betting_market_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::betting_market_object& betting_market_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::betting_market_object& betting_market_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
|
||||
void on_unresolved_event(database& db);
|
||||
void on_frozen_event(database& db);
|
||||
void on_closed_event(database& db);
|
||||
void on_graded_event(database& db, betting_market_resolution_type new_grading);
|
||||
void on_settled_event(database& db);
|
||||
void on_canceled_event(database& db);
|
||||
private:
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
class bet_object : public graphene::db::abstract_object< bet_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = bet_object_type;
|
||||
|
||||
account_id_type bettor_id;
|
||||
|
||||
betting_market_id_type betting_market_id;
|
||||
|
||||
asset amount_to_bet;
|
||||
|
||||
bet_multiplier_type backer_multiplier;
|
||||
|
||||
bet_type back_or_lay;
|
||||
|
||||
fc::optional<fc::time_point_sec> end_of_delay;
|
||||
|
||||
static share_type get_approximate_matching_amount(share_type bet_amount, bet_multiplier_type backer_multiplier, bet_type back_or_lay, bool round_up = false);
|
||||
|
||||
// returns the amount of a bet that completely matches this bet
|
||||
share_type get_approximate_matching_amount(bool round_up = false) const;
|
||||
|
||||
static share_type get_exact_matching_amount(share_type bet_amount, bet_multiplier_type backer_multiplier, bet_type back_or_lay);
|
||||
share_type get_exact_matching_amount() const;
|
||||
|
||||
static std::pair<share_type, share_type> get_ratio(bet_multiplier_type backer_multiplier);
|
||||
std::pair<share_type, share_type> get_ratio() const;
|
||||
|
||||
// returns the minimum amount this bet could have that could be matched at these odds
|
||||
share_type get_minimum_matchable_amount() const;
|
||||
// returns the minimum amount another user could bet to match this bet at these odds
|
||||
share_type get_minimum_matching_amount() const;
|
||||
};
|
||||
|
||||
class betting_market_position_object : public graphene::db::abstract_object< betting_market_position_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_betting_market_position_object_type;
|
||||
|
||||
account_id_type bettor_id;
|
||||
|
||||
betting_market_id_type betting_market_id;
|
||||
|
||||
share_type pay_if_payout_condition;
|
||||
share_type pay_if_not_payout_condition;
|
||||
share_type pay_if_canceled;
|
||||
share_type pay_if_not_canceled;
|
||||
share_type fees_collected;
|
||||
|
||||
share_type reduce();
|
||||
};
|
||||
|
||||
typedef multi_index_container<
|
||||
betting_market_rules_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >
|
||||
> > betting_market_rules_object_multi_index_type;
|
||||
typedef generic_index<betting_market_rules_object, betting_market_rules_object_multi_index_type> betting_market_rules_object_index;
|
||||
|
||||
typedef multi_index_container<
|
||||
betting_market_group_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag<by_event_id>, member<betting_market_group_object, event_id_type, &betting_market_group_object::event_id> >,
|
||||
ordered_non_unique< tag<by_settling_time>, member<betting_market_group_object, fc::optional<fc::time_point_sec>, &betting_market_group_object::settling_time> >
|
||||
> > betting_market_group_object_multi_index_type;
|
||||
typedef generic_index<betting_market_group_object, betting_market_group_object_multi_index_type> betting_market_group_object_index;
|
||||
|
||||
typedef multi_index_container<
|
||||
betting_market_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag<by_betting_market_group_id>, member<betting_market_object, betting_market_group_id_type, &betting_market_object::group_id> >
|
||||
> > betting_market_object_multi_index_type;
|
||||
|
||||
typedef generic_index<betting_market_object, betting_market_object_multi_index_type> betting_market_object_index;
|
||||
|
||||
struct compare_bet_by_odds {
|
||||
bool operator()(const bet_object& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(lhs.end_of_delay, lhs.betting_market_id, lhs.back_or_lay, lhs.backer_multiplier, lhs.id,
|
||||
rhs.end_of_delay, rhs.betting_market_id, rhs.back_or_lay, rhs.backer_multiplier, rhs.id);
|
||||
}
|
||||
|
||||
template<typename T0>
|
||||
bool operator() (const std::tuple<T0>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(fc::optional<time_point_sec>(), std::get<0>(lhs), rhs.end_of_delay, rhs.betting_market_id);
|
||||
}
|
||||
|
||||
template<typename T0>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0>& rhs) const
|
||||
{
|
||||
return compare(lhs.end_of_delay, lhs.betting_market_id, fc::optional<time_point_sec>(), std::get<0>(rhs));
|
||||
}
|
||||
|
||||
template<typename T0, typename T1>
|
||||
bool operator() (const std::tuple<T0, T1>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(fc::optional<fc::time_point_sec>(), std::get<0>(lhs), std::get<1>(lhs), rhs.end_of_delay, rhs.betting_market_id, rhs.back_or_lay);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0, T1>& rhs) const
|
||||
{
|
||||
return compare(lhs.end_of_delay, lhs.betting_market_id, lhs.back_or_lay, fc::optional<time_point_sec>(), std::get<0>(rhs), std::get<1>(rhs));
|
||||
}
|
||||
|
||||
template<typename T0, typename T1, typename T2>
|
||||
bool operator() (const std::tuple<T0, T1, T2>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(fc::optional<time_point_sec>(), std::get<0>(lhs), std::get<1>(lhs), std::get<2>(lhs),
|
||||
rhs.end_of_delay, rhs.betting_market_id, rhs.back_or_lay, rhs.backer_multiplier);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1, typename T2>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0, T1, T2>& rhs) const
|
||||
{
|
||||
return compare(lhs.end_of_delay, lhs.betting_market_id, lhs.back_or_lay, lhs.backer_multiplier,
|
||||
fc::optional<time_point_sec>(), std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs));
|
||||
}
|
||||
template<typename T0, typename T1, typename T2, typename T3>
|
||||
bool operator() (const std::tuple<T0, T1, T2, T3>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(fc::optional<time_point_sec>(), std::get<0>(lhs), std::get<1>(lhs), std::get<2>(lhs), std::get<3>(lhs),
|
||||
rhs.end_of_delay, rhs.betting_market_id, rhs.back_or_lay, rhs.backer_multiplier, rhs.id);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1, typename T2, typename T3>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0, T1, T2, T3>& rhs) const
|
||||
{
|
||||
return compare(lhs.end_of_delay, lhs.betting_market_id, lhs.back_or_lay, lhs.backer_multiplier, lhs.id,
|
||||
fc::optional<time_point_sec>(), std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs), std::get<3>(rhs));
|
||||
}
|
||||
bool compare(const fc::optional<fc::time_point_sec>& lhs_end_of_delay,
|
||||
const betting_market_id_type& lhs_betting_market_id,
|
||||
const fc::optional<fc::time_point_sec>& rhs_end_of_delay,
|
||||
const betting_market_id_type& rhs_betting_market_id) const
|
||||
{
|
||||
// if either bet is delayed, sort the delayed bet to the
|
||||
// front. If both are delayed, the delay expiring soonest
|
||||
// comes first.
|
||||
if (lhs_end_of_delay || rhs_end_of_delay)
|
||||
{
|
||||
if (!rhs_end_of_delay)
|
||||
return true;
|
||||
if (!lhs_end_of_delay)
|
||||
return false;
|
||||
return *lhs_end_of_delay < *rhs_end_of_delay;
|
||||
}
|
||||
|
||||
return lhs_betting_market_id < rhs_betting_market_id;
|
||||
}
|
||||
bool compare(const fc::optional<fc::time_point_sec>& lhs_end_of_delay,
|
||||
const betting_market_id_type& lhs_betting_market_id, bet_type lhs_bet_type,
|
||||
const fc::optional<fc::time_point_sec>& rhs_end_of_delay,
|
||||
const betting_market_id_type& rhs_betting_market_id, bet_type rhs_bet_type) const
|
||||
{
|
||||
// if either bet is delayed, sort the delayed bet to the
|
||||
// front. If both are delayed, the delay expiring soonest
|
||||
// comes first.
|
||||
if (lhs_end_of_delay || rhs_end_of_delay)
|
||||
{
|
||||
if (!rhs_end_of_delay)
|
||||
return true;
|
||||
if (!lhs_end_of_delay)
|
||||
return false;
|
||||
return *lhs_end_of_delay < *rhs_end_of_delay;
|
||||
}
|
||||
|
||||
if (lhs_betting_market_id < rhs_betting_market_id)
|
||||
return true;
|
||||
if (lhs_betting_market_id > rhs_betting_market_id)
|
||||
return false;
|
||||
return lhs_bet_type < rhs_bet_type;
|
||||
}
|
||||
bool compare(const fc::optional<fc::time_point_sec>& lhs_end_of_delay,
|
||||
const betting_market_id_type& lhs_betting_market_id, bet_type lhs_bet_type,
|
||||
bet_multiplier_type lhs_backer_multiplier,
|
||||
const fc::optional<fc::time_point_sec>& rhs_end_of_delay,
|
||||
const betting_market_id_type& rhs_betting_market_id, bet_type rhs_bet_type,
|
||||
bet_multiplier_type rhs_backer_multiplier) const
|
||||
{
|
||||
// if either bet is delayed, sort the delayed bet to the
|
||||
// front. If both are delayed, the delay expiring soonest
|
||||
// comes first.
|
||||
if (lhs_end_of_delay || rhs_end_of_delay)
|
||||
{
|
||||
if (!rhs_end_of_delay)
|
||||
return true;
|
||||
if (!lhs_end_of_delay)
|
||||
return false;
|
||||
return *lhs_end_of_delay < *rhs_end_of_delay;
|
||||
}
|
||||
|
||||
if (lhs_betting_market_id < rhs_betting_market_id)
|
||||
return true;
|
||||
if (lhs_betting_market_id > rhs_betting_market_id)
|
||||
return false;
|
||||
if (lhs_bet_type < rhs_bet_type)
|
||||
return true;
|
||||
if (lhs_bet_type > rhs_bet_type)
|
||||
return false;
|
||||
if (lhs_bet_type == bet_type::back)
|
||||
return lhs_backer_multiplier < rhs_backer_multiplier;
|
||||
else
|
||||
return lhs_backer_multiplier > rhs_backer_multiplier;
|
||||
}
|
||||
bool compare(const fc::optional<fc::time_point_sec>& lhs_end_of_delay,
|
||||
const betting_market_id_type& lhs_betting_market_id, bet_type lhs_bet_type,
|
||||
bet_multiplier_type lhs_backer_multiplier, const bet_id_type& lhs_bet_id,
|
||||
const fc::optional<fc::time_point_sec>& rhs_end_of_delay,
|
||||
const betting_market_id_type& rhs_betting_market_id, bet_type rhs_bet_type,
|
||||
bet_multiplier_type rhs_backer_multiplier, const bet_id_type& rhs_bet_id) const
|
||||
{
|
||||
|
||||
// if either bet is delayed, sort the delayed bet to the
|
||||
// front. If both are delayed, the delay expiring soonest
|
||||
// comes first.
|
||||
if (lhs_end_of_delay || rhs_end_of_delay)
|
||||
{
|
||||
if (!rhs_end_of_delay)
|
||||
return true;
|
||||
if (!lhs_end_of_delay)
|
||||
return false;
|
||||
if (*lhs_end_of_delay < *rhs_end_of_delay)
|
||||
return true;
|
||||
if (*lhs_end_of_delay > *rhs_end_of_delay)
|
||||
return false;
|
||||
// if both bets have the same delay, prefer the one
|
||||
// that was placed first (lowest id)
|
||||
return lhs_bet_id < rhs_bet_id;
|
||||
}
|
||||
|
||||
// if neither bet was delayed
|
||||
if (lhs_betting_market_id < rhs_betting_market_id)
|
||||
return true;
|
||||
if (lhs_betting_market_id > rhs_betting_market_id)
|
||||
return false;
|
||||
if (lhs_bet_type < rhs_bet_type)
|
||||
return true;
|
||||
if (lhs_bet_type > rhs_bet_type)
|
||||
return false;
|
||||
if (lhs_backer_multiplier < rhs_backer_multiplier)
|
||||
return lhs_bet_type == bet_type::back;
|
||||
if (lhs_backer_multiplier > rhs_backer_multiplier)
|
||||
return lhs_bet_type == bet_type::lay;
|
||||
return lhs_bet_id < rhs_bet_id;
|
||||
}
|
||||
};
|
||||
|
||||
struct compare_bet_by_bettor_then_odds {
|
||||
bool operator()(const bet_object& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(lhs.bettor_id, lhs.betting_market_id, lhs.back_or_lay, lhs.backer_multiplier, lhs.id,
|
||||
rhs.bettor_id, rhs.betting_market_id, rhs.back_or_lay, rhs.backer_multiplier, rhs.id);
|
||||
}
|
||||
|
||||
template<typename T0>
|
||||
bool operator() (const std::tuple<T0>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(std::get<0>(lhs), rhs.bettor_id);
|
||||
}
|
||||
|
||||
template<typename T0>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0>& rhs) const
|
||||
{
|
||||
return compare(lhs.bettor_id, std::get<0>(rhs));
|
||||
}
|
||||
|
||||
template<typename T0, typename T1>
|
||||
bool operator() (const std::tuple<T0, T1>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(std::get<0>(lhs), std::get<1>(lhs), rhs.bettor_id, rhs.betting_market_id);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0, T1>& rhs) const
|
||||
{
|
||||
return compare(lhs.bettor_id, lhs.betting_market_id, std::get<0>(rhs), std::get<1>(rhs));
|
||||
}
|
||||
|
||||
template<typename T0, typename T1, typename T2>
|
||||
bool operator() (const std::tuple<T0, T1, T2>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(std::get<0>(lhs), std::get<1>(lhs), std::get<2>(lhs),
|
||||
rhs.bettor_id, rhs.betting_market_id, rhs.back_or_lay);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1, typename T2>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0, T1, T2>& rhs) const
|
||||
{
|
||||
return compare(lhs.bettor_id, lhs.betting_market_id, lhs.back_or_lay,
|
||||
std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs));
|
||||
}
|
||||
template<typename T0, typename T1, typename T2, typename T3>
|
||||
bool operator() (const std::tuple<T0, T1, T2, T3>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(std::get<0>(lhs), std::get<1>(lhs), std::get<2>(lhs), std::get<3>(lhs),
|
||||
rhs.bettor_id, rhs.betting_market_id, rhs.back_or_lay, rhs.backer_multiplier);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1, typename T2, typename T3>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0, T1, T2, T3>& rhs) const
|
||||
{
|
||||
return compare(lhs.bettor_id, lhs.betting_market_id, lhs.back_or_lay, lhs.backer_multiplier, lhs.id,
|
||||
std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs), std::get<3>(rhs));
|
||||
}
|
||||
template<typename T0, typename T1, typename T2, typename T3, typename T4>
|
||||
bool operator() (const std::tuple<T0, T1, T2, T3, T4>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(std::get<0>(lhs), std::get<1>(lhs), std::get<2>(lhs), std::get<3>(lhs), std::get<4>(lhs),
|
||||
rhs.bettor_id, rhs.betting_market_id, rhs.back_or_lay, rhs.backer_multiplier, rhs.id);
|
||||
}
|
||||
template<typename T0, typename T1, typename T2, typename T3, typename T4>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0, T1, T2, T3, T4>& rhs) const
|
||||
{
|
||||
return compare(lhs.bettor_id, lhs.betting_market_id, lhs.back_or_lay, lhs.backer_multiplier, lhs.id,
|
||||
std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs), std::get<3>(rhs), std::get<4>(rhs));
|
||||
}
|
||||
bool compare(const account_id_type& lhs_bettor_id, const account_id_type& rhs_bettor_id) const
|
||||
{
|
||||
return lhs_bettor_id < rhs_bettor_id;
|
||||
}
|
||||
bool compare(const account_id_type& lhs_bettor_id, const betting_market_id_type& lhs_betting_market_id,
|
||||
const account_id_type& rhs_bettor_id, const betting_market_id_type& rhs_betting_market_id) const
|
||||
{
|
||||
if (lhs_bettor_id < rhs_bettor_id)
|
||||
return true;
|
||||
if (lhs_bettor_id > rhs_bettor_id)
|
||||
return false;
|
||||
return lhs_betting_market_id < rhs_betting_market_id;
|
||||
}
|
||||
|
||||
bool compare(const account_id_type& lhs_bettor_id, const betting_market_id_type& lhs_betting_market_id,
|
||||
bet_type lhs_bet_type,
|
||||
const account_id_type& rhs_bettor_id, const betting_market_id_type& rhs_betting_market_id,
|
||||
bet_type rhs_bet_type) const
|
||||
{
|
||||
if (lhs_bettor_id < rhs_bettor_id)
|
||||
return true;
|
||||
if (lhs_bettor_id > rhs_bettor_id)
|
||||
return false;
|
||||
if (lhs_betting_market_id < rhs_betting_market_id)
|
||||
return true;
|
||||
if (lhs_betting_market_id > rhs_betting_market_id)
|
||||
return false;
|
||||
return lhs_bet_type < rhs_bet_type;
|
||||
}
|
||||
bool compare(const account_id_type& lhs_bettor_id, const betting_market_id_type& lhs_betting_market_id,
|
||||
bet_type lhs_bet_type,
|
||||
bet_multiplier_type lhs_backer_multiplier,
|
||||
const account_id_type& rhs_bettor_id, const betting_market_id_type& rhs_betting_market_id,
|
||||
bet_type rhs_bet_type,
|
||||
bet_multiplier_type rhs_backer_multiplier) const
|
||||
{
|
||||
if (lhs_bettor_id < rhs_bettor_id)
|
||||
return true;
|
||||
if (lhs_bettor_id > rhs_bettor_id)
|
||||
return false;
|
||||
if (lhs_betting_market_id < rhs_betting_market_id)
|
||||
return true;
|
||||
if (lhs_betting_market_id > rhs_betting_market_id)
|
||||
return false;
|
||||
if (lhs_bet_type < rhs_bet_type)
|
||||
return true;
|
||||
if (lhs_bet_type > rhs_bet_type)
|
||||
return false;
|
||||
if (lhs_bet_type == bet_type::back)
|
||||
return lhs_backer_multiplier < rhs_backer_multiplier;
|
||||
else
|
||||
return lhs_backer_multiplier > rhs_backer_multiplier;
|
||||
}
|
||||
bool compare(const account_id_type& lhs_bettor_id, const betting_market_id_type& lhs_betting_market_id,
|
||||
bet_type lhs_bet_type,
|
||||
bet_multiplier_type lhs_backer_multiplier, const bet_id_type& lhs_bet_id,
|
||||
const account_id_type& rhs_bettor_id, const betting_market_id_type& rhs_betting_market_id,
|
||||
bet_type rhs_bet_type,
|
||||
bet_multiplier_type rhs_backer_multiplier, const bet_id_type& rhs_bet_id) const
|
||||
{
|
||||
if (lhs_bettor_id < rhs_bettor_id)
|
||||
return true;
|
||||
if (lhs_bettor_id > rhs_bettor_id)
|
||||
return false;
|
||||
if (lhs_betting_market_id < rhs_betting_market_id)
|
||||
return true;
|
||||
if (lhs_betting_market_id > rhs_betting_market_id)
|
||||
return false;
|
||||
if (lhs_bet_type < rhs_bet_type)
|
||||
return true;
|
||||
if (lhs_bet_type > rhs_bet_type)
|
||||
return false;
|
||||
if (lhs_backer_multiplier < rhs_backer_multiplier)
|
||||
return lhs_bet_type == bet_type::back;
|
||||
if (lhs_backer_multiplier > rhs_backer_multiplier)
|
||||
return lhs_bet_type == bet_type::lay;
|
||||
|
||||
return lhs_bet_id < rhs_bet_id;
|
||||
}
|
||||
};
|
||||
|
||||
struct by_odds {};
|
||||
struct by_bettor_and_odds {};
|
||||
typedef multi_index_container<
|
||||
bet_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag<by_odds>, identity<bet_object>, compare_bet_by_odds >,
|
||||
ordered_unique< tag<by_bettor_and_odds>, identity<bet_object>, compare_bet_by_bettor_then_odds > > > bet_object_multi_index_type;
|
||||
typedef generic_index<bet_object, bet_object_multi_index_type> bet_object_index;
|
||||
|
||||
struct by_bettor_betting_market{};
|
||||
struct by_betting_market_bettor{};
|
||||
typedef multi_index_container<
|
||||
betting_market_position_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag<by_bettor_betting_market>,
|
||||
composite_key<
|
||||
betting_market_position_object,
|
||||
member<betting_market_position_object, account_id_type, &betting_market_position_object::bettor_id>,
|
||||
member<betting_market_position_object, betting_market_id_type, &betting_market_position_object::betting_market_id> > >,
|
||||
ordered_unique< tag<by_betting_market_bettor>,
|
||||
composite_key<
|
||||
betting_market_position_object,
|
||||
member<betting_market_position_object, betting_market_id_type, &betting_market_position_object::betting_market_id>,
|
||||
member<betting_market_position_object, account_id_type, &betting_market_position_object::bettor_id> > >
|
||||
> > betting_market_position_multi_index_type;
|
||||
|
||||
typedef generic_index<betting_market_position_object, betting_market_position_multi_index_type> betting_market_position_index;
|
||||
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator<<( Stream& s, const betting_market_object& betting_market_obj )
|
||||
{
|
||||
// pack all fields exposed in the header in the usual way
|
||||
// instead of calling the derived pack, just serialize the one field in the base class
|
||||
// fc::raw::pack<Stream, const graphene::db::abstract_object<betting_market_object> >(s, betting_market_obj);
|
||||
fc::raw::pack(s, betting_market_obj.id);
|
||||
fc::raw::pack(s, betting_market_obj.group_id);
|
||||
fc::raw::pack(s, betting_market_obj.description);
|
||||
fc::raw::pack(s, betting_market_obj.payout_condition);
|
||||
fc::raw::pack(s, betting_market_obj.resolution);
|
||||
|
||||
// fc::raw::pack the contents hidden in the impl class
|
||||
std::ostringstream stream;
|
||||
betting_market_obj.pack_impl(stream);
|
||||
std::string stringified_stream(stream.str());
|
||||
fc::raw::pack(s, stream.str());
|
||||
|
||||
return s;
|
||||
}
|
||||
template<typename Stream>
|
||||
inline Stream& operator>>( Stream& s, betting_market_object& betting_market_obj )
|
||||
{
|
||||
// unpack all fields exposed in the header in the usual way
|
||||
//fc::raw::unpack<Stream, graphene::db::abstract_object<betting_market_object> >(s, betting_market_obj);
|
||||
fc::raw::unpack(s, betting_market_obj.id);
|
||||
fc::raw::unpack(s, betting_market_obj.group_id);
|
||||
fc::raw::unpack(s, betting_market_obj.description);
|
||||
fc::raw::unpack(s, betting_market_obj.payout_condition);
|
||||
fc::raw::unpack(s, betting_market_obj.resolution);
|
||||
|
||||
// fc::raw::unpack the contents hidden in the impl class
|
||||
std::string stringified_stream;
|
||||
fc::raw::unpack(s, stringified_stream);
|
||||
std::istringstream stream(stringified_stream);
|
||||
betting_market_obj.unpack_impl(stream);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator<<( Stream& s, const betting_market_group_object& betting_market_group_obj )
|
||||
{
|
||||
// pack all fields exposed in the header in the usual way
|
||||
// instead of calling the derived pack, just serialize the one field in the base class
|
||||
// fc::raw::pack<Stream, const graphene::db::abstract_object<betting_market_group_object> >(s, betting_market_group_obj);
|
||||
fc::raw::pack(s, betting_market_group_obj.id);
|
||||
fc::raw::pack(s, betting_market_group_obj.description);
|
||||
fc::raw::pack(s, betting_market_group_obj.event_id);
|
||||
fc::raw::pack(s, betting_market_group_obj.rules_id);
|
||||
fc::raw::pack(s, betting_market_group_obj.asset_id);
|
||||
fc::raw::pack(s, betting_market_group_obj.total_matched_bets_amount);
|
||||
fc::raw::pack(s, betting_market_group_obj.never_in_play);
|
||||
fc::raw::pack(s, betting_market_group_obj.delay_before_settling);
|
||||
fc::raw::pack(s, betting_market_group_obj.settling_time);
|
||||
// fc::raw::pack the contents hidden in the impl class
|
||||
std::ostringstream stream;
|
||||
betting_market_group_obj.pack_impl(stream);
|
||||
std::string stringified_stream(stream.str());
|
||||
fc::raw::pack(s, stream.str());
|
||||
|
||||
return s;
|
||||
}
|
||||
template<typename Stream>
|
||||
inline Stream& operator>>( Stream& s, betting_market_group_object& betting_market_group_obj )
|
||||
{
|
||||
// unpack all fields exposed in the header in the usual way
|
||||
//fc::raw::unpack<Stream, graphene::db::abstract_object<betting_market_group_object> >(s, betting_market_group_obj);
|
||||
fc::raw::unpack(s, betting_market_group_obj.id);
|
||||
fc::raw::unpack(s, betting_market_group_obj.description);
|
||||
fc::raw::unpack(s, betting_market_group_obj.event_id);
|
||||
fc::raw::unpack(s, betting_market_group_obj.rules_id);
|
||||
fc::raw::unpack(s, betting_market_group_obj.asset_id);
|
||||
fc::raw::unpack(s, betting_market_group_obj.total_matched_bets_amount);
|
||||
fc::raw::unpack(s, betting_market_group_obj.never_in_play);
|
||||
fc::raw::unpack(s, betting_market_group_obj.delay_before_settling);
|
||||
fc::raw::unpack(s, betting_market_group_obj.settling_time);
|
||||
|
||||
// fc::raw::unpack the contents hidden in the impl class
|
||||
std::string stringified_stream;
|
||||
fc::raw::unpack(s, stringified_stream);
|
||||
std::istringstream stream(stringified_stream);
|
||||
betting_market_group_obj.unpack_impl(stream);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_rules_object, (graphene::db::object), (name)(description) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_group_object, (graphene::db::object), (description) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_object, (graphene::db::object), (group_id) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::bet_object, (graphene::db::object), (bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(back_or_lay)(end_of_delay) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_position_object, (graphene::db::object), (bettor_id)(betting_market_id)(pay_if_payout_condition)(pay_if_not_payout_condition)(pay_if_canceled)(pay_if_not_canceled)(fees_collected) )
|
||||
|
|
@ -23,8 +23,8 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#define GRAPHENE_SYMBOL "BTS"
|
||||
#define GRAPHENE_ADDRESS_PREFIX "BTS"
|
||||
#define GRAPHENE_SYMBOL "PPY"
|
||||
#define GRAPHENE_ADDRESS_PREFIX "PPY"
|
||||
|
||||
#define GRAPHENE_MIN_ACCOUNT_NAME_LENGTH 1
|
||||
#define GRAPHENE_MAX_ACCOUNT_NAME_LENGTH 63
|
||||
|
|
@ -113,6 +113,9 @@
|
|||
|
||||
#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) \
|
||||
|
|
@ -126,6 +129,10 @@
|
|||
| (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)
|
||||
|
|
@ -144,7 +151,7 @@
|
|||
#define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4
|
||||
#define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3
|
||||
|
||||
#define GRAPHENE_CURRENT_DB_VERSION "BTS2.8"
|
||||
#define GRAPHENE_CURRENT_DB_VERSION "BTS2.9"
|
||||
|
||||
#define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT)
|
||||
|
||||
|
|
@ -164,8 +171,59 @@
|
|||
#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 GRAPHENE_RAKE_FEE_ACCOUNT_ID (graphene::chain::account_id_type(6))
|
||||
#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 GRAPHENE_DEFAULT_RAKE_FEE_PERCENTAGE (3*GRAPHENE_1_PERCENT)
|
||||
|
||||
/**
|
||||
* Betting-related constants.
|
||||
*
|
||||
* We store bet multipliers as fixed-precision uint32_t. These values are
|
||||
* the maximum power-of-ten bet we can have on a "symmetric" market:
|
||||
* (decimal) 1.0001 - 10001
|
||||
* (fractional) 1:10000 - 10000:1
|
||||
*/
|
||||
///@{
|
||||
/// betting odds (multipliers) are stored as fixed-precision, divide by this to get the actual multiplier
|
||||
#define GRAPHENE_BETTING_ODDS_PRECISION 10000
|
||||
/// the smallest bet multiplier we will accept
|
||||
#define GRAPHENE_BETTING_MIN_MULTIPLIER 10001
|
||||
/// the largest bet multiplier we will accept
|
||||
#define GRAPHENE_BETTING_MAX_MULTIPLIER 100010000
|
||||
///@}
|
||||
#define GRAPHENE_DEFAULT_MIN_BET_MULTIPLIER 10100
|
||||
#define GRAPHENE_DEFAULT_MAX_BET_MULTIPLIER 10000000
|
||||
#define GRAPHENE_DEFAULT_PERMITTED_BETTING_ODDS_INCREMENTS { { 20000, 100}, /* <= 2: 0.01 */ \
|
||||
{ 30000, 200}, /* <= 3: 0.02 */ \
|
||||
{ 40000, 500}, /* <= 4: 0.05 */ \
|
||||
{ 60000, 1000}, /* <= 6: 0.10 */ \
|
||||
{ 100000, 2000}, /* <= 10: 0.20 */ \
|
||||
{ 200000, 5000}, /* <= 20: 0.50 */ \
|
||||
{ 300000, 10000}, /* <= 30: 1.00 */ \
|
||||
{ 500000, 20000}, /* <= 50: 2.00 */ \
|
||||
{ 1000000, 50000}, /* <= 100: 5.00 */ \
|
||||
{ 10000000, 100000} } /* <= 1000: 10.00 */
|
||||
#define GRAPHENE_DEFAULT_BETTING_PERCENT_FEE (2 * GRAPHENE_1_PERCENT)
|
||||
#define GRAPHENE_DEFAULT_LIVE_BETTING_DELAY_TIME 5 // seconds
|
||||
#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
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@
|
|||
#include <graphene/db/simple_index.hpp>
|
||||
#include <fc/signals.hpp>
|
||||
|
||||
#include <fc/crypto/hash_ctr_rng.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/protocol.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
|
|
@ -169,8 +171,18 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
uint32_t push_applied_operation( const operation& op );
|
||||
void set_applied_operation_result( uint32_t op_id, const operation_result& r );
|
||||
|
||||
// most plugins should use the const version of get_applied_operations
|
||||
const vector<optional< operation_history_object > >& get_applied_operations()const;
|
||||
|
||||
// the account_history plugin uses the non-const version. When it decides to track an
|
||||
// operation and assigns an operation_id to it, it will store that id into the operation
|
||||
// history object so other plugins that evaluate later can reference it.
|
||||
vector<optional< operation_history_object > >& get_applied_operations();
|
||||
|
||||
// the bookie plugin depends on change notifications that are skipped during normal replays
|
||||
void force_slow_replays();
|
||||
|
||||
string to_pretty_string( const asset& a )const;
|
||||
|
||||
/**
|
||||
|
|
@ -193,12 +205,18 @@ namespace graphene { namespace chain {
|
|||
* Emitted After a block has been applied and committed. The callback
|
||||
* should not yield and should execute quickly.
|
||||
*/
|
||||
fc::signal<void(const vector<object_id_type>&)> changed_objects;
|
||||
fc::signal<void(const vector<object_id_type>&, const flat_set<account_id_type>&)> new_objects;
|
||||
|
||||
/**
|
||||
* Emitted After a block has been applied and committed. The callback
|
||||
* should not yield and should execute quickly.
|
||||
*/
|
||||
fc::signal<void(const vector<object_id_type>&, const flat_set<account_id_type>&)> changed_objects;
|
||||
|
||||
/** this signal is emitted any time an object is removed and contains a
|
||||
* pointer to the last value of every object that was removed.
|
||||
*/
|
||||
fc::signal<void(const vector<const object*>&)> removed_objects;
|
||||
fc::signal<void(const vector<object_id_type>&, const vector<const object*>&, const flat_set<account_id_type>&)> removed_objects;
|
||||
|
||||
//////////////////// db_witness_schedule.cpp ////////////////////
|
||||
|
||||
|
|
@ -238,7 +256,9 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
uint32_t get_slot_at_time(fc::time_point_sec when)const;
|
||||
|
||||
vector<witness_id_type> get_near_witness_schedule()const;
|
||||
void update_witness_schedule();
|
||||
void update_witness_schedule(const signed_block& next_block);
|
||||
|
||||
//////////////////// db_getter.cpp ////////////////////
|
||||
|
||||
|
|
@ -250,6 +270,8 @@ namespace graphene { namespace chain {
|
|||
const node_property_object& get_node_properties()const;
|
||||
const fee_schedule& current_fee_schedule()const;
|
||||
|
||||
uint64_t get_random_bits( uint64_t bound );
|
||||
|
||||
time_point_sec head_block_time()const;
|
||||
uint32_t head_block_num()const;
|
||||
block_id_type head_block_id()const;
|
||||
|
|
@ -322,6 +344,8 @@ namespace graphene { namespace chain {
|
|||
//////////////////// db_debug.cpp ////////////////////
|
||||
|
||||
void debug_dump();
|
||||
void apply_debug_updates();
|
||||
void debug_update( const fc::variant_object& update );
|
||||
|
||||
//////////////////// db_market.cpp ////////////////////
|
||||
|
||||
|
|
@ -361,6 +385,28 @@ namespace graphene { namespace chain {
|
|||
asset max_settlement);
|
||||
///@}
|
||||
|
||||
//////////////////// db_bet.cpp ////////////////////
|
||||
|
||||
/// @{ @group Betting Market Helpers
|
||||
void cancel_bet(const bet_object& bet, bool create_virtual_op = true);
|
||||
void cancel_all_unmatched_bets_on_betting_market(const betting_market_object& betting_market);
|
||||
void cancel_all_unmatched_bets_on_betting_market_group(const betting_market_group_object& betting_market_group);
|
||||
void validate_betting_market_group_resolutions(const betting_market_group_object& betting_market_group,
|
||||
const std::map<betting_market_id_type, betting_market_resolution_type>& resolutions);
|
||||
void resolve_betting_market_group(const betting_market_group_object& betting_market_group,
|
||||
const std::map<betting_market_id_type, betting_market_resolution_type>& resolutions);
|
||||
void settle_betting_market_group(const betting_market_group_object& betting_market_group);
|
||||
/**
|
||||
* @brief Process a new bet
|
||||
* @param new_bet_object The new bet to process
|
||||
* @return true if order was completely filled; false otherwise
|
||||
*
|
||||
* This function takes a new bet and attempts to match it with existing
|
||||
* bets already on the books.
|
||||
*/
|
||||
bool place_bet(const bet_object& new_bet_object);
|
||||
///@}
|
||||
|
||||
/**
|
||||
* @return true if the order was completely filled and thus freed.
|
||||
*/
|
||||
|
|
@ -427,11 +473,14 @@ namespace graphene { namespace chain {
|
|||
void update_signing_witness(const witness_object& signing_witness, const signed_block& new_block);
|
||||
void update_last_irreversible_block();
|
||||
void clear_expired_transactions();
|
||||
void place_delayed_bets();
|
||||
void clear_expired_proposals();
|
||||
void clear_expired_orders();
|
||||
void update_expired_feeds();
|
||||
void update_maintenance_flag( bool new_maintenance_flag );
|
||||
void update_withdraw_permissions();
|
||||
void update_tournaments();
|
||||
void update_betting_markets(fc::time_point_sec current_block_time);
|
||||
bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true );
|
||||
|
||||
///Steps performed only at maintenance intervals
|
||||
|
|
@ -487,6 +536,8 @@ namespace graphene { namespace chain {
|
|||
flat_map<uint32_t,block_id_type> _checkpoints;
|
||||
|
||||
node_property_object _node_property_object;
|
||||
fc::hash_ctr_rng<secret_hash_type, 20> _random_number_generator;
|
||||
bool _slow_replays = false;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
|
|
|
|||
65
libraries/chain/include/graphene/chain/event_evaluator.hpp
Normal file
65
libraries/chain/include/graphene/chain/event_evaluator.hpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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 event_create_evaluator : public evaluator<event_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef event_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const event_create_operation& o );
|
||||
object_id_type do_apply( const event_create_operation& o );
|
||||
private:
|
||||
event_group_id_type event_group_id;
|
||||
};
|
||||
|
||||
class event_update_evaluator : public evaluator<event_update_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef event_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const event_update_operation& o );
|
||||
void_result do_apply( const event_update_operation& o );
|
||||
private:
|
||||
event_group_id_type event_group_id;
|
||||
};
|
||||
|
||||
class event_update_status_evaluator : public evaluator<event_update_status_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef event_update_status_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const event_update_status_operation& o );
|
||||
void_result do_apply( const event_update_status_operation& o );
|
||||
private:
|
||||
const event_object* _event_to_update = nullptr;
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 event_group_create_evaluator : public evaluator<event_group_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef event_group_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const event_group_create_operation& o );
|
||||
object_id_type do_apply( const event_group_create_operation& o );
|
||||
|
||||
private:
|
||||
sport_id_type sport_id;
|
||||
};
|
||||
|
||||
class event_group_update_evaluator : public evaluator<event_group_update_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef event_group_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const event_group_update_operation& o );
|
||||
void_result do_apply( const event_group_update_operation& o );
|
||||
|
||||
private:
|
||||
sport_id_type sport_id;
|
||||
};
|
||||
} } // graphene::chain
|
||||
|
|
@ -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 <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class database;
|
||||
|
||||
struct by_sport_id;
|
||||
|
||||
class event_group_object : public graphene::db::abstract_object< event_group_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = event_group_object_type;
|
||||
|
||||
internationalized_string_type name;
|
||||
sport_id_type sport_id;
|
||||
};
|
||||
|
||||
typedef multi_index_container<
|
||||
event_group_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag<by_sport_id>, member< event_group_object, sport_id_type, &event_group_object::sport_id > > >
|
||||
> event_group_object_multi_index_type;
|
||||
|
||||
typedef generic_index<event_group_object, event_group_object_multi_index_type> event_group_object_index;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::event_group_object, (graphene::db::object), (name)(sport_id) )
|
||||
153
libraries/chain/include/graphene/chain/event_object.hpp
Normal file
153
libraries/chain/include/graphene/chain/event_object.hpp
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* 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>
|
||||
#include <graphene/chain/protocol/event.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class event_object;
|
||||
} }
|
||||
|
||||
namespace fc {
|
||||
void to_variant(const graphene::chain::event_object& event_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::event_object& event_obj);
|
||||
} //end namespace fc
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class database;
|
||||
|
||||
class event_object : public graphene::db::abstract_object< event_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = event_object_type;
|
||||
|
||||
event_object();
|
||||
event_object(const event_object& rhs);
|
||||
~event_object();
|
||||
event_object& operator=(const event_object& rhs);
|
||||
|
||||
internationalized_string_type name;
|
||||
|
||||
internationalized_string_type season;
|
||||
|
||||
optional<time_point_sec> start_time;
|
||||
|
||||
event_group_id_type event_group_id;
|
||||
|
||||
bool at_least_one_betting_market_group_settled;
|
||||
|
||||
event_status get_status() const;
|
||||
vector<string> scores;
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const event_object& event_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, event_object& event_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::event_object& event_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::event_object& event_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
|
||||
void on_upcoming_event(database& db);
|
||||
void on_in_progress_event(database& db);
|
||||
void on_frozen_event(database& db);
|
||||
void on_finished_event(database& db);
|
||||
void on_canceled_event(database& db);
|
||||
void on_settled_event(database& db);
|
||||
void on_betting_market_group_resolved(database& db, betting_market_group_id_type resolved_group, bool was_canceled);
|
||||
void on_betting_market_group_closed(database& db, betting_market_group_id_type closed_group);
|
||||
void dispatch_new_status(database& db, event_status new_status);
|
||||
private:
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
struct by_event_group_id;
|
||||
typedef multi_index_container<
|
||||
event_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag<by_event_group_id>, member< event_object, event_group_id_type, &event_object::event_group_id > > > > event_object_multi_index_type;
|
||||
|
||||
typedef generic_index<event_object, event_object_multi_index_type> event_object_index;
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator<<( Stream& s, const event_object& event_obj )
|
||||
{
|
||||
fc_elog(fc::logger::get("event"), "In event_obj to_raw");
|
||||
// pack all fields exposed in the header in the usual way
|
||||
// instead of calling the derived pack, just serialize the one field in the base class
|
||||
// fc::raw::pack<Stream, const graphene::db::abstract_object<event_object> >(s, event_obj);
|
||||
fc::raw::pack(s, event_obj.id);
|
||||
fc::raw::pack(s, event_obj.name);
|
||||
fc::raw::pack(s, event_obj.season);
|
||||
fc::raw::pack(s, event_obj.start_time);
|
||||
fc::raw::pack(s, event_obj.event_group_id);
|
||||
fc::raw::pack(s, event_obj.at_least_one_betting_market_group_settled);
|
||||
fc::raw::pack(s, event_obj.scores);
|
||||
|
||||
// fc::raw::pack the contents hidden in the impl class
|
||||
std::ostringstream stream;
|
||||
event_obj.pack_impl(stream);
|
||||
std::string stringified_stream(stream.str());
|
||||
fc::raw::pack(s, stream.str());
|
||||
|
||||
return s;
|
||||
}
|
||||
template<typename Stream>
|
||||
inline Stream& operator>>( Stream& s, event_object& event_obj )
|
||||
{
|
||||
fc_elog(fc::logger::get("event"), "In event_obj from_raw");
|
||||
// unpack all fields exposed in the header in the usual way
|
||||
//fc::raw::unpack<Stream, graphene::db::abstract_object<event_object> >(s, event_obj);
|
||||
fc::raw::unpack(s, event_obj.id);
|
||||
fc::raw::unpack(s, event_obj.name);
|
||||
fc::raw::unpack(s, event_obj.season);
|
||||
fc::raw::unpack(s, event_obj.start_time);
|
||||
fc::raw::unpack(s, event_obj.event_group_id);
|
||||
fc::raw::unpack(s, event_obj.at_least_one_betting_market_group_settled);
|
||||
fc::raw::unpack(s, event_obj.scores);
|
||||
|
||||
// fc::raw::unpack the contents hidden in the impl class
|
||||
std::string stringified_stream;
|
||||
fc::raw::unpack(s, stringified_stream);
|
||||
std::istringstream stream(stringified_stream);
|
||||
event_obj.unpack_impl(stream);
|
||||
|
||||
return s;
|
||||
}
|
||||
} } // graphene::chain
|
||||
FC_REFLECT(graphene::chain::event_object, (name))
|
||||
|
||||
147
libraries/chain/include/graphene/chain/game_object.hpp
Normal file
147
libraries/chain/include/graphene/chain/game_object.hpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/rock_paper_scissors.hpp>
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
#include <graphene/db/flat_index.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class game_object;
|
||||
} }
|
||||
|
||||
namespace fc {
|
||||
void to_variant(const graphene::chain::game_object& game_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::game_object& game_obj);
|
||||
} //end namespace fc
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class database;
|
||||
using namespace graphene::db;
|
||||
|
||||
enum class game_state
|
||||
{
|
||||
game_in_progress,
|
||||
expecting_commit_moves,
|
||||
expecting_reveal_moves,
|
||||
game_complete
|
||||
};
|
||||
|
||||
class game_object : public graphene::db::abstract_object<game_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = game_object_type;
|
||||
|
||||
match_id_type match_id;
|
||||
|
||||
vector<account_id_type> players;
|
||||
|
||||
flat_set<account_id_type> winners;
|
||||
|
||||
game_specific_details game_details;
|
||||
|
||||
fc::optional<time_point_sec> next_timeout;
|
||||
|
||||
game_state get_state() const;
|
||||
|
||||
game_object();
|
||||
game_object(const game_object& rhs);
|
||||
~game_object();
|
||||
game_object& operator=(const game_object& rhs);
|
||||
|
||||
void evaluate_move_operation(const database& db, const game_move_operation& op) const;
|
||||
void make_automatic_moves(database& db);
|
||||
void determine_winner(database& db);
|
||||
|
||||
void on_move(database& db, const game_move_operation& op);
|
||||
void on_timeout(database& db);
|
||||
void start_game(database& db, const std::vector<account_id_type>& players);
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const game_object& game_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, game_object& game_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::game_object& game_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::game_object& game_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
struct by_next_timeout {};
|
||||
typedef multi_index_container<
|
||||
game_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag<by_next_timeout>,
|
||||
composite_key<game_object,
|
||||
member<game_object, optional<time_point_sec>, &game_object::next_timeout>,
|
||||
member<object, object_id_type, &object::id> > > >
|
||||
> game_object_multi_index_type;
|
||||
typedef generic_index<game_object, game_object_multi_index_type> game_index;
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator<<( Stream& s, const game_object& game_obj )
|
||||
{
|
||||
// pack all fields exposed in the header in the usual way
|
||||
// instead of calling the derived pack, just serialize the one field in the base class
|
||||
// fc::raw::pack<Stream, const graphene::db::abstract_object<game_object> >(s, game_obj);
|
||||
fc::raw::pack(s, game_obj.id);
|
||||
fc::raw::pack(s, game_obj.match_id);
|
||||
fc::raw::pack(s, game_obj.players);
|
||||
fc::raw::pack(s, game_obj.winners);
|
||||
fc::raw::pack(s, game_obj.game_details);
|
||||
fc::raw::pack(s, game_obj.next_timeout);
|
||||
|
||||
// fc::raw::pack the contents hidden in the impl class
|
||||
std::ostringstream stream;
|
||||
game_obj.pack_impl(stream);
|
||||
std::string stringified_stream(stream.str());
|
||||
fc::raw::pack(s, stream.str());
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator>>( Stream& s, game_object& game_obj )
|
||||
{
|
||||
// unpack all fields exposed in the header in the usual way
|
||||
//fc::raw::unpack<Stream, graphene::db::abstract_object<game_object> >(s, game_obj);
|
||||
fc::raw::unpack(s, game_obj.id);
|
||||
fc::raw::unpack(s, game_obj.match_id);
|
||||
fc::raw::unpack(s, game_obj.players);
|
||||
fc::raw::unpack(s, game_obj.winners);
|
||||
fc::raw::unpack(s, game_obj.game_details);
|
||||
fc::raw::unpack(s, game_obj.next_timeout);
|
||||
|
||||
// fc::raw::unpack the contents hidden in the impl class
|
||||
std::string stringified_stream;
|
||||
fc::raw::unpack(s, stringified_stream);
|
||||
std::istringstream stream(stringified_stream);
|
||||
game_obj.unpack_impl(stream);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::game_state,
|
||||
(game_in_progress)
|
||||
(expecting_commit_moves)
|
||||
(expecting_reveal_moves)
|
||||
(game_complete))
|
||||
|
||||
//FC_REFLECT_TYPENAME(graphene::chain::game_object) // manually serialized
|
||||
FC_REFLECT(graphene::chain::game_object, (players))
|
||||
|
||||
|
||||
|
|
@ -52,6 +52,46 @@ struct genesis_state_type {
|
|||
public_key_type active_key;
|
||||
bool is_lifetime_member = false;
|
||||
};
|
||||
struct initial_bts_account_type {
|
||||
struct initial_authority {
|
||||
uint32_t weight_threshold;
|
||||
flat_map<string, weight_type> account_auths; // uses account name instead of account id
|
||||
flat_map<public_key_type, weight_type> key_auths;
|
||||
flat_map<address, weight_type> address_auths;
|
||||
};
|
||||
struct initial_cdd_vesting_policy {
|
||||
uint32_t vesting_seconds;
|
||||
fc::uint128_t coin_seconds_earned;
|
||||
fc::time_point_sec start_claim;
|
||||
fc::time_point_sec coin_seconds_earned_last_update;
|
||||
};
|
||||
struct initial_linear_vesting_policy {
|
||||
fc::time_point_sec begin_timestamp;
|
||||
uint32_t vesting_cliff_seconds;
|
||||
uint32_t vesting_duration_seconds;
|
||||
share_type begin_balance;
|
||||
};
|
||||
struct initial_vesting_balance {
|
||||
string asset_symbol;
|
||||
share_type amount;
|
||||
std::string policy_type; // either "linear" or "cdd"
|
||||
fc::variant policy; // either an initial_cdd_vesting_policy or initial_linear_vesting_policy
|
||||
};
|
||||
initial_bts_account_type(const string& name = string(),
|
||||
const initial_authority& owner_authority = initial_authority(),
|
||||
const initial_authority& active_authority = initial_authority(),
|
||||
const share_type& core_balance = share_type())
|
||||
: name(name),
|
||||
owner_authority(owner_authority),
|
||||
active_authority(active_authority),
|
||||
core_balance(core_balance)
|
||||
{}
|
||||
string name;
|
||||
initial_authority owner_authority;
|
||||
initial_authority active_authority;
|
||||
share_type core_balance;
|
||||
fc::optional<std::vector<initial_vesting_balance> > vesting_balances;
|
||||
};
|
||||
struct initial_asset_type {
|
||||
struct initial_collateral_position {
|
||||
address owner;
|
||||
|
|
@ -81,6 +121,7 @@ struct genesis_state_type {
|
|||
string asset_symbol;
|
||||
share_type amount;
|
||||
time_point_sec begin_timestamp;
|
||||
fc::optional<uint32_t> vesting_cliff_seconds;
|
||||
uint32_t vesting_duration_seconds = 0;
|
||||
share_type begin_balance;
|
||||
};
|
||||
|
|
@ -103,6 +144,7 @@ struct genesis_state_type {
|
|||
share_type max_core_supply = GRAPHENE_MAX_SHARE_SUPPLY;
|
||||
chain_parameters initial_parameters;
|
||||
immutable_chain_parameters immutable_parameters;
|
||||
vector<initial_bts_account_type> initial_bts_accounts;
|
||||
vector<initial_account_type> initial_accounts;
|
||||
vector<initial_asset_type> initial_assets;
|
||||
vector<initial_balance_type> initial_balances;
|
||||
|
|
@ -139,7 +181,7 @@ FC_REFLECT(graphene::chain::genesis_state_type::initial_balance_type,
|
|||
(owner)(asset_symbol)(amount))
|
||||
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_vesting_balance_type,
|
||||
(owner)(asset_symbol)(amount)(begin_timestamp)(vesting_duration_seconds)(begin_balance))
|
||||
(owner)(asset_symbol)(amount)(begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds)(begin_balance))
|
||||
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_witness_type, (owner_name)(block_signing_key))
|
||||
|
||||
|
|
@ -147,8 +189,35 @@ FC_REFLECT(graphene::chain::genesis_state_type::initial_committee_member_type, (
|
|||
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_worker_type, (owner_name)(daily_pay))
|
||||
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_authority,
|
||||
(weight_threshold)
|
||||
(account_auths)
|
||||
(key_auths)
|
||||
(address_auths))
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_cdd_vesting_policy,
|
||||
(vesting_seconds)
|
||||
(coin_seconds_earned)
|
||||
(start_claim)
|
||||
(coin_seconds_earned_last_update))
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_linear_vesting_policy,
|
||||
(begin_timestamp)
|
||||
(vesting_cliff_seconds)
|
||||
(vesting_duration_seconds)
|
||||
(begin_balance))
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type::initial_vesting_balance,
|
||||
(asset_symbol)
|
||||
(amount)
|
||||
(policy_type)
|
||||
(policy))
|
||||
FC_REFLECT(graphene::chain::genesis_state_type::initial_bts_account_type,
|
||||
(name)
|
||||
(owner_authority)
|
||||
(active_authority)
|
||||
(core_balance)
|
||||
(vesting_balances))
|
||||
|
||||
FC_REFLECT(graphene::chain::genesis_state_type,
|
||||
(initial_timestamp)(max_core_supply)(initial_parameters)(initial_accounts)(initial_assets)(initial_balances)
|
||||
(initial_timestamp)(max_core_supply)(initial_parameters)(initial_bts_accounts)(initial_accounts)(initial_assets)(initial_balances)
|
||||
(initial_vesting_balances)(initial_active_witnesses)(initial_witness_candidates)
|
||||
(initial_committee_candidates)(initial_worker_candidates)
|
||||
(initial_chain_id)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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 {
|
||||
|
||||
class database;
|
||||
|
||||
class global_betting_statistics_object : public graphene::db::abstract_object< global_betting_statistics_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_global_betting_statistics_object_type;
|
||||
|
||||
uint32_t number_of_active_events;
|
||||
map<asset_id_type, share_type> total_amount_staked;
|
||||
};
|
||||
|
||||
typedef multi_index_container<
|
||||
global_betting_statistics_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > > > > global_betting_statistics_object_multi_index_type;
|
||||
typedef generic_index<global_betting_statistics_object, global_betting_statistics_object_multi_index_type> global_betting_statistics_object_index;
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::global_betting_statistics_object, (graphene::db::object), (number_of_active_events)(total_amount_staked) )
|
||||
|
|
@ -69,6 +69,7 @@ namespace graphene { namespace chain {
|
|||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_dynamic_global_property_object_type;
|
||||
|
||||
secret_hash_type random;
|
||||
uint32_t head_block_number = 0;
|
||||
block_id_type head_block_id;
|
||||
time_point_sec time;
|
||||
|
|
@ -125,6 +126,7 @@ namespace graphene { namespace chain {
|
|||
}}
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::db::object),
|
||||
(random)
|
||||
(head_block_number)
|
||||
(head_block_id)
|
||||
(time)
|
||||
|
|
|
|||
|
|
@ -120,6 +120,13 @@ class call_order_object : public abstract_object<call_order_object>
|
|||
share_type collateral; ///< call_price.base.asset_id, access via get_collateral
|
||||
share_type debt; ///< call_price.quote.asset_id, access via get_collateral
|
||||
price call_price; ///< Debt / Collateral
|
||||
|
||||
pair<asset_id_type,asset_id_type> get_market()const
|
||||
{
|
||||
auto tmp = std::make_pair( call_price.base.asset_id, call_price.quote.asset_id );
|
||||
if( tmp.first > tmp.second ) std::swap( tmp.first, tmp.second );
|
||||
return tmp;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
167
libraries/chain/include/graphene/chain/match_object.hpp
Normal file
167
libraries/chain/include/graphene/chain/match_object.hpp
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
#include <graphene/chain/rock_paper_scissors.hpp>
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
#include <graphene/db/flat_index.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class match_object;
|
||||
} }
|
||||
|
||||
namespace fc {
|
||||
void to_variant(const graphene::chain::match_object& match_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::match_object& match_obj);
|
||||
} //end namespace fc
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class database;
|
||||
using namespace graphene::db;
|
||||
|
||||
enum class match_state
|
||||
{
|
||||
waiting_on_previous_matches,
|
||||
match_in_progress,
|
||||
match_complete
|
||||
};
|
||||
|
||||
class match_object : public graphene::db::abstract_object<match_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = match_object_type;
|
||||
|
||||
tournament_id_type tournament_id;
|
||||
|
||||
/// The players in the match
|
||||
vector<account_id_type> players;
|
||||
|
||||
/// The list of games in the match
|
||||
/// Unlike tournaments where the list of matches is known at the start,
|
||||
/// the list of games will start with one game and grow until we have played
|
||||
/// enough games to declare a winner for the match.
|
||||
vector<game_id_type> games;
|
||||
|
||||
/// A list of the winners of each round of the game. This information is
|
||||
/// also stored in the game object, but is duplicated here to allow displaying
|
||||
/// information about a match without having to request all game objects
|
||||
vector<flat_set<account_id_type> > game_winners;
|
||||
|
||||
/// A count of the number of wins for each player
|
||||
vector<uint32_t> number_of_wins;
|
||||
|
||||
/// the total number of games that ended up in a tie/draw/stalemate
|
||||
uint32_t number_of_ties;
|
||||
|
||||
// If the match is not yet complete, this will be empty
|
||||
// If the match is in the "match_complete" state, it will contain the
|
||||
// list of winners.
|
||||
// For Rock-paper-scissors, there will be one winner, unless there is
|
||||
// a stalemate (in that case, there are no winners)
|
||||
flat_set<account_id_type> match_winners;
|
||||
|
||||
/// the time the match started
|
||||
time_point_sec start_time;
|
||||
|
||||
/// If the match has ended, the time it ended
|
||||
optional<time_point_sec> end_time;
|
||||
|
||||
match_object();
|
||||
match_object(const match_object& rhs);
|
||||
~match_object();
|
||||
match_object& operator=(const match_object& rhs);
|
||||
|
||||
match_state get_state() const;
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const match_object& match_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, match_object& match_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::match_object& match_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::match_object& match_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
void on_initiate_match(database& db);
|
||||
void on_game_complete(database& db, const game_object& game);
|
||||
game_id_type start_next_game(database& db, match_id_type match_id);
|
||||
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
typedef multi_index_container<
|
||||
match_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > > >
|
||||
> match_object_multi_index_type;
|
||||
typedef generic_index<match_object, match_object_multi_index_type> match_index;
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator<<( Stream& s, const match_object& match_obj )
|
||||
{
|
||||
// pack all fields exposed in the header in the usual way
|
||||
// instead of calling the derived pack, just serialize the one field in the base class
|
||||
// fc::raw::pack<Stream, const graphene::db::abstract_object<match_object> >(s, match_obj);
|
||||
fc::raw::pack(s, match_obj.id);
|
||||
fc::raw::pack(s, match_obj.tournament_id);
|
||||
fc::raw::pack(s, match_obj.players);
|
||||
fc::raw::pack(s, match_obj.games);
|
||||
fc::raw::pack(s, match_obj.game_winners);
|
||||
fc::raw::pack(s, match_obj.number_of_wins);
|
||||
fc::raw::pack(s, match_obj.number_of_ties);
|
||||
fc::raw::pack(s, match_obj.match_winners);
|
||||
fc::raw::pack(s, match_obj.start_time);
|
||||
fc::raw::pack(s, match_obj.end_time);
|
||||
|
||||
// fc::raw::pack the contents hidden in the impl class
|
||||
std::ostringstream stream;
|
||||
match_obj.pack_impl(stream);
|
||||
std::string stringified_stream(stream.str());
|
||||
fc::raw::pack(s, stream.str());
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator>>( Stream& s, match_object& match_obj )
|
||||
{
|
||||
// unpack all fields exposed in the header in the usual way
|
||||
//fc::raw::unpack<Stream, graphene::db::abstract_object<match_object> >(s, match_obj);
|
||||
fc::raw::unpack(s, match_obj.id);
|
||||
fc::raw::unpack(s, match_obj.tournament_id);
|
||||
fc::raw::unpack(s, match_obj.players);
|
||||
fc::raw::unpack(s, match_obj.games);
|
||||
fc::raw::unpack(s, match_obj.game_winners);
|
||||
fc::raw::unpack(s, match_obj.number_of_wins);
|
||||
fc::raw::unpack(s, match_obj.number_of_ties);
|
||||
fc::raw::unpack(s, match_obj.match_winners);
|
||||
fc::raw::unpack(s, match_obj.start_time);
|
||||
fc::raw::unpack(s, match_obj.end_time);
|
||||
|
||||
// fc::raw::unpack the contents hidden in the impl class
|
||||
std::string stringified_stream;
|
||||
fc::raw::unpack(s, stringified_stream);
|
||||
std::istringstream stream(stringified_stream);
|
||||
match_obj.unpack_impl(stream);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::match_state,
|
||||
(waiting_on_previous_matches)
|
||||
(match_in_progress)
|
||||
(match_complete))
|
||||
|
||||
//FC_REFLECT_TYPENAME(graphene::chain::match_object) // manually serialized
|
||||
FC_REFLECT(graphene::chain::match_object, (players))
|
||||
|
||||
|
|
@ -43,5 +43,6 @@ namespace graphene { namespace chain {
|
|||
~node_property_object(){}
|
||||
|
||||
uint32_t skip_flags = 0;
|
||||
std::map< block_id_type, std::vector< fc::variant_object > > debug_updates;
|
||||
};
|
||||
} } // graphene::chain
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -89,13 +90,48 @@ namespace graphene { namespace chain {
|
|||
public:
|
||||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_account_transaction_history_object_type;
|
||||
account_id_type account; /// the account this operation applies to
|
||||
operation_history_id_type operation_id;
|
||||
uint32_t sequence = 0; /// the operation position within the given account
|
||||
account_transaction_history_id_type next;
|
||||
|
||||
//std::pair<account_id_type,operation_history_id_type> account_op()const { return std::tie( account, operation_id ); }
|
||||
//std::pair<account_id_type,uint32_t> account_seq()const { return std::tie( account, sequence ); }
|
||||
};
|
||||
|
||||
struct by_id;
|
||||
struct by_seq;
|
||||
struct by_op;
|
||||
struct by_opid;
|
||||
typedef multi_index_container<
|
||||
account_transaction_history_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag<by_seq>,
|
||||
composite_key< account_transaction_history_object,
|
||||
member< account_transaction_history_object, account_id_type, &account_transaction_history_object::account>,
|
||||
member< account_transaction_history_object, uint32_t, &account_transaction_history_object::sequence>
|
||||
>
|
||||
>,
|
||||
ordered_unique< tag<by_op>,
|
||||
composite_key< account_transaction_history_object,
|
||||
member< account_transaction_history_object, account_id_type, &account_transaction_history_object::account>,
|
||||
member< account_transaction_history_object, operation_history_id_type, &account_transaction_history_object::operation_id>
|
||||
>
|
||||
>,
|
||||
ordered_non_unique< tag<by_opid>,
|
||||
member< account_transaction_history_object, operation_history_id_type, &account_transaction_history_object::operation_id>
|
||||
>
|
||||
>
|
||||
> account_transaction_history_multi_index_type;
|
||||
|
||||
typedef generic_index<account_transaction_history_object, account_transaction_history_multi_index_type> account_transaction_history_index;
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::operation_history_object, (graphene::chain::object),
|
||||
(op)(result)(block_num)(trx_in_block)(op_in_trx)(virtual_op) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::account_transaction_history_object, (graphene::chain::object),
|
||||
(operation_id)(next) )
|
||||
(account)(operation_id)(sequence)(next) )
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ class proposal_object : public abstract_object<proposal_object>
|
|||
flat_set<account_id_type> required_owner_approvals;
|
||||
flat_set<account_id_type> available_owner_approvals;
|
||||
flat_set<public_key_type> available_key_approvals;
|
||||
account_id_type proposer;
|
||||
|
||||
bool is_authorized_to_execute(database& db)const;
|
||||
};
|
||||
|
|
@ -93,4 +94,4 @@ typedef generic_index<proposal_object, proposal_multi_index_container> proposal_
|
|||
FC_REFLECT_DERIVED( graphene::chain::proposal_object, (graphene::chain::object),
|
||||
(expiration_time)(review_period_time)(proposed_transaction)(required_active_approvals)
|
||||
(available_active_approvals)(required_owner_approvals)(available_owner_approvals)
|
||||
(available_key_approvals) )
|
||||
(available_key_approvals)(proposer) )
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ namespace graphene { namespace chain {
|
|||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::account_options, (memo_key)(voting_account)(num_witness)(num_committee)(votes)(extensions))
|
||||
FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing)
|
||||
// FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing)
|
||||
FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing,
|
||||
(no_listing)(white_listed)(black_listed)(white_and_black_listed))
|
||||
|
||||
|
|
|
|||
|
|
@ -112,6 +112,53 @@ namespace graphene { namespace chain {
|
|||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The dividend_asset_options struct contains configurable options available only to dividend-paying assets.
|
||||
*
|
||||
* @note Changes to this struct will break protocol compatibility
|
||||
*/
|
||||
struct dividend_asset_options {
|
||||
/// Time when the next payout should occur.
|
||||
/// The payouts will happen on the maintenance interval at or after this time
|
||||
/// If this is set to null, there will be no payouts.
|
||||
fc::optional<fc::time_point_sec> next_payout_time;
|
||||
/// If payouts happen on a fixed schedule, this specifies the interval between
|
||||
/// payouts in seconds. After each payout, the next payout time will be incremented by
|
||||
/// this amount.
|
||||
/// If payout_interval is not set, the next payout (if any) will be the last until
|
||||
/// the options are updated again.
|
||||
fc::optional<uint32_t> payout_interval;
|
||||
/// Each dividend distribution incurs a fee that is based on the number of accounts
|
||||
/// that hold the dividend asset, not as a percentage of the amount paid out.
|
||||
/// This parameter prevents assets from being distributed unless the fee is less than
|
||||
/// the percentage here, to prevent a slow trickle of deposits to the account from being
|
||||
/// completely consumed.
|
||||
/// In other words, if you set this parameter to 10% and the fees work out to 100 BTS
|
||||
/// to share out, balances in the dividend distribution accounts will not be shared out
|
||||
/// if the balance is less than 10000 BTS.
|
||||
uint64_t minimum_fee_percentage;
|
||||
|
||||
/// Normally, pending dividend payments are calculated each maintenance interval in
|
||||
/// which there are balances in the dividend distribution account. At present, this
|
||||
/// is once per hour on the BitShares blockchain. If this is too often (too expensive
|
||||
/// in fees or to computationally-intensive for the blockchain) this can be increased.
|
||||
/// If you set this to, for example, one day, distributions will take place on even
|
||||
/// multiples of one day, allowing deposits to the distribution account to accumulate
|
||||
/// for 23 maintenance intervals and then computing the pending payouts on the 24th.
|
||||
///
|
||||
/// Payouts will always occur at the next payout time whether or not it falls on a
|
||||
/// multiple of the distribution interval, and the timer on the distribution interval
|
||||
/// are reset at payout time. So if you have the distribution interval at three days
|
||||
/// and the payout interval at one week, payouts will occur at days 3, 6, 7, 10, 13, 14...
|
||||
fc::optional<uint32_t> minimum_distribution_interval;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
/// Perform internal consistency checks.
|
||||
/// @throws fc::exception if any check fails
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup operations
|
||||
|
|
@ -236,6 +283,59 @@ namespace graphene { namespace chain {
|
|||
{ return 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Virtual op generated when a dividend asset pays out dividends
|
||||
*/
|
||||
struct asset_dividend_distribution_operation : public base_operation
|
||||
{
|
||||
asset_dividend_distribution_operation() {}
|
||||
asset_dividend_distribution_operation(const asset_id_type& dividend_asset_id,
|
||||
const account_id_type& account_id,
|
||||
const flat_set<asset>& amounts) :
|
||||
dividend_asset_id(dividend_asset_id),
|
||||
account_id(account_id),
|
||||
amounts(amounts)
|
||||
{}
|
||||
struct fee_parameters_type {
|
||||
/* note: this is a virtual op and there are no fees directly charged for it */
|
||||
|
||||
/* Whenever the system computes the pending dividend payments for an asset,
|
||||
* it charges the distribution_base_fee + distribution_fee_per_holder.
|
||||
* The computational cost of distributing the dividend payment is proportional
|
||||
* to the number of dividend holders the asset is divided up among.
|
||||
*/
|
||||
/** This fee is charged whenever the system schedules pending dividend
|
||||
* payments.
|
||||
*/
|
||||
uint64_t distribution_base_fee;
|
||||
/** This fee is charged (in addition to the distribution_base_fee) for each
|
||||
* user the dividend payment is shared out amongst
|
||||
*/
|
||||
uint32_t distribution_fee_per_holder;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
|
||||
/// The dividend-paying asset which triggered this payout
|
||||
asset_id_type dividend_asset_id;
|
||||
|
||||
/// The user account receiving the dividends
|
||||
account_id_type account_id;
|
||||
|
||||
/// The amounts received
|
||||
flat_set<asset> amounts;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return account_id; }
|
||||
void validate()const {
|
||||
FC_ASSERT( false, "virtual operation" );
|
||||
}
|
||||
|
||||
share_type calculate_fee(const fee_parameters_type& params)const
|
||||
{ return 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup operations
|
||||
*/
|
||||
|
|
@ -319,6 +419,35 @@ namespace graphene { namespace chain {
|
|||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update options specific to dividend-paying assets
|
||||
* @ingroup operations
|
||||
*
|
||||
* Dividend-paying assets have some options which are not relevant to other asset types.
|
||||
* This operation is used to update those options an an existing dividend-paying asset.
|
||||
* This can also be used to convert a non-dividend-paying asset into a dividend-paying
|
||||
* asset.
|
||||
*
|
||||
* @pre @ref issuer MUST be an existing account and MUST match asset_object::issuer on @ref asset_to_update
|
||||
* @pre @ref fee MUST be nonnegative, and @ref issuer MUST have a sufficient balance to pay it
|
||||
* @pre @ref new_options SHALL be internally consistent, as verified by @ref validate()
|
||||
* @post @ref asset_to_update will have dividend-specific options matching those of new_options
|
||||
*/
|
||||
struct asset_update_dividend_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 500 * GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
|
||||
asset fee;
|
||||
account_id_type issuer;
|
||||
asset_id_type asset_to_update;
|
||||
|
||||
dividend_asset_options new_options;
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return issuer; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update the set of feed-producing accounts for a BitAsset
|
||||
* @ingroup operations
|
||||
|
|
@ -462,6 +591,15 @@ FC_REFLECT( graphene::chain::asset_options,
|
|||
(description)
|
||||
(extensions)
|
||||
)
|
||||
|
||||
FC_REFLECT( graphene::chain::dividend_asset_options,
|
||||
(next_payout_time)
|
||||
(payout_interval)
|
||||
(minimum_fee_percentage)
|
||||
(minimum_distribution_interval)
|
||||
(extensions)
|
||||
)
|
||||
|
||||
FC_REFLECT( graphene::chain::bitasset_options,
|
||||
(feed_lifetime_sec)
|
||||
(minimum_feeds)
|
||||
|
|
@ -480,11 +618,12 @@ FC_REFLECT( graphene::chain::asset_settle_cancel_operation::fee_parameters_type,
|
|||
FC_REFLECT( graphene::chain::asset_fund_fee_pool_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::asset_update_operation::fee_parameters_type, (fee)(price_per_kbyte) )
|
||||
FC_REFLECT( graphene::chain::asset_update_bitasset_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::asset_update_dividend_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::asset_update_feed_producers_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::asset_publish_feed_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::asset_issue_operation::fee_parameters_type, (fee)(price_per_kbyte) )
|
||||
FC_REFLECT( graphene::chain::asset_reserve_operation::fee_parameters_type, (fee) )
|
||||
|
||||
FC_REFLECT( graphene::chain::asset_dividend_distribution_operation::fee_parameters_type, (distribution_base_fee)(distribution_fee_per_holder))
|
||||
|
||||
FC_REFLECT( graphene::chain::asset_create_operation,
|
||||
(fee)
|
||||
|
|
@ -511,6 +650,13 @@ FC_REFLECT( graphene::chain::asset_update_bitasset_operation,
|
|||
(new_options)
|
||||
(extensions)
|
||||
)
|
||||
FC_REFLECT( graphene::chain::asset_update_dividend_operation,
|
||||
(fee)
|
||||
(issuer)
|
||||
(asset_to_update)
|
||||
(new_options)
|
||||
(extensions)
|
||||
)
|
||||
FC_REFLECT( graphene::chain::asset_update_feed_producers_operation,
|
||||
(fee)(issuer)(asset_to_update)(new_feed_producers)(extensions)
|
||||
)
|
||||
|
|
@ -525,3 +671,4 @@ FC_REFLECT( graphene::chain::asset_reserve_operation,
|
|||
(fee)(payer)(amount_to_reserve)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::asset_fund_fee_pool_operation, (fee)(from_account)(asset_id)(amount)(extensions) );
|
||||
FC_REFLECT( graphene::chain::asset_dividend_distribution_operation, (fee)(dividend_asset_id)(account_id)(amounts)(extensions) );
|
||||
|
|
|
|||
|
|
@ -132,5 +132,5 @@ void add_authority_accounts(
|
|||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::authority, (weight_threshold)(account_auths)(key_auths)(address_auths) )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::authority::classification )
|
||||
// FC_REFLECT_TYPENAME( graphene::chain::authority::classification )
|
||||
FC_REFLECT_ENUM( graphene::chain::authority::classification, (owner)(active)(key) )
|
||||
|
|
|
|||
|
|
@ -0,0 +1,493 @@
|
|||
/*
|
||||
* 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/chain/protocol/base.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct betting_market_rules_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
/**
|
||||
* A short name for the rules, like "Premier League Rules 1.0", probably not
|
||||
* displayed to the user
|
||||
*/
|
||||
internationalized_string_type name;
|
||||
|
||||
/**
|
||||
* The full text of the rules to be displayed to the user. As yet, there is
|
||||
* no standard format (html, markdown, etc)
|
||||
*/
|
||||
internationalized_string_type description;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
|
||||
struct betting_market_rules_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
betting_market_rules_id_type betting_market_rules_id;
|
||||
|
||||
fc::optional<internationalized_string_type> new_name;
|
||||
|
||||
fc::optional<internationalized_string_type> new_description;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
enum class betting_market_status
|
||||
{
|
||||
unresolved, /// no grading has been published for this betting market
|
||||
frozen, /// bets are suspended, no bets allowed
|
||||
graded, /// grading of win or not_win has been published
|
||||
canceled, /// the betting market is canceled, no further bets are allowed
|
||||
settled, /// the betting market has been paid out
|
||||
BETTING_MARKET_STATUS_COUNT
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The status of a betting market group. This controls the behavior of the betting
|
||||
* markets in the group.
|
||||
*/
|
||||
enum class betting_market_group_status
|
||||
{
|
||||
upcoming, /// betting markets are accepting bets, will never go "in_play"
|
||||
in_play, /// betting markets are delaying bets
|
||||
closed, /// betting markets are no longer accepting bets
|
||||
graded, /// witnesses have published win/not win for the betting markets
|
||||
re_grading, /// initial win/not win grading has been challenged
|
||||
settled, /// paid out
|
||||
frozen, /// betting markets are not accepting bets
|
||||
canceled, /// canceled
|
||||
BETTING_MARKET_GROUP_STATUS_COUNT
|
||||
};
|
||||
|
||||
|
||||
struct betting_market_group_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
/**
|
||||
* A description of the betting market, like "Moneyline", "Over/Under 180",
|
||||
* used for display
|
||||
*/
|
||||
internationalized_string_type description;
|
||||
|
||||
/**
|
||||
* This can be a event_id_type, or a
|
||||
* relative object id that resolves to a event_id_type
|
||||
*/
|
||||
object_id_type event_id;
|
||||
|
||||
/**
|
||||
* This can be a betting_market_rules_id_type, or a
|
||||
* relative object id that resolves to a betting_market_rules_id_type
|
||||
*/
|
||||
object_id_type rules_id;
|
||||
|
||||
/**
|
||||
* The asset used to place bets for all betting markets in this group
|
||||
*/
|
||||
asset_id_type asset_id;
|
||||
|
||||
/**
|
||||
* If true, this market will never go "in-play"
|
||||
*/
|
||||
bool never_in_play;
|
||||
|
||||
/**
|
||||
* After a grading has been published, the blockchain will wait this many
|
||||
* seconds before settling (paying the winners).
|
||||
* If the published grading is flagged (challenged) during this period,
|
||||
* settling will be delayed indefinitely until the betting market
|
||||
* group is re-graded (not implemented)
|
||||
*/
|
||||
uint32_t delay_before_settling;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct betting_market_group_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
betting_market_group_id_type betting_market_group_id;
|
||||
|
||||
optional<internationalized_string_type> new_description;
|
||||
|
||||
optional<object_id_type> new_rules_id;
|
||||
|
||||
optional<betting_market_group_status> status;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct betting_market_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
/**
|
||||
* This can be a betting_market_group_id_type, or a
|
||||
* relative object id that resolves to a betting_market_group_id_type
|
||||
*/
|
||||
object_id_type group_id;
|
||||
|
||||
internationalized_string_type description;
|
||||
|
||||
internationalized_string_type payout_condition;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct betting_market_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
betting_market_id_type betting_market_id;
|
||||
|
||||
optional<object_id_type> new_group_id;
|
||||
|
||||
optional<internationalized_string_type> new_description;
|
||||
|
||||
optional<internationalized_string_type> new_payout_condition;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
enum class betting_market_resolution_type {
|
||||
win,
|
||||
not_win,
|
||||
cancel,
|
||||
BETTING_MARKET_RESOLUTION_COUNT
|
||||
};
|
||||
|
||||
struct betting_market_group_resolve_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
betting_market_group_id_type betting_market_group_id;
|
||||
|
||||
std::map<betting_market_id_type, betting_market_resolution_type> resolutions;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct betting_market_group_resolved_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {};
|
||||
|
||||
account_id_type bettor_id;
|
||||
betting_market_group_id_type betting_market_group_id;
|
||||
std::map<betting_market_id_type, betting_market_resolution_type> resolutions;
|
||||
|
||||
share_type winnings; // always the asset type of the betting market group
|
||||
share_type fees_paid; // always the asset type of the betting market group
|
||||
|
||||
asset fee; // unused in a virtual operation
|
||||
|
||||
betting_market_group_resolved_operation() {}
|
||||
betting_market_group_resolved_operation(account_id_type bettor_id,
|
||||
betting_market_group_id_type betting_market_group_id,
|
||||
const std::map<betting_market_id_type, betting_market_resolution_type>& resolutions,
|
||||
share_type winnings,
|
||||
share_type fees_paid) :
|
||||
bettor_id(bettor_id),
|
||||
betting_market_group_id(betting_market_group_id),
|
||||
resolutions(resolutions),
|
||||
winnings(winnings),
|
||||
fees_paid(fees_paid)
|
||||
{
|
||||
}
|
||||
|
||||
account_id_type fee_payer()const { return bettor_id; }
|
||||
void validate()const { FC_ASSERT(false, "virtual operation"); }
|
||||
/// This is a virtual operation; there is no fee
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct betting_market_group_cancel_unmatched_bets_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
betting_market_group_id_type betting_market_group_id;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
enum class bet_type { back, lay };
|
||||
|
||||
struct bet_place_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type
|
||||
{
|
||||
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; // fixed fee charged upon placing the bet
|
||||
};
|
||||
asset fee;
|
||||
|
||||
account_id_type bettor_id;
|
||||
|
||||
betting_market_id_type betting_market_id;
|
||||
|
||||
/// the bettor's stake
|
||||
asset amount_to_bet;
|
||||
|
||||
// decimal odds as seen by the backer, even if this is a lay bet.
|
||||
// this is a fixed-precision number scaled by GRAPHENE_BETTING_ODDS_PRECISION.
|
||||
//
|
||||
// For example, an even 1/1 bet would be decimal odds 2.0, so backer_multiplier
|
||||
// would be 2 * GRAPHENE_BETTING_ODDS_PRECISION.
|
||||
bet_multiplier_type backer_multiplier;
|
||||
|
||||
bet_type back_or_lay;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return bettor_id; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* virtual op generated when a bet is matched
|
||||
*/
|
||||
struct bet_matched_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {};
|
||||
|
||||
bet_matched_operation(){}
|
||||
bet_matched_operation(account_id_type bettor_id, bet_id_type bet_id,
|
||||
asset amount_bet,
|
||||
bet_multiplier_type backer_multiplier,
|
||||
share_type guaranteed_winnings_returned) :
|
||||
bettor_id(bettor_id),
|
||||
bet_id(bet_id),
|
||||
amount_bet(amount_bet),
|
||||
backer_multiplier(backer_multiplier),
|
||||
guaranteed_winnings_returned(guaranteed_winnings_returned)
|
||||
{}
|
||||
|
||||
account_id_type bettor_id;
|
||||
bet_id_type bet_id;
|
||||
asset amount_bet;
|
||||
bet_multiplier_type backer_multiplier; // the actual odds received
|
||||
share_type guaranteed_winnings_returned; // same asset type as amount_bet
|
||||
asset fee; // unimportant for a virtual op
|
||||
|
||||
account_id_type fee_payer()const { return bettor_id; }
|
||||
void validate()const { FC_ASSERT(false, "virtual operation"); }
|
||||
|
||||
/// This is a virtual operation; there is no fee
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct bet_cancel_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type
|
||||
{
|
||||
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
//uint64_t fee = 0;
|
||||
};
|
||||
asset fee;
|
||||
|
||||
/// the bettor who is cancelling the bet
|
||||
account_id_type bettor_id;
|
||||
/// the bet being canceled
|
||||
bet_id_type bet_to_cancel;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return bettor_id; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* virtual op generated when a bet is canceled
|
||||
*/
|
||||
struct bet_canceled_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {};
|
||||
|
||||
bet_canceled_operation(){}
|
||||
bet_canceled_operation(account_id_type bettor_id, bet_id_type bet_id,
|
||||
asset stake_returned) :
|
||||
bettor_id(bettor_id),
|
||||
bet_id(bet_id),
|
||||
stake_returned(stake_returned)
|
||||
{}
|
||||
|
||||
account_id_type bettor_id;
|
||||
bet_id_type bet_id;
|
||||
asset stake_returned;
|
||||
asset fee; // unimportant for a virtual op
|
||||
|
||||
account_id_type fee_payer()const { return bettor_id; }
|
||||
void validate()const { FC_ASSERT(false, "virtual operation"); }
|
||||
|
||||
/// This is a virtual operation; there is no fee
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* virtual op generated when a bet amount is rounded down to an amount that can
|
||||
* match evenly at a given odds (the blockchain does this automatically at the time
|
||||
* the bet is placed on the order books). (note: there is no way a user can adjust their bet
|
||||
* after placing it, aside from canceling the bet and placing a new one)
|
||||
*/
|
||||
struct bet_adjusted_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {};
|
||||
|
||||
bet_adjusted_operation(){}
|
||||
bet_adjusted_operation(account_id_type bettor_id, bet_id_type bet_id,
|
||||
asset stake_returned) :
|
||||
bettor_id(bettor_id),
|
||||
bet_id(bet_id),
|
||||
stake_returned(stake_returned)
|
||||
{}
|
||||
|
||||
account_id_type bettor_id;
|
||||
bet_id_type bet_id;
|
||||
asset stake_returned;
|
||||
asset fee; // unimportant for a virtual op
|
||||
|
||||
account_id_type fee_payer()const { return bettor_id; }
|
||||
void validate()const { FC_ASSERT(false, "virtual operation"); }
|
||||
|
||||
/// This is a virtual operation; there is no fee
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_rules_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_rules_create_operation,
|
||||
(fee)(name)(description)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_rules_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_rules_update_operation,
|
||||
(fee)(new_name)(new_description)(extensions)(betting_market_rules_id) )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::betting_market_status,
|
||||
(unresolved)
|
||||
(frozen)
|
||||
(graded)
|
||||
(canceled)
|
||||
(settled)
|
||||
(BETTING_MARKET_STATUS_COUNT) )
|
||||
FC_REFLECT_ENUM( graphene::chain::betting_market_group_status,
|
||||
(upcoming)
|
||||
(in_play)
|
||||
(closed)
|
||||
(graded)
|
||||
(re_grading)
|
||||
(settled)
|
||||
(frozen)
|
||||
(canceled)
|
||||
(BETTING_MARKET_GROUP_STATUS_COUNT) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_group_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_group_create_operation,
|
||||
(fee)(description)(event_id)(rules_id)(asset_id)
|
||||
(never_in_play)(delay_before_settling)
|
||||
(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_group_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_group_update_operation,
|
||||
(fee)(betting_market_group_id)(new_description)(new_rules_id)(status)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_create_operation,
|
||||
(fee)(group_id)(description)(payout_condition)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_update_operation,
|
||||
(fee)(betting_market_id)(new_group_id)(new_description)(new_payout_condition)(extensions) )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::betting_market_resolution_type, (win)(not_win)(cancel)(BETTING_MARKET_RESOLUTION_COUNT) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_group_resolve_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_group_resolve_operation,
|
||||
(fee)(betting_market_group_id)(resolutions)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_group_resolved_operation::fee_parameters_type, )
|
||||
FC_REFLECT( graphene::chain::betting_market_group_resolved_operation,
|
||||
(bettor_id)(betting_market_group_id)(resolutions)(winnings)(fees_paid)(fee) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_group_cancel_unmatched_bets_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_group_cancel_unmatched_bets_operation,
|
||||
(fee)(betting_market_group_id)(extensions) )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::bet_type, (back)(lay) )
|
||||
FC_REFLECT( graphene::chain::bet_place_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::bet_place_operation,
|
||||
(fee)(bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(back_or_lay)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bet_matched_operation::fee_parameters_type, )
|
||||
FC_REFLECT( graphene::chain::bet_matched_operation, (bettor_id)(bet_id)(amount_bet)(backer_multiplier)(guaranteed_winnings_returned) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bet_cancel_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::bet_cancel_operation, (fee) (bettor_id) (bet_to_cancel) (extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bet_canceled_operation::fee_parameters_type, )
|
||||
FC_REFLECT( graphene::chain::bet_canceled_operation, (bettor_id)(bet_id)(stake_returned) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bet_adjusted_operation::fee_parameters_type, )
|
||||
FC_REFLECT( graphene::chain::bet_adjusted_operation, (bettor_id)(bet_id)(stake_returned) )
|
||||
|
|
@ -33,6 +33,8 @@ namespace graphene { namespace chain {
|
|||
uint32_t block_num()const { return num_from_id(previous) + 1; }
|
||||
fc::time_point_sec timestamp;
|
||||
witness_id_type witness;
|
||||
secret_hash_type next_secret_hash;
|
||||
secret_hash_type previous_secret;
|
||||
checksum_type transaction_merkle_root;
|
||||
extensions_type extensions;
|
||||
|
||||
|
|
@ -57,6 +59,13 @@ namespace graphene { namespace chain {
|
|||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::block_header, (previous)(timestamp)(witness)(transaction_merkle_root)(extensions) )
|
||||
FC_REFLECT( graphene::chain::block_header,
|
||||
(previous)
|
||||
(timestamp)
|
||||
(witness)
|
||||
(next_secret_hash)
|
||||
(previous_secret)
|
||||
(transaction_merkle_root)
|
||||
(extensions) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::signed_block_header, (graphene::chain::block_header), (witness_signature) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::signed_block, (graphene::chain::signed_block_header), (transactions) )
|
||||
|
|
|
|||
|
|
@ -69,6 +69,27 @@ namespace graphene { namespace chain {
|
|||
uint16_t accounts_per_fee_scale = GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE; ///< number of accounts between fee scalings
|
||||
uint8_t account_fee_scale_bitshifts = GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS; ///< number of times to left bitshift account registration fee at each scaling
|
||||
uint8_t max_authority_depth = GRAPHENE_MAX_SIG_CHECK_DEPTH;
|
||||
uint16_t betting_rake_fee_percentage = GRAPHENE_DEFAULT_RAKE_FEE_PERCENTAGE; ///< part of prize paid into the dividend account for the core token holders
|
||||
bet_multiplier_type min_bet_multiplier = GRAPHENE_DEFAULT_MIN_BET_MULTIPLIER;
|
||||
bet_multiplier_type max_bet_multiplier = GRAPHENE_DEFAULT_MAX_BET_MULTIPLIER;
|
||||
flat_map<bet_multiplier_type, bet_multiplier_type> permitted_betting_odds_increments = GRAPHENE_DEFAULT_PERMITTED_BETTING_ODDS_INCREMENTS;
|
||||
uint16_t live_betting_delay_time = GRAPHENE_DEFAULT_LIVE_BETTING_DELAY_TIME; ///< delayed bets
|
||||
//uint8_t witness_schedule_algorithm = GRAPHENE_WITNESS_SHUFFLED_ALGORITHM; ///< 0 shuffled, 1 scheduled
|
||||
uint8_t witness_schedule_algorithm = GRAPHENE_WITNESS_SCHEDULED_ALGORITHM; ///< 0 shuffled, 1 scheduled
|
||||
/* rps tournament parameters constraints */
|
||||
uint32_t min_round_delay = TOURNAMENT_MIN_ROUND_DELAY; ///< miniaml delay between games
|
||||
uint32_t max_round_delay = TOURNAMENT_MAX_ROUND_DELAY; ///< maximal delay between games
|
||||
uint32_t min_time_per_commit_move = TOURNAMENT_MIN_TIME_PER_COMMIT_MOVE; ///< minimal time to commit the next move
|
||||
uint32_t max_time_per_commit_move = TOURNAMENT_MAN_TIME_PER_COMMIT_MOVE; ///< maximal time to commit the next move
|
||||
uint32_t min_time_per_reveal_move = TOURNAMENT_MIN_TIME_PER_REVEAL_MOVE; ///< minimal time to reveal move
|
||||
uint32_t max_time_per_reveal_move = TOURNAMENT_MAX_TIME_PER_REVEAL_MOVE; ///< maximal time to reveal move
|
||||
uint16_t rake_fee_percentage = TOURNAMENT_DEFAULT_RAKE_FEE_PERCENTAGE; ///< part of prize paid into the dividend account for the core token holders
|
||||
uint32_t maximum_registration_deadline = TOURNAMENT_MAXIMAL_REGISTRATION_DEADLINE; ///< value registration deadline must be before
|
||||
uint16_t maximum_players_in_tournament = TOURNAMENT_MAX_PLAYERS_NUMBER; ///< maximal count of players in tournament
|
||||
uint16_t maximum_tournament_whitelist_length = TOURNAMENT_MAX_WHITELIST_LENGTH; ///< maximal tournament whitelist length
|
||||
uint32_t maximum_tournament_start_time_in_future = TOURNAMENT_MAX_START_TIME_IN_FUTURE;
|
||||
uint32_t maximum_tournament_start_delay = TOURNAMENT_MAX_START_DELAY;
|
||||
uint16_t maximum_tournament_number_of_wins = TOURNAMENT_MAX_NUMBER_OF_WINS;
|
||||
extensions_type extensions;
|
||||
|
||||
/** defined in fee_schedule.cpp */
|
||||
|
|
@ -106,5 +127,24 @@ FC_REFLECT( graphene::chain::chain_parameters,
|
|||
(accounts_per_fee_scale)
|
||||
(account_fee_scale_bitshifts)
|
||||
(max_authority_depth)
|
||||
(min_bet_multiplier)
|
||||
(max_bet_multiplier)
|
||||
(betting_rake_fee_percentage)
|
||||
(permitted_betting_odds_increments)
|
||||
(witness_schedule_algorithm)
|
||||
(live_betting_delay_time)
|
||||
(min_round_delay)
|
||||
(max_round_delay)
|
||||
(min_time_per_commit_move)
|
||||
(max_time_per_commit_move)
|
||||
(min_time_per_reveal_move)
|
||||
(max_time_per_reveal_move)
|
||||
(rake_fee_percentage)
|
||||
(maximum_registration_deadline)
|
||||
(maximum_players_in_tournament)
|
||||
(maximum_tournament_whitelist_length)
|
||||
(maximum_tournament_start_time_in_future)
|
||||
(maximum_tournament_start_delay)
|
||||
(maximum_tournament_number_of_wins)
|
||||
(extensions)
|
||||
)
|
||||
|
|
|
|||
147
libraries/chain/include/graphene/chain/protocol/event.hpp
Normal file
147
libraries/chain/include/graphene/chain/protocol/event.hpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* 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/chain/protocol/base.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct event_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
/**
|
||||
* The name of the event
|
||||
*/
|
||||
internationalized_string_type name;
|
||||
|
||||
internationalized_string_type season;
|
||||
|
||||
optional<time_point_sec> start_time;
|
||||
|
||||
/**
|
||||
* This can be a event_group_id_type, or a
|
||||
* relative object id that resolves to a event_group_id_type
|
||||
*/
|
||||
object_id_type event_group_id;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* The status of an event. This is used to display in the UI, and setting
|
||||
* the event's status to certain values will propagate down to the
|
||||
* betting market groups in the event:
|
||||
* - when set to `in_progress`, all betting market groups are set to `in_play`
|
||||
* - when set to `completed`, all betting market groups are set to `closed`
|
||||
* - when set to `frozen`, all betting market groups are set to `frozen`
|
||||
* - when set to `canceled`, all betting market groups are set to `canceled`
|
||||
*/
|
||||
enum class event_status
|
||||
{
|
||||
upcoming, /// Event has not started yet, betting is allowed
|
||||
in_progress, /// Event is in progress, if "in-play" betting is enabled, bets will be delayed
|
||||
frozen, /// Betting is temporarily disabled
|
||||
finished, /// Event has finished, no more betting allowed
|
||||
canceled, /// Event has been canceled, all betting markets have been canceled
|
||||
settled, /// All betting markets have been paid out
|
||||
STATUS_COUNT
|
||||
};
|
||||
|
||||
struct event_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
event_id_type event_id;
|
||||
|
||||
optional<object_id_type> new_event_group_id;
|
||||
|
||||
optional<internationalized_string_type> new_name;
|
||||
|
||||
optional<internationalized_string_type> new_season;
|
||||
|
||||
optional<time_point_sec> new_start_time;
|
||||
|
||||
optional<event_status> new_status;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* The current (or final) score of an event.
|
||||
* This is only used for display to the user, witnesses must resolve each
|
||||
* betting market explicitly.
|
||||
* These are free-form strings that we assume will make sense to the user.
|
||||
* For a game like football, this may be a score like "3". For races,
|
||||
* it could be a time like "1:53.4".
|
||||
*/
|
||||
struct event_update_status_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
/// the id of the event to update
|
||||
event_id_type event_id;
|
||||
|
||||
/**
|
||||
* the new status of the event (if the status hasn't changed, the creator
|
||||
* of this operation must still set `status` to the event's current status)
|
||||
*/
|
||||
event_status status;
|
||||
|
||||
/*
|
||||
* scores for each competitor stored in same order as competitors in event_object
|
||||
*/
|
||||
vector<string> scores;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::event_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::event_create_operation,
|
||||
(fee)(name)(season)(start_time)(event_group_id)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::event_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::event_update_operation,
|
||||
(fee)(event_id)(new_event_group_id)(new_name)(new_season)(new_start_time)(new_status)(extensions) )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::event_status, (upcoming)(in_progress)(frozen)(finished)(canceled)(settled)(STATUS_COUNT) )
|
||||
FC_REFLECT( graphene::chain::event_update_status_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::event_update_status_operation,
|
||||
(fee)(event_id)(status)(scores)(extensions) )
|
||||
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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/chain/protocol/base.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct event_group_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
/**
|
||||
* The name of the event_group
|
||||
*/
|
||||
internationalized_string_type name;
|
||||
|
||||
/**
|
||||
* This can be a sport_id_type, or a
|
||||
* relative object id that resolves to a sport_id_type
|
||||
*/
|
||||
object_id_type sport_id;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct event_group_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
event_group_id_type event_group_id;
|
||||
|
||||
/**
|
||||
* This can be a sport_id_type, or a
|
||||
* relative object id that resolves to a sport_id_type
|
||||
*/
|
||||
optional<object_id_type> new_sport_id;
|
||||
|
||||
optional<internationalized_string_type> new_name;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::event_group_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::event_group_create_operation,
|
||||
(fee)(name)(sport_id)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::event_group_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::event_group_update_operation,
|
||||
(fee)(new_sport_id)(new_name)(event_group_id)(extensions) )
|
||||
|
|
@ -82,6 +82,8 @@ void operator<<( Stream& stream, const graphene::chain::extension<T>& value )
|
|||
fc::reflector<T>::visit( read_vtor );
|
||||
}
|
||||
|
||||
|
||||
|
||||
template< typename Stream, typename T >
|
||||
struct graphene_extension_unpack_visitor
|
||||
{
|
||||
|
|
@ -108,7 +110,7 @@ struct graphene_extension_unpack_visitor
|
|||
{
|
||||
if( (count_left > 0) && (which == next_which) )
|
||||
{
|
||||
Member temp;
|
||||
typename Member::value_type temp;
|
||||
fc::raw::unpack( stream, temp );
|
||||
(value.*member) = temp;
|
||||
--count_left;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,11 @@
|
|||
#include <graphene/chain/protocol/withdraw_permission.hpp>
|
||||
#include <graphene/chain/protocol/witness.hpp>
|
||||
#include <graphene/chain/protocol/worker.hpp>
|
||||
#include <graphene/chain/protocol/sport.hpp>
|
||||
#include <graphene/chain/protocol/event_group.hpp>
|
||||
#include <graphene/chain/protocol/event.hpp>
|
||||
#include <graphene/chain/protocol/betting_market.hpp>
|
||||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -91,7 +96,35 @@ namespace graphene { namespace chain {
|
|||
transfer_from_blind_operation,
|
||||
asset_settle_cancel_operation, // VIRTUAL
|
||||
asset_claim_fees_operation,
|
||||
fba_distribute_operation // VIRTUAL
|
||||
fba_distribute_operation, // VIRTUAL
|
||||
asset_update_dividend_operation,
|
||||
asset_dividend_distribution_operation, // VIRTUAL
|
||||
sport_create_operation,
|
||||
sport_update_operation,
|
||||
event_group_create_operation,
|
||||
event_group_update_operation,
|
||||
event_create_operation,
|
||||
event_update_operation,
|
||||
betting_market_rules_create_operation,
|
||||
betting_market_rules_update_operation,
|
||||
betting_market_group_create_operation,
|
||||
betting_market_create_operation,
|
||||
bet_place_operation,
|
||||
betting_market_group_resolve_operation,
|
||||
betting_market_group_resolved_operation, // VIRTUAL
|
||||
bet_adjusted_operation, // VIRTUAL
|
||||
betting_market_group_cancel_unmatched_bets_operation,
|
||||
bet_matched_operation, // VIRTUAL
|
||||
bet_cancel_operation,
|
||||
bet_canceled_operation, // VIRTUAL
|
||||
tournament_create_operation,
|
||||
tournament_join_operation,
|
||||
game_move_operation,
|
||||
tournament_payout_operation, // VIRTUAL
|
||||
tournament_leave_operation,
|
||||
betting_market_group_update_operation,
|
||||
betting_market_update_operation,
|
||||
event_update_status_operation
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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 <cassert>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <fc/container/flat.hpp>
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct rock_paper_scissors_game_options
|
||||
{
|
||||
/// If true and a user fails to commit their move before the time_per_commit_move expires,
|
||||
/// the blockchain will randomly choose a move for the user
|
||||
bool insurance_enabled;
|
||||
/// The number of seconds users are given to commit their next move, counted from the beginning
|
||||
/// of the hand (during the game, a hand begins immediately on the block containing the
|
||||
/// second player's reveal or where the time_per_reveal move has expired).
|
||||
/// Note, if these times aren't an even multiple of the block interval, they will be rounded
|
||||
/// up.
|
||||
uint32_t time_per_commit_move;
|
||||
|
||||
/// The number of seconds users are given to reveal their move, counted from the time of the
|
||||
/// block containing the second commit or the where the time_per_commit_move expired
|
||||
uint32_t time_per_reveal_move;
|
||||
|
||||
/// The number of allowed gestures, must be either 3 or 5. If 3, the game is
|
||||
/// standard rock-paper-scissors, if 5, it's
|
||||
/// rock-paper-scissors-lizard-spock.
|
||||
uint8_t number_of_gestures;
|
||||
};
|
||||
|
||||
enum class rock_paper_scissors_gesture
|
||||
{
|
||||
rock,
|
||||
paper,
|
||||
scissors,
|
||||
spock,
|
||||
lizard
|
||||
};
|
||||
|
||||
struct rock_paper_scissors_throw
|
||||
{
|
||||
uint64_t nonce1;
|
||||
uint64_t nonce2;
|
||||
rock_paper_scissors_gesture gesture;
|
||||
fc::sha256 calculate_hash() const;
|
||||
};
|
||||
|
||||
struct rock_paper_scissors_throw_commit
|
||||
{
|
||||
uint64_t nonce1;
|
||||
fc::sha256 throw_hash;
|
||||
bool operator<(const graphene::chain::rock_paper_scissors_throw_commit& rhs) const
|
||||
{
|
||||
return std::tie(nonce1, throw_hash) < std::tie(rhs.nonce1, rhs.throw_hash);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct rock_paper_scissors_throw_reveal
|
||||
{
|
||||
uint64_t nonce2;
|
||||
rock_paper_scissors_gesture gesture;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::rock_paper_scissors_game_options, (insurance_enabled)(time_per_commit_move)(time_per_reveal_move)(number_of_gestures) )
|
||||
|
||||
// FC_REFLECT_TYPENAME( graphene::chain::rock_paper_scissors_gesture)
|
||||
FC_REFLECT_ENUM( graphene::chain::rock_paper_scissors_gesture,
|
||||
(rock)
|
||||
(paper)
|
||||
(scissors)
|
||||
(spock)
|
||||
(lizard))
|
||||
|
||||
FC_REFLECT( graphene::chain::rock_paper_scissors_throw,
|
||||
(nonce1)
|
||||
(nonce2)
|
||||
(gesture) )
|
||||
|
||||
FC_REFLECT( graphene::chain::rock_paper_scissors_throw_commit,
|
||||
(nonce1)
|
||||
(throw_hash) )
|
||||
|
||||
FC_REFLECT( graphene::chain::rock_paper_scissors_throw_reveal,
|
||||
(nonce2)(gesture) )
|
||||
|
||||
70
libraries/chain/include/graphene/chain/protocol/sport.hpp
Normal file
70
libraries/chain/include/graphene/chain/protocol/sport.hpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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/chain/protocol/base.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct sport_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
/**
|
||||
* The name of the sport
|
||||
*/
|
||||
internationalized_string_type name;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct sport_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
sport_id_type sport_id;
|
||||
|
||||
optional<internationalized_string_type> new_name;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::sport_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::sport_create_operation,
|
||||
(fee)(name)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::sport_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::sport_update_operation,
|
||||
(fee)(sport_id)(new_name)(extensions) )
|
||||
279
libraries/chain/include/graphene/chain/protocol/tournament.hpp
Normal file
279
libraries/chain/include/graphene/chain/protocol/tournament.hpp
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* 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 <cassert>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <fc/container/flat.hpp>
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
#include <graphene/chain/protocol/asset.hpp>
|
||||
#include <graphene/chain/protocol/rock_paper_scissors.hpp>
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
enum class payout_type
|
||||
{
|
||||
prize_award,
|
||||
buyin_refund,
|
||||
rake_fee
|
||||
};
|
||||
|
||||
typedef fc::static_variant<rock_paper_scissors_game_options> game_specific_options;
|
||||
|
||||
/**
|
||||
* @brief Options specified when creating a new tournament
|
||||
*/
|
||||
struct tournament_options
|
||||
{
|
||||
/// If there aren't enough players registered for the tournament before this time,
|
||||
/// the tournament is canceled
|
||||
fc::time_point_sec registration_deadline;
|
||||
|
||||
/// Number of players in the tournament. Must be greater than 1. Currently max is 255, should it be committe-settable?
|
||||
uint32_t number_of_players;
|
||||
|
||||
/// Each player must pay this much to join the tournament. This can be
|
||||
/// in any asset supported by the blockchain. If the tournament is canceled,
|
||||
/// the buy-in will be returned.
|
||||
asset buy_in;
|
||||
|
||||
/// A list of all accounts allowed to register for this tournament. If empty,
|
||||
/// anyone can register for the tournament
|
||||
flat_set<account_id_type> whitelist;
|
||||
|
||||
/// If specified, this is the time the tourament will start (must not be before the registration
|
||||
/// deadline). If this is not specified, the creator must specify `start_delay` instead.
|
||||
optional<fc::time_point_sec> start_time;
|
||||
|
||||
/// If specified, this is the number of seconds after the final player registers before the
|
||||
/// tournament begins. If this is not specified, the creator must specify an absolute `start_time`
|
||||
optional<uint32_t> start_delay;
|
||||
|
||||
/// The delay, in seconds, between the end of the last game in one round of the tournament and the
|
||||
/// start of all the games in the next round
|
||||
uint32_t round_delay;
|
||||
|
||||
/// The winner of a round in the tournament is the first to reach this number of wins
|
||||
uint32_t number_of_wins;
|
||||
|
||||
/// Metadata about this tournament. This can be empty or it can contain any keys the creator desires.
|
||||
/// The GUI will standardize on displaying a few keys, likely:
|
||||
/// "name"
|
||||
/// "description"
|
||||
/// "url"
|
||||
fc::variant_object meta;
|
||||
|
||||
/// Parameters that are specific to the type_of_game in this tournament
|
||||
/// The type stored in this static_variant field determines what type of game is being
|
||||
/// played, so each different supported game must have a unique game_options data type
|
||||
game_specific_options game_options;
|
||||
|
||||
void validate() const;
|
||||
};
|
||||
|
||||
struct tournament_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
share_type fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
uint32_t price_per_kbyte = 10;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
|
||||
/// The account that created the tournament
|
||||
account_id_type creator;
|
||||
|
||||
/// Options for the tournament
|
||||
tournament_options options;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return creator; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const;
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct tournament_join_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
share_type fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
|
||||
/// The account that is paying the buy-in for the tournament, if the tournament is
|
||||
/// canceled, will be refunded the buy-in.
|
||||
account_id_type payer_account_id;
|
||||
|
||||
/// The account that will play in the tournament, will receive any winnings.
|
||||
account_id_type player_account_id;
|
||||
|
||||
/// The tournament `player_account_id` is joining
|
||||
tournament_id_type tournament_id;
|
||||
|
||||
/// The buy-in paid by the `payer_account_id`
|
||||
asset buy_in;
|
||||
|
||||
extensions_type extensions;
|
||||
account_id_type fee_payer()const { return payer_account_id; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const;
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct tournament_leave_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
share_type fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
|
||||
/// The account that is unregistering the player from tournament (must be payer or player)
|
||||
account_id_type canceling_account_id;
|
||||
|
||||
/// The account that would play in the tournament, would receive any winnings.
|
||||
account_id_type player_account_id;
|
||||
|
||||
/// The tournament `player_account_id` is leaving
|
||||
tournament_id_type tournament_id;
|
||||
|
||||
extensions_type extensions;
|
||||
account_id_type fee_payer()const { return canceling_account_id; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const;
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
|
||||
typedef fc::static_variant<rock_paper_scissors_throw_commit, rock_paper_scissors_throw_reveal> game_specific_moves;
|
||||
|
||||
struct game_move_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
share_type fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
asset fee;
|
||||
|
||||
/// the id of the game
|
||||
game_id_type game_id;
|
||||
|
||||
/// The account of the player making this move
|
||||
account_id_type player_account_id;
|
||||
|
||||
/// the move itself
|
||||
game_specific_moves move;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return player_account_id; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const;
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct tournament_payout_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {};
|
||||
|
||||
asset fee;
|
||||
|
||||
/// The account received payout
|
||||
account_id_type payout_account_id;
|
||||
|
||||
/// The tournament generated payout
|
||||
tournament_id_type tournament_id;
|
||||
|
||||
/// The payout amount
|
||||
asset payout_amount;
|
||||
|
||||
payout_type type;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return payout_account_id; }
|
||||
share_type calculate_fee(const fee_parameters_type&)const { return 0; }
|
||||
void validate()const {}
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::payout_type,
|
||||
(prize_award)
|
||||
(buyin_refund)
|
||||
(rake_fee)
|
||||
)
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::game_specific_options )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::game_specific_moves )
|
||||
FC_REFLECT( graphene::chain::tournament_options,
|
||||
(registration_deadline)
|
||||
(number_of_players)
|
||||
(buy_in)
|
||||
(whitelist)
|
||||
(start_time)
|
||||
(start_delay)
|
||||
(round_delay)
|
||||
(number_of_wins)
|
||||
(meta)
|
||||
(game_options))
|
||||
FC_REFLECT( graphene::chain::tournament_create_operation,
|
||||
(fee)
|
||||
(creator)
|
||||
(options)
|
||||
(extensions))
|
||||
FC_REFLECT( graphene::chain::tournament_join_operation,
|
||||
(fee)
|
||||
(payer_account_id)
|
||||
(player_account_id)
|
||||
(tournament_id)
|
||||
(buy_in)
|
||||
(extensions))
|
||||
FC_REFLECT( graphene::chain::tournament_leave_operation,
|
||||
(fee)
|
||||
(canceling_account_id)
|
||||
(player_account_id)
|
||||
(tournament_id)
|
||||
(extensions))
|
||||
FC_REFLECT( graphene::chain::game_move_operation,
|
||||
(fee)
|
||||
(game_id)
|
||||
(player_account_id)
|
||||
(move)
|
||||
(extensions))
|
||||
FC_REFLECT( graphene::chain::tournament_payout_operation,
|
||||
(fee)
|
||||
(payout_account_id)
|
||||
(tournament_id)
|
||||
(payout_amount)
|
||||
(type)
|
||||
(extensions))
|
||||
|
||||
FC_REFLECT( graphene::chain::tournament_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::tournament_join_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::tournament_leave_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::game_move_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::tournament_payout_operation::fee_parameters_type, )
|
||||
|
||||
|
|
@ -134,6 +134,17 @@ namespace graphene { namespace chain {
|
|||
vesting_balance_object_type,
|
||||
worker_object_type,
|
||||
balance_object_type,
|
||||
sport_object_type,
|
||||
event_group_object_type,
|
||||
event_object_type,
|
||||
betting_market_rules_object_type,
|
||||
betting_market_group_object_type,
|
||||
betting_market_object_type,
|
||||
bet_object_type,
|
||||
tournament_object_type,
|
||||
tournament_details_object_type,
|
||||
match_object_type,
|
||||
game_object_type,
|
||||
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||
};
|
||||
|
||||
|
|
@ -155,7 +166,12 @@ namespace graphene { namespace chain {
|
|||
impl_budget_record_object_type,
|
||||
impl_special_authority_object_type,
|
||||
impl_buyback_object_type,
|
||||
impl_fba_accumulator_object_type
|
||||
impl_fba_accumulator_object_type,
|
||||
impl_betting_market_position_object_type,
|
||||
impl_global_betting_statistics_object_type,
|
||||
impl_asset_dividend_data_type,
|
||||
impl_pending_dividend_payout_balance_for_holder_object_type,
|
||||
impl_distributed_dividend_balance_data_type
|
||||
};
|
||||
|
||||
//typedef fc::unsigned_int object_id_type;
|
||||
|
|
@ -175,6 +191,17 @@ namespace graphene { namespace chain {
|
|||
class worker_object;
|
||||
class balance_object;
|
||||
class blinded_balance_object;
|
||||
class sport_object;
|
||||
class event_group_object;
|
||||
class event_object;
|
||||
class betting_market_rules_object;
|
||||
class betting_market_group_object;
|
||||
class betting_market_object;
|
||||
class bet_object;
|
||||
class tournament_object;
|
||||
class tournament_details_object;
|
||||
class match_object;
|
||||
class game_object;
|
||||
|
||||
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
|
||||
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
|
||||
|
|
@ -190,6 +217,17 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type;
|
||||
typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type;
|
||||
typedef object_id< protocol_ids, balance_object_type, balance_object> balance_id_type;
|
||||
typedef object_id< protocol_ids, sport_object_type, sport_object> sport_id_type;
|
||||
typedef object_id< protocol_ids, event_group_object_type, event_group_object> event_group_id_type;
|
||||
typedef object_id< protocol_ids, event_object_type, event_object> event_id_type;
|
||||
typedef object_id< protocol_ids, betting_market_rules_object_type, betting_market_rules_object> betting_market_rules_id_type;
|
||||
typedef object_id< protocol_ids, betting_market_group_object_type, betting_market_group_object> betting_market_group_id_type;
|
||||
typedef object_id< protocol_ids, betting_market_object_type, betting_market_object> betting_market_id_type;
|
||||
typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type;
|
||||
typedef object_id< protocol_ids, tournament_object_type, tournament_object> tournament_id_type;
|
||||
typedef object_id< protocol_ids, tournament_details_object_type, tournament_details_object> tournament_details_id_type;
|
||||
typedef object_id< protocol_ids, match_object_type, match_object> match_id_type;
|
||||
typedef object_id< protocol_ids, game_object_type, game_object> game_id_type;
|
||||
|
||||
// implementation types
|
||||
class global_property_object;
|
||||
|
|
@ -207,11 +245,18 @@ namespace graphene { namespace chain {
|
|||
class special_authority_object;
|
||||
class buyback_object;
|
||||
class fba_accumulator_object;
|
||||
class betting_market_position_object;
|
||||
class global_betting_statistics_object;
|
||||
class tournament_details_object;
|
||||
class asset_dividend_data_object;
|
||||
class pending_dividend_payout_balance_for_holder_object;
|
||||
|
||||
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
|
||||
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
|
||||
typedef object_id< implementation_ids, impl_asset_dynamic_data_type, asset_dynamic_data_object> asset_dynamic_data_id_type;
|
||||
typedef object_id< implementation_ids, impl_asset_bitasset_data_type, asset_bitasset_data_object> asset_bitasset_data_id_type;
|
||||
typedef object_id< implementation_ids, impl_asset_dividend_data_type, asset_dividend_data_object> asset_dividend_data_id_type;
|
||||
typedef object_id< implementation_ids, impl_pending_dividend_payout_balance_for_holder_object_type, pending_dividend_payout_balance_for_holder_object> pending_dividend_payout_balance_for_holder_object_type;
|
||||
typedef object_id< implementation_ids, impl_account_balance_object_type, account_balance_object> account_balance_id_type;
|
||||
typedef object_id< implementation_ids, impl_account_statistics_object_type,account_statistics_object> account_statistics_id_type;
|
||||
typedef object_id< implementation_ids, impl_transaction_object_type, transaction_object> transaction_obj_id_type;
|
||||
|
|
@ -227,6 +272,8 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< implementation_ids, impl_special_authority_object_type, special_authority_object > special_authority_id_type;
|
||||
typedef object_id< implementation_ids, impl_buyback_object_type, buyback_object > buyback_id_type;
|
||||
typedef object_id< implementation_ids, impl_fba_accumulator_object_type, fba_accumulator_object > fba_accumulator_id_type;
|
||||
typedef object_id< implementation_ids, impl_betting_market_position_object_type, betting_market_position_object > betting_market_position_id_type;
|
||||
typedef object_id< implementation_ids, impl_global_betting_statistics_object_type, global_betting_statistics_object > global_betting_statistics_id_type;
|
||||
|
||||
typedef fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
|
||||
typedef fc::ripemd160 block_id_type;
|
||||
|
|
@ -235,6 +282,7 @@ namespace graphene { namespace chain {
|
|||
typedef fc::sha256 digest_type;
|
||||
typedef fc::ecc::compact_signature signature_type;
|
||||
typedef safe<int64_t> share_type;
|
||||
typedef fc::ripemd160 secret_hash_type;
|
||||
typedef uint16_t weight_type;
|
||||
|
||||
struct public_key_type
|
||||
|
|
@ -304,6 +352,10 @@ namespace graphene { namespace chain {
|
|||
friend bool operator == ( const extended_private_key_type& p1, const extended_private_key_type& p2);
|
||||
friend bool operator != ( const extended_private_key_type& p1, const extended_private_key_type& p2);
|
||||
};
|
||||
|
||||
typedef flat_map<std::string, std::string> internationalized_string_type;
|
||||
|
||||
typedef uint32_t bet_multiplier_type;
|
||||
} } // graphene::chain
|
||||
|
||||
namespace fc
|
||||
|
|
@ -340,6 +392,17 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
|
|||
(vesting_balance_object_type)
|
||||
(worker_object_type)
|
||||
(balance_object_type)
|
||||
(sport_object_type)
|
||||
(event_group_object_type)
|
||||
(event_object_type)
|
||||
(betting_market_rules_object_type)
|
||||
(betting_market_group_object_type)
|
||||
(betting_market_object_type)
|
||||
(bet_object_type)
|
||||
(tournament_object_type)
|
||||
(tournament_details_object_type)
|
||||
(match_object_type)
|
||||
(game_object_type)
|
||||
(OBJECT_TYPE_COUNT)
|
||||
)
|
||||
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
||||
|
|
@ -360,6 +423,11 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
|||
(impl_special_authority_object_type)
|
||||
(impl_buyback_object_type)
|
||||
(impl_fba_accumulator_object_type)
|
||||
(impl_betting_market_position_object_type)
|
||||
(impl_global_betting_statistics_object_type)
|
||||
(impl_asset_dividend_data_type)
|
||||
(impl_pending_dividend_payout_balance_for_holder_object_type)
|
||||
(impl_distributed_dividend_balance_data_type)
|
||||
)
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::share_type )
|
||||
|
|
@ -378,6 +446,14 @@ FC_REFLECT_TYPENAME( graphene::chain::withdraw_permission_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::vesting_balance_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::worker_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::balance_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::sport_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::event_group_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::event_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::betting_market_rules_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::betting_market_group_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::betting_market_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::bet_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::tournament_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::global_property_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::dynamic_global_property_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::asset_dynamic_data_id_type )
|
||||
|
|
@ -391,6 +467,9 @@ FC_REFLECT_TYPENAME( graphene::chain::budget_record_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::special_authority_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::buyback_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::fba_accumulator_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::betting_market_position_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::global_betting_statistics_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type )
|
||||
|
||||
FC_REFLECT( graphene::chain::void_t, )
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,6 @@ void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo );
|
|||
|
||||
} // fc
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::vote_id_type::vote_type )
|
||||
FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(VOTE_TYPE_COUNT) )
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ namespace graphene { namespace chain {
|
|||
account_id_type witness_account;
|
||||
string url;
|
||||
public_key_type block_signing_key;
|
||||
secret_hash_type initial_secret;
|
||||
|
||||
account_id_type fee_payer()const { return witness_account; }
|
||||
void validate()const;
|
||||
|
|
@ -67,6 +68,8 @@ namespace graphene { namespace chain {
|
|||
optional< string > new_url;
|
||||
/// The new block signing key.
|
||||
optional< public_key_type > new_signing_key;
|
||||
/// The new secreat hash.
|
||||
optional<secret_hash_type> new_initial_secret;
|
||||
|
||||
account_id_type fee_payer()const { return witness_account; }
|
||||
void validate()const;
|
||||
|
|
@ -77,7 +80,7 @@ namespace graphene { namespace chain {
|
|||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::witness_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::witness_create_operation, (fee)(witness_account)(url)(block_signing_key) )
|
||||
FC_REFLECT( graphene::chain::witness_create_operation, (fee)(witness_account)(url)(block_signing_key)(initial_secret) )
|
||||
|
||||
FC_REFLECT( graphene::chain::witness_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::witness_update_operation, (fee)(witness)(witness_account)(new_url)(new_signing_key) )
|
||||
FC_REFLECT( graphene::chain::witness_update_operation, (fee)(witness)(witness_account)(new_url)(new_signing_key)(new_initial_secret) )
|
||||
|
|
|
|||
|
|
@ -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 <string>
|
||||
|
||||
#include <fc/crypto/sha256.hpp>
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
#include <fc/optional.hpp>
|
||||
#include <fc/static_variant.hpp>
|
||||
#include <fc/array.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
struct rock_paper_scissors_game_details
|
||||
{
|
||||
// note: I wanted to declare these as fixed arrays, but they don't serialize properly
|
||||
//fc::array<fc::optional<rock_paper_scissors_throw_commit>, 2> commit_moves;
|
||||
//fc::array<fc::optional<rock_paper_scissors_throw_reveal>, 2> reveal_moves;
|
||||
std::vector<fc::optional<rock_paper_scissors_throw_commit> > commit_moves;
|
||||
std::vector<fc::optional<rock_paper_scissors_throw_reveal> > reveal_moves;
|
||||
rock_paper_scissors_game_details() :
|
||||
commit_moves(2),
|
||||
reveal_moves(2)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef fc::static_variant<rock_paper_scissors_game_details> game_specific_details;
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::rock_paper_scissors_game_details,
|
||||
(commit_moves)(reveal_moves) )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::game_specific_details )
|
||||
|
||||
50
libraries/chain/include/graphene/chain/sport_evaluator.hpp
Normal file
50
libraries/chain/include/graphene/chain/sport_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/operations.hpp>
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class sport_create_evaluator : public evaluator<sport_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sport_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const sport_create_operation& o );
|
||||
object_id_type do_apply( const sport_create_operation& o );
|
||||
};
|
||||
|
||||
class sport_update_evaluator : public evaluator<sport_update_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sport_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const sport_update_operation& o );
|
||||
void_result do_apply( const sport_update_operation& o );
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
51
libraries/chain/include/graphene/chain/sport_object.hpp
Normal file
51
libraries/chain/include/graphene/chain/sport_object.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 {
|
||||
|
||||
class database;
|
||||
|
||||
class sport_object : public graphene::db::abstract_object< sport_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = sport_object_type;
|
||||
|
||||
internationalized_string_type name;
|
||||
};
|
||||
|
||||
typedef multi_index_container<
|
||||
sport_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > > > > sport_object_multi_index_type;
|
||||
|
||||
typedef generic_index<sport_object, sport_object_multi_index_type> sport_object_index;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::sport_object, (graphene::db::object), (name) )
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class tournament_create_evaluator : public evaluator<tournament_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef tournament_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const tournament_create_operation& o );
|
||||
object_id_type do_apply( const tournament_create_operation& o );
|
||||
};
|
||||
|
||||
class tournament_join_evaluator : public evaluator<tournament_join_evaluator>
|
||||
{
|
||||
private:
|
||||
const tournament_object* _tournament_obj = nullptr;
|
||||
const tournament_details_object* _tournament_details_obj = nullptr;
|
||||
const account_object* _payer_account = nullptr;
|
||||
const asset_object* _buy_in_asset_type = nullptr;
|
||||
public:
|
||||
typedef tournament_join_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const tournament_join_operation& o );
|
||||
void_result do_apply( const tournament_join_operation& o );
|
||||
};
|
||||
|
||||
class tournament_leave_evaluator : public evaluator<tournament_leave_evaluator>
|
||||
{
|
||||
private:
|
||||
const tournament_object* _tournament_obj = nullptr;
|
||||
const tournament_details_object* _tournament_details_obj = nullptr;
|
||||
public:
|
||||
typedef tournament_leave_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const tournament_leave_operation& o );
|
||||
void_result do_apply( const tournament_leave_operation& o );
|
||||
};
|
||||
|
||||
class game_move_evaluator : public evaluator<game_move_evaluator>
|
||||
{
|
||||
private:
|
||||
const game_object* _game_obj = nullptr;
|
||||
public:
|
||||
typedef game_move_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const game_move_operation& o );
|
||||
void_result do_apply( const game_move_operation& o );
|
||||
};
|
||||
|
||||
|
||||
} }
|
||||
249
libraries/chain/include/graphene/chain/tournament_object.hpp
Normal file
249
libraries/chain/include/graphene/chain/tournament_object.hpp
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
#include <graphene/chain/rock_paper_scissors.hpp>
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
#include <graphene/db/flat_index.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class tournament_object;
|
||||
} }
|
||||
|
||||
namespace fc {
|
||||
void to_variant(const graphene::chain::tournament_object& tournament_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::tournament_object& tournament_obj);
|
||||
} //end namespace fc
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class database;
|
||||
using namespace graphene::db;
|
||||
|
||||
/// The tournament object has a lot of details, most of which are only of interest to anyone
|
||||
/// involved in the tournament. The main `tournament_object` contains all of the information
|
||||
/// needed to display an overview of the tournament, this object contains the rest.
|
||||
class tournament_details_object : public graphene::db::abstract_object<tournament_details_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = tournament_details_object_type;
|
||||
|
||||
/// the tournament object for which this is the details
|
||||
tournament_id_type tournament_id;
|
||||
|
||||
/// List of players registered for this tournament
|
||||
flat_set<account_id_type> registered_players;
|
||||
|
||||
/// List of payers who have contributed to the prize pool
|
||||
flat_map<account_id_type, share_type> payers;
|
||||
|
||||
/// List of player payer pairs needed by torunament leave operation
|
||||
flat_map<account_id_type, account_id_type> players_payers;
|
||||
|
||||
/// List of all matches in this tournament. When the tournament starts, all matches
|
||||
/// are created. Matches in the first round will have players, matches in later
|
||||
/// rounds will not be populated.
|
||||
vector<match_id_type> matches;
|
||||
};
|
||||
|
||||
enum class tournament_state
|
||||
{
|
||||
accepting_registrations,
|
||||
awaiting_start,
|
||||
in_progress,
|
||||
registration_period_expired,
|
||||
concluded
|
||||
};
|
||||
|
||||
class tournament_object : public graphene::db::abstract_object<tournament_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = tournament_object_type;
|
||||
|
||||
tournament_object();
|
||||
tournament_object(const tournament_object& rhs);
|
||||
~tournament_object();
|
||||
tournament_object& operator=(const tournament_object& rhs);
|
||||
|
||||
tournament_id_type get_id() const { return id; };
|
||||
/// the account that created this tournament
|
||||
account_id_type creator;
|
||||
|
||||
/// the options set when creating the tournament
|
||||
tournament_options options;
|
||||
|
||||
/// If the tournament has started, the time it started
|
||||
optional<time_point_sec> start_time;
|
||||
/// If the tournament has ended, the time it ended
|
||||
optional<time_point_sec> end_time;
|
||||
|
||||
/// Total prize pool accumulated
|
||||
/// This is the sum of all payers in the details object, and will be
|
||||
/// registered_players.size() * buy_in_amount
|
||||
share_type prize_pool;
|
||||
|
||||
/// The number of players registered for the tournament
|
||||
/// (same as the details object's registered_players.size(), here to avoid
|
||||
/// the GUI having to get the details object)
|
||||
uint32_t registered_players = 0;
|
||||
|
||||
/// The current high-level status of the tournament (whether it is currently running or has been canceled, etc)
|
||||
//tournament_state state;
|
||||
|
||||
/// Detailed information on this tournament
|
||||
tournament_details_id_type tournament_details_id;
|
||||
|
||||
tournament_state get_state() const;
|
||||
|
||||
time_point_sec get_registration_deadline() const { return options.registration_deadline; }
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const tournament_object& tournament_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, tournament_object& tournament_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::tournament_object& tournament_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::tournament_object& tournament_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
|
||||
/// called by database maintenance code when registration for this contest has expired
|
||||
void on_registration_deadline_passed(database& db);
|
||||
void on_player_registered(database& db, account_id_type payer_id, account_id_type player_id);
|
||||
void on_player_unregistered(database& db, account_id_type player_id);
|
||||
void on_start_time_arrived(database& db);
|
||||
void on_match_completed(database& db, const match_object& match);
|
||||
|
||||
void check_for_new_matches_to_start(database& db) const;
|
||||
private:
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
struct by_registration_deadline {};
|
||||
struct by_start_time {};
|
||||
typedef multi_index_container<
|
||||
tournament_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag<by_registration_deadline>,
|
||||
composite_key<tournament_object,
|
||||
const_mem_fun<tournament_object, tournament_state, &tournament_object::get_state>,
|
||||
const_mem_fun<tournament_object, time_point_sec, &tournament_object::get_registration_deadline> > >,
|
||||
ordered_non_unique< tag<by_start_time>,
|
||||
composite_key<tournament_object,
|
||||
const_mem_fun<tournament_object, tournament_state, &tournament_object::get_state>,
|
||||
member<tournament_object, optional<time_point_sec>, &tournament_object::start_time> > >
|
||||
>
|
||||
> tournament_object_multi_index_type;
|
||||
typedef generic_index<tournament_object, tournament_object_multi_index_type> tournament_index;
|
||||
|
||||
typedef multi_index_container<
|
||||
tournament_details_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > > >
|
||||
> tournament_details_object_multi_index_type;
|
||||
typedef generic_index<tournament_details_object, tournament_details_object_multi_index_type> tournament_details_index;
|
||||
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator<<( Stream& s, const tournament_object& tournament_obj )
|
||||
{
|
||||
fc_elog(fc::logger::get("tournament"), "In tournament_obj to_raw");
|
||||
// pack all fields exposed in the header in the usual way
|
||||
// instead of calling the derived pack, just serialize the one field in the base class
|
||||
// fc::raw::pack<Stream, const graphene::db::abstract_object<tournament_object> >(s, tournament_obj);
|
||||
fc::raw::pack(s, tournament_obj.id);
|
||||
fc::raw::pack(s, tournament_obj.creator);
|
||||
fc::raw::pack(s, tournament_obj.options);
|
||||
fc::raw::pack(s, tournament_obj.start_time);
|
||||
fc::raw::pack(s, tournament_obj.end_time);
|
||||
fc::raw::pack(s, tournament_obj.prize_pool);
|
||||
fc::raw::pack(s, tournament_obj.registered_players);
|
||||
fc::raw::pack(s, tournament_obj.tournament_details_id);
|
||||
|
||||
// fc::raw::pack the contents hidden in the impl class
|
||||
std::ostringstream stream;
|
||||
tournament_obj.pack_impl(stream);
|
||||
std::string stringified_stream(stream.str());
|
||||
fc_elog(fc::logger::get("tournament"), "Serialized state ${state} to bytes ${bytes}",
|
||||
("state", tournament_obj.get_state())("bytes", fc::to_hex(stringified_stream.c_str(), stringified_stream.size())));
|
||||
fc::raw::pack(s, stream.str());
|
||||
|
||||
return s;
|
||||
}
|
||||
template<typename Stream>
|
||||
inline Stream& operator>>( Stream& s, tournament_object& tournament_obj )
|
||||
{
|
||||
fc_elog(fc::logger::get("tournament"), "In tournament_obj from_raw");
|
||||
// unpack all fields exposed in the header in the usual way
|
||||
//fc::raw::unpack<Stream, graphene::db::abstract_object<tournament_object> >(s, tournament_obj);
|
||||
fc::raw::unpack(s, tournament_obj.id);
|
||||
fc::raw::unpack(s, tournament_obj.creator);
|
||||
fc::raw::unpack(s, tournament_obj.options);
|
||||
fc::raw::unpack(s, tournament_obj.start_time);
|
||||
fc::raw::unpack(s, tournament_obj.end_time);
|
||||
fc::raw::unpack(s, tournament_obj.prize_pool);
|
||||
fc::raw::unpack(s, tournament_obj.registered_players);
|
||||
fc::raw::unpack(s, tournament_obj.tournament_details_id);
|
||||
|
||||
// fc::raw::unpack the contents hidden in the impl class
|
||||
std::string stringified_stream;
|
||||
fc::raw::unpack(s, stringified_stream);
|
||||
std::istringstream stream(stringified_stream);
|
||||
tournament_obj.unpack_impl(stream);
|
||||
fc_elog(fc::logger::get("tournament"), "Deserialized state ${state} from bytes ${bytes}",
|
||||
("state", tournament_obj.get_state())("bytes", fc::to_hex(stringified_stream.c_str(), stringified_stream.size())));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This secondary index will allow a reverse lookup of all tournaments
|
||||
* a particular account has registered for. This will be attached
|
||||
* to the tournament details index because the registrations are contained
|
||||
* in the tournament details object, but it will index the tournament ids
|
||||
* since that is most useful to the GUI.
|
||||
*/
|
||||
class tournament_players_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, map it to the set of tournaments in which that account is registered as a player */
|
||||
map< account_id_type, flat_set<tournament_id_type> > account_to_joined_tournaments;
|
||||
|
||||
vector<tournament_id_type> get_registered_tournaments_for_account( const account_id_type& a )const;
|
||||
protected:
|
||||
|
||||
flat_set<account_id_type> before_account_ids;
|
||||
};
|
||||
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::chain::tournament_details_object, (graphene::db::object),
|
||||
(tournament_id)
|
||||
(registered_players)
|
||||
(payers)
|
||||
(players_payers)
|
||||
(matches))
|
||||
//FC_REFLECT_TYPENAME(graphene::chain::tournament_object) // manually serialized
|
||||
FC_REFLECT(graphene::chain::tournament_object, (creator))
|
||||
FC_REFLECT_ENUM(graphene::chain::tournament_state,
|
||||
(accepting_registrations)
|
||||
(awaiting_start)
|
||||
(in_progress)
|
||||
(registration_period_expired)
|
||||
(concluded))
|
||||
|
||||
|
|
@ -31,8 +31,10 @@
|
|||
#include <fc/uint128.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
|
||||
|
||||
#define offset_d(i,f) (long(&(i)->f) - long(i))
|
||||
#define offset_s(t,f) offset_d((t*)1000, f)
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
|
@ -171,13 +173,29 @@ namespace graphene { namespace chain {
|
|||
* @ingroup object_index
|
||||
*/
|
||||
struct by_account;
|
||||
struct by_asset_balance;
|
||||
typedef multi_index_container<
|
||||
vesting_balance_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag<by_account>,
|
||||
member<vesting_balance_object, account_id_type, &vesting_balance_object::owner>
|
||||
>
|
||||
>,
|
||||
//ordered_unique< tag<by_asset_balance>,
|
||||
ordered_non_unique< tag<by_asset_balance>,
|
||||
composite_key<
|
||||
vesting_balance_object,
|
||||
member_offset<vesting_balance_object, asset_id_type, (size_t) (offset_s(vesting_balance_object,balance) + offset_s(asset,asset_id))>,
|
||||
member_offset<vesting_balance_object, share_type, (size_t) (offset_s(vesting_balance_object,balance) + offset_s(asset,amount))>
|
||||
//member<vesting_balance_object, account_id_type, &vesting_balance_object::owner>
|
||||
//member_offset<vesting_balance_object, account_id_type, (size_t) (offset_s(vesting_balance_object,owner))>
|
||||
>,
|
||||
composite_key_compare<
|
||||
std::less< asset_id_type >,
|
||||
std::greater< share_type >
|
||||
//std::less< account_id_type >
|
||||
>
|
||||
>
|
||||
>
|
||||
> vesting_balance_multi_index_type;
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ namespace graphene { namespace chain {
|
|||
account_id_type witness_account;
|
||||
uint64_t last_aslot = 0;
|
||||
public_key_type signing_key;
|
||||
secret_hash_type next_secret_hash;
|
||||
secret_hash_type previous_secret;
|
||||
optional< vesting_balance_id_type > pay_vb;
|
||||
vote_id_type vote_id;
|
||||
uint64_t total_votes = 0;
|
||||
|
|
@ -74,6 +76,8 @@ FC_REFLECT_DERIVED( graphene::chain::witness_object, (graphene::db::object),
|
|||
(witness_account)
|
||||
(last_aslot)
|
||||
(signing_key)
|
||||
(next_secret_hash)
|
||||
(previous_secret)
|
||||
(pay_vb)
|
||||
(vote_id)
|
||||
(total_votes)
|
||||
|
|
|
|||
|
|
@ -25,11 +25,34 @@
|
|||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <graphene/chain/witness_scheduler.hpp>
|
||||
#include <graphene/chain/witness_scheduler_rng.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class witness_schedule_object;
|
||||
|
||||
typedef hash_ctr_rng<
|
||||
/* HashClass = */ fc::sha256,
|
||||
/* SeedLength = */ GRAPHENE_RNG_SEED_LENGTH
|
||||
> witness_scheduler_rng;
|
||||
|
||||
typedef generic_witness_scheduler<
|
||||
/* WitnessID = */ witness_id_type,
|
||||
/* RNG = */ witness_scheduler_rng,
|
||||
/* CountType = */ decltype( chain_parameters::maximum_witness_count ),
|
||||
/* OffsetType = */ uint32_t,
|
||||
/* debug = */ true
|
||||
> witness_scheduler;
|
||||
|
||||
typedef generic_far_future_witness_scheduler<
|
||||
/* WitnessID = */ witness_id_type,
|
||||
/* RNG = */ witness_scheduler_rng,
|
||||
/* CountType = */ decltype( chain_parameters::maximum_witness_count ),
|
||||
/* OffsetType = */ uint32_t,
|
||||
/* debug = */ true
|
||||
> far_future_witness_scheduler;
|
||||
|
||||
class witness_schedule_object : public graphene::db::abstract_object<witness_schedule_object>
|
||||
{
|
||||
public:
|
||||
|
|
@ -37,12 +60,39 @@ class witness_schedule_object : public graphene::db::abstract_object<witness_sch
|
|||
static const uint8_t type_id = impl_witness_schedule_object_type;
|
||||
|
||||
vector< witness_id_type > current_shuffled_witnesses;
|
||||
|
||||
witness_scheduler scheduler;
|
||||
uint32_t last_scheduling_block;
|
||||
uint64_t slots_since_genesis = 0;
|
||||
fc::array< char, sizeof(secret_hash_type) > rng_seed;
|
||||
|
||||
/**
|
||||
* Not necessary for consensus, but used for figuring out the participation rate.
|
||||
* The nth bit is 0 if the nth slot was unfilled, else it is 1.
|
||||
*/
|
||||
fc::uint128 recent_slots_filled;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
|
||||
FC_REFLECT( graphene::chain::witness_scheduler,
|
||||
(_turns)
|
||||
(_tokens)
|
||||
(_min_token_count)
|
||||
(_ineligible_waiting_for_token)
|
||||
(_ineligible_no_turn)
|
||||
(_eligible)
|
||||
(_schedule)
|
||||
(_lame_duck)
|
||||
)
|
||||
FC_REFLECT_DERIVED(
|
||||
graphene::chain::witness_schedule_object,
|
||||
(graphene::db::object),
|
||||
(scheduler)
|
||||
(last_scheduling_block)
|
||||
(slots_since_genesis)
|
||||
(rng_seed)
|
||||
(recent_slots_filled)
|
||||
(current_shuffled_witnesses)
|
||||
)
|
||||
|
|
|
|||
440
libraries/chain/include/graphene/chain/witness_scheduler.hpp
Normal file
440
libraries/chain/include/graphene/chain/witness_scheduler.hpp
Normal file
|
|
@ -0,0 +1,440 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Cryptonomex, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
|
||||
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted until September 8, 2015, provided that the following conditions are met:
|
||||
*
|
||||
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
// @ROL helpfull dumps when debugging
|
||||
//#define ROL_DUMP
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
//using boost::container::flat_set;
|
||||
|
||||
enum witness_scheduler_relax_flags
|
||||
{
|
||||
emit_turn = 0x01,
|
||||
emit_token = 0x02
|
||||
};
|
||||
|
||||
template< typename WitnessID, typename RNG, typename CountType, typename OffsetType, bool debug = true >
|
||||
class generic_witness_scheduler
|
||||
{
|
||||
public:
|
||||
void check_invariant() const
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
CountType tokens = _ineligible_no_turn.size() + _eligible.size();
|
||||
CountType turns = _eligible.size();
|
||||
for( const std::pair< WitnessID, bool >& item : _ineligible_waiting_for_token )
|
||||
turns += (item.second ? 1 : 0 );
|
||||
|
||||
assert( _tokens == tokens );
|
||||
assert( _turns == turns );
|
||||
#endif
|
||||
|
||||
set< WitnessID > witness_set;
|
||||
// make sure each witness_id occurs only once among the three states
|
||||
auto process_id = [&]( WitnessID item )
|
||||
{
|
||||
assert( witness_set.find( item ) == witness_set.end() );
|
||||
witness_set.insert( item );
|
||||
} ;
|
||||
|
||||
for( const std::pair< WitnessID, bool >& item : _ineligible_waiting_for_token )
|
||||
process_id( item.first );
|
||||
for( const WitnessID& item : _ineligible_no_turn )
|
||||
process_id( item );
|
||||
for( const WitnessID& item : _eligible )
|
||||
process_id( item );
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deterministically evolve over time
|
||||
*/
|
||||
uint32_t relax()
|
||||
{
|
||||
uint32_t relax_flags = 0;
|
||||
|
||||
if( debug ) check_invariant();
|
||||
assert( _min_token_count > 0 );
|
||||
|
||||
// turn distribution
|
||||
if( _turns == 0 )
|
||||
{
|
||||
relax_flags |= emit_turn;
|
||||
for( const WitnessID& item : _ineligible_no_turn )
|
||||
_eligible.push_back( item );
|
||||
_turns += _ineligible_no_turn.size();
|
||||
_ineligible_no_turn.clear();
|
||||
if( debug ) check_invariant();
|
||||
|
||||
for( std::pair< WitnessID, bool >& item : _ineligible_waiting_for_token )
|
||||
{
|
||||
assert( item.second == false );
|
||||
item.second = true;
|
||||
}
|
||||
_turns += _ineligible_waiting_for_token.size();
|
||||
if( debug ) check_invariant();
|
||||
}
|
||||
|
||||
// token distribution
|
||||
while( true )
|
||||
{
|
||||
if( _ineligible_waiting_for_token.empty() )
|
||||
{
|
||||
// eligible must be non-empty
|
||||
assert( !_eligible.empty() );
|
||||
return relax_flags;
|
||||
}
|
||||
|
||||
if( _tokens >= _min_token_count )
|
||||
{
|
||||
if( !_eligible.empty() )
|
||||
return relax_flags;
|
||||
}
|
||||
|
||||
const std::pair< WitnessID, bool >& item = _ineligible_waiting_for_token.front();
|
||||
if( item.second )
|
||||
_eligible.push_back( item.first );
|
||||
else
|
||||
_ineligible_no_turn.push_back( item.first );
|
||||
_ineligible_waiting_for_token.pop_front();
|
||||
relax_flags |= emit_token;
|
||||
_tokens++;
|
||||
if( debug ) check_invariant();
|
||||
}
|
||||
|
||||
return relax_flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add another element to _schedule
|
||||
*/
|
||||
uint32_t produce_schedule( RNG& rng )
|
||||
{
|
||||
uint32_t relax_flags = relax();
|
||||
if( debug ) check_invariant();
|
||||
if( _eligible.empty() )
|
||||
return relax_flags;
|
||||
|
||||
decltype( rng( _eligible.size() ) ) pos = rng( _eligible.size() );
|
||||
assert( (pos >= 0) && (pos < _eligible.size()) );
|
||||
auto it = _eligible.begin() + pos;
|
||||
_schedule.push_back( *it );
|
||||
_ineligible_waiting_for_token.emplace_back( *it, false );
|
||||
_eligible.erase( it );
|
||||
_turns--;
|
||||
_tokens--;
|
||||
if( debug ) check_invariant();
|
||||
return relax_flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull an element from _schedule
|
||||
*/
|
||||
WitnessID consume_schedule()
|
||||
{
|
||||
assert( _schedule.size() > 0 );
|
||||
|
||||
WitnessID result = _schedule.front();
|
||||
_schedule.pop_front();
|
||||
|
||||
auto it = _lame_duck.find( result );
|
||||
if( it != _lame_duck.end() )
|
||||
_lame_duck.erase( it );
|
||||
if( debug ) check_invariant();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all witnesses in the removal_set from
|
||||
* future scheduling (but not from the current schedule).
|
||||
*/
|
||||
template< typename T >
|
||||
void remove_all( const T& removal_set )
|
||||
{
|
||||
if( debug ) check_invariant();
|
||||
|
||||
_ineligible_waiting_for_token.erase(
|
||||
std::remove_if(
|
||||
_ineligible_waiting_for_token.begin(),
|
||||
_ineligible_waiting_for_token.end(),
|
||||
[&]( const std::pair< WitnessID, bool >& item ) -> bool
|
||||
{
|
||||
bool found = removal_set.find( item.first ) != removal_set.end();
|
||||
_turns -= (found & item.second) ? 1 : 0;
|
||||
return found;
|
||||
} ),
|
||||
_ineligible_waiting_for_token.end() );
|
||||
if( debug ) check_invariant();
|
||||
|
||||
_ineligible_no_turn.erase(
|
||||
std::remove_if(
|
||||
_ineligible_no_turn.begin(),
|
||||
_ineligible_no_turn.end(),
|
||||
[&]( WitnessID item ) -> bool
|
||||
{
|
||||
bool found = (removal_set.find( item ) != removal_set.end());
|
||||
_tokens -= (found ? 1 : 0);
|
||||
return found;
|
||||
} ),
|
||||
_ineligible_no_turn.end() );
|
||||
if( debug ) check_invariant();
|
||||
|
||||
_eligible.erase(
|
||||
std::remove_if(
|
||||
_eligible.begin(),
|
||||
_eligible.end(),
|
||||
[&]( WitnessID item ) -> bool
|
||||
{
|
||||
bool found = (removal_set.find( item ) != removal_set.end());
|
||||
_tokens -= (found ? 1 : 0);
|
||||
_turns -= (found ? 1 : 0);
|
||||
return found;
|
||||
} ),
|
||||
_eligible.end() );
|
||||
if( debug ) check_invariant();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to call insert_all() and remove_all()
|
||||
* as needed to update to the given revised_set.
|
||||
*/
|
||||
template< typename T >
|
||||
void insert_all( const T& insertion_set )
|
||||
{
|
||||
if( debug ) check_invariant();
|
||||
for( const WitnessID wid : insertion_set )
|
||||
{
|
||||
_eligible.push_back( wid );
|
||||
}
|
||||
_turns += insertion_set.size();
|
||||
_tokens += insertion_set.size();
|
||||
if( debug ) check_invariant();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to call insert_all() and remove_all()
|
||||
* as needed to update to the given revised_set.
|
||||
*
|
||||
* This function calls find() on revised_set for all current
|
||||
* witnesses. Running time is O(n*log(n)) if the revised_set
|
||||
* implementation of find() is O(log(n)).
|
||||
*
|
||||
* TODO: Rewriting to use std::set_difference may marginally
|
||||
* increase efficiency, but a benchmark is needed to justify this.
|
||||
*/
|
||||
template< typename T >
|
||||
void update( const T& revised_set )
|
||||
{
|
||||
#ifdef ROL_DUMP
|
||||
wdump((revised_set));
|
||||
#endif
|
||||
set< WitnessID > current_set;
|
||||
set< WitnessID > schedule_set;
|
||||
|
||||
/* current_set.reserve(
|
||||
_ineligible_waiting_for_token.size()
|
||||
+ _ineligible_no_turn.size()
|
||||
+ _eligible.size()
|
||||
+ _schedule.size() );
|
||||
*/
|
||||
for( const auto& item : _ineligible_waiting_for_token )
|
||||
current_set.insert( item.first );
|
||||
for( const WitnessID& item : _ineligible_no_turn )
|
||||
current_set.insert( item );
|
||||
for( const WitnessID& item : _eligible )
|
||||
current_set.insert( item );
|
||||
for( const WitnessID& item : _schedule )
|
||||
{
|
||||
current_set.insert( item );
|
||||
schedule_set.insert( item );
|
||||
}
|
||||
|
||||
set< WitnessID > insertion_set;
|
||||
//insertion_set.reserve( revised_set.size() );
|
||||
for( const WitnessID& item : revised_set )
|
||||
{
|
||||
if( current_set.find( item ) == current_set.end() )
|
||||
insertion_set.insert( item );
|
||||
}
|
||||
|
||||
set< WitnessID > removal_set;
|
||||
//removal_set.reserve( current_set.size() );
|
||||
for( const WitnessID& item : current_set )
|
||||
{
|
||||
if( revised_set.find( item ) == revised_set.end() )
|
||||
{
|
||||
if( schedule_set.find( item ) == schedule_set.end() )
|
||||
removal_set.insert( item );
|
||||
else
|
||||
_lame_duck.insert( item );
|
||||
}
|
||||
}
|
||||
|
||||
insert_all( insertion_set );
|
||||
remove_all( removal_set );
|
||||
#ifdef ROL_DUMP
|
||||
wdump((_eligible));
|
||||
wdump((_schedule));
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of scheduled witnesses
|
||||
*/
|
||||
|
||||
size_t size( )const
|
||||
{
|
||||
return _schedule.size();
|
||||
}
|
||||
|
||||
bool get_slot( OffsetType offset, WitnessID& wit )const
|
||||
{
|
||||
#ifdef ROL_DUMP
|
||||
wdump((_schedule)(_schedule.size())(offset));
|
||||
#endif
|
||||
if (_schedule.empty())
|
||||
return false; //@ROL wit is 1.6.0!
|
||||
if( offset >= _schedule.size() )
|
||||
{
|
||||
wit = _schedule[ 0 ];
|
||||
return false;
|
||||
}
|
||||
wit = _schedule[ offset ];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the schedule, then re-schedule the given witness as the
|
||||
* first witness.
|
||||
*/
|
||||
void reset_schedule( WitnessID first_witness )
|
||||
{
|
||||
#ifdef ROL_DUMP
|
||||
wdump((first_witness));
|
||||
#endif
|
||||
_schedule.clear();
|
||||
for( const WitnessID& wid : _ineligible_no_turn )
|
||||
{
|
||||
_eligible.push_back( wid );
|
||||
}
|
||||
_turns += _ineligible_no_turn.size();
|
||||
_ineligible_no_turn.clear();
|
||||
for( const auto& item : _ineligible_waiting_for_token )
|
||||
{
|
||||
_eligible.push_back( item.first );
|
||||
_turns += (item.second ? 0 : 1);
|
||||
}
|
||||
_tokens += _ineligible_waiting_for_token.size();
|
||||
_ineligible_waiting_for_token.clear();
|
||||
if( debug ) check_invariant();
|
||||
#ifdef ROL_DUMP
|
||||
wdump((_eligible));
|
||||
#endif
|
||||
auto it = std::find( _eligible.begin(), _eligible.end(), first_witness );
|
||||
//@ROL: maybe good idea, when wit is 1.6.0 if ( it == _eligible.end() ) return;
|
||||
assert( it != _eligible.end() );
|
||||
|
||||
_schedule.push_back( *it );
|
||||
_ineligible_waiting_for_token.emplace_back( *it, false );
|
||||
_eligible.erase( it );
|
||||
_turns--;
|
||||
_tokens--;
|
||||
if( debug ) check_invariant();
|
||||
return;
|
||||
}
|
||||
|
||||
// keep track of total turns / tokens in existence
|
||||
CountType _turns = 0;
|
||||
CountType _tokens = 0;
|
||||
|
||||
// new tokens handed out when _tokens < _min_token_count
|
||||
CountType _min_token_count;
|
||||
|
||||
// WitnessID appears in exactly one of the following:
|
||||
// has no token; second indicates whether we have a turn or not:
|
||||
std::deque < std::pair< WitnessID, bool > > _ineligible_waiting_for_token; // ".." | "T."
|
||||
// has token, but no turn
|
||||
std::vector< WitnessID > _ineligible_no_turn; // ".t"
|
||||
// has token and turn
|
||||
std::vector< WitnessID > _eligible; // "Tt"
|
||||
|
||||
// scheduled
|
||||
std::deque < WitnessID > _schedule;
|
||||
|
||||
// in _schedule, but not to be replaced
|
||||
set< WitnessID > _lame_duck;
|
||||
};
|
||||
|
||||
template< typename WitnessID, typename RNG, typename CountType, typename OffsetType, bool debug = true >
|
||||
class generic_far_future_witness_scheduler
|
||||
{
|
||||
public:
|
||||
generic_far_future_witness_scheduler(
|
||||
const generic_witness_scheduler< WitnessID, RNG, CountType, OffsetType, debug >& base_scheduler,
|
||||
RNG rng
|
||||
)
|
||||
{
|
||||
generic_witness_scheduler< WitnessID, RNG, CountType, OffsetType, debug > extended_scheduler = base_scheduler;
|
||||
_begin_offset = base_scheduler.size()+1;
|
||||
while( (extended_scheduler.produce_schedule( rng ) & emit_turn) == 0 )
|
||||
_begin_offset++;
|
||||
assert( _begin_offset == extended_scheduler.size() );
|
||||
|
||||
_end_offset = _begin_offset;
|
||||
while( (extended_scheduler.produce_schedule( rng ) & emit_turn) == 0 )
|
||||
_end_offset++;
|
||||
assert( _end_offset == extended_scheduler.size()-1 );
|
||||
_schedule.resize( extended_scheduler._schedule.size() );
|
||||
std::copy( extended_scheduler._schedule.begin(),
|
||||
extended_scheduler._schedule.end(),
|
||||
_schedule.begin() );
|
||||
return;
|
||||
}
|
||||
|
||||
bool get_slot( OffsetType offset, WitnessID& wit )const
|
||||
{
|
||||
if( offset <= _end_offset )
|
||||
wit = _schedule[ offset ];
|
||||
else
|
||||
wit = _schedule[ _begin_offset +
|
||||
(
|
||||
(offset - _begin_offset) %
|
||||
(_end_offset + 1 - _begin_offset)
|
||||
) ];
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector< WitnessID > _schedule;
|
||||
OffsetType _begin_offset;
|
||||
OffsetType _end_offset;
|
||||
};
|
||||
|
||||
} }
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue