Merged beatrice into 5050
This commit is contained in:
parent
dfffabd004
commit
aa3128fe89
192 changed files with 244632 additions and 218620 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -41,3 +41,5 @@ object_database/*
|
|||
|
||||
*.pyc
|
||||
*.pyo
|
||||
.vscode
|
||||
.DS_Store
|
||||
|
|
|
|||
2
.gitmodules
vendored
2
.gitmodules
vendored
|
|
@ -4,5 +4,5 @@
|
|||
ignore = dirty
|
||||
[submodule "libraries/fc"]
|
||||
path = libraries/fc
|
||||
url = https://bitbucket.org/peerplaysblockchain/peerplays-fc.git
|
||||
url = https://github.com/PBSA/peerplays-fc.git
|
||||
ignore = dirty
|
||||
|
|
|
|||
|
|
@ -32,13 +32,16 @@ if (USE_PCH)
|
|||
include (cotire)
|
||||
endif(USE_PCH)
|
||||
|
||||
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)
|
||||
|
|
|
|||
94
Dockerfile
94
Dockerfile
|
|
@ -1,22 +1,84 @@
|
|||
# This will build the witness_node in a docker image. Make sure you've already
|
||||
# checked out the submodules before building.
|
||||
FROM phusion/baseimage:0.9.19
|
||||
MAINTAINER PeerPlays Blockchain Standards Association
|
||||
|
||||
FROM l3iggs/archlinux:latest
|
||||
MAINTAINER Nathan Hourt <nathan@followmyvote.com>
|
||||
ENV LANG=en_US.UTF-8
|
||||
RUN \
|
||||
apt-get update -y && \
|
||||
apt-get install -y \
|
||||
g++ \
|
||||
autoconf \
|
||||
cmake \
|
||||
git \
|
||||
libbz2-dev \
|
||||
libreadline-dev \
|
||||
libboost-all-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libssl-dev \
|
||||
libncurses-dev \
|
||||
doxygen \
|
||||
ca-certificates \
|
||||
ntp \
|
||||
wget \
|
||||
&& \
|
||||
apt-get update -y && \
|
||||
apt-get install -y fish && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
RUN pacman -Syu --noconfirm gcc make autoconf automake cmake ninja boost libtool git
|
||||
ADD . /peerplays-core
|
||||
WORKDIR /peerplays-core
|
||||
|
||||
ADD . /bitshares-2
|
||||
WORKDIR /bitshares-2
|
||||
RUN cmake -G Ninja -DCMAKE_BUILD_TYPE=Release .
|
||||
RUN ninja witness_node || ninja -j 1 witness_node
|
||||
# Compile
|
||||
RUN \
|
||||
( git submodule sync --recursive || \
|
||||
find `pwd` -type f -name .git | \
|
||||
while read f; do \
|
||||
rel="$(echo "${f#$PWD/}" | sed 's=[^/]*/=../=g')"; \
|
||||
sed -i "s=: .*/.git/=: $rel/=" "$f"; \
|
||||
done && \
|
||||
git submodule sync --recursive ) && \
|
||||
git submodule update --init --recursive && \
|
||||
BOOST_ROOT=$HOME/opt/boost_1_60_0 && \
|
||||
wget -c 'http://sourceforge.net/projects/boost/files/boost/1.60.0/boost_1_60_0.tar.gz/download' -O boost_1_60_0.tar.gz &&\
|
||||
tar -zxvf boost_1_60_0.tar.gz && \
|
||||
cd boost_1_60_0/ && \
|
||||
./bootstrap.sh "--prefix=$BOOST_ROOT" && \
|
||||
./b2 install -j$(nproc) && \
|
||||
cd .. && \
|
||||
cmake \
|
||||
-DBOOST_ROOT="$BOOST_ROOT" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
. && \
|
||||
make witness_node cli_wallet -j$(nproc) && \
|
||||
install -s programs/witness_node/witness_node programs/cli_wallet/cli_wallet /usr/local/bin && \
|
||||
#
|
||||
# Obtain version
|
||||
mkdir /etc/peerplays && \
|
||||
git rev-parse --short HEAD > /etc/peerplays/version && \
|
||||
cd / && \
|
||||
rm -rf /peerplays-core
|
||||
|
||||
RUN mkdir /data_dir
|
||||
ADD docker/default_config.ini /default_config.ini
|
||||
ADD docker/launch /launch
|
||||
RUN chmod a+x /launch
|
||||
VOLUME /data_dir
|
||||
# Home directory $HOME
|
||||
WORKDIR /
|
||||
RUN useradd -s /bin/bash -m -d /var/lib/peerplays peerplays
|
||||
ENV HOME /var/lib/peerplays
|
||||
RUN chown peerplays:peerplays -R /var/lib/peerplays
|
||||
|
||||
EXPOSE 8090 9090
|
||||
# Volume
|
||||
VOLUME ["/var/lib/peerplays", "/etc/peerplays"]
|
||||
|
||||
ENTRYPOINT ["/launch"]
|
||||
# rpc service:
|
||||
EXPOSE 8090
|
||||
# p2p service:
|
||||
EXPOSE 1776
|
||||
|
||||
# default exec/config files
|
||||
ADD docker/default_config.ini /etc/peerplays/config.ini
|
||||
ADD docker/peerplaysentry.sh /usr/local/bin/peerplaysentry.sh
|
||||
RUN chmod a+x /usr/local/bin/peerplaysentry.sh
|
||||
|
||||
# Make Docker send SIGINT instead of SIGTERM to the daemon
|
||||
STOPSIGNAL SIGINT
|
||||
|
||||
# default execute entry
|
||||
CMD ["/usr/local/bin/peerplaysentry.sh"]
|
||||
|
|
|
|||
17
HEADER
17
HEADER
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Cryptonomex, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
|
||||
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted until September 8, 2015, provided that the following conditions are met:
|
||||
*
|
||||
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
Copyright (c) 2015-2016 Cryptonomex Inc. <contact@cryptonomex.com>
|
||||
Copyright (c) 2015-2017 contributors <CONTRIBUTORS.txt>
|
||||
Copyright (c) 2017 - 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
|
||||
The MIT License
|
||||
|
||||
|
|
@ -19,4 +21,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.
|
||||
|
||||
36
README.md
36
README.md
|
|
@ -6,10 +6,9 @@ This is a quick introduction to get new developers and witnesses up to speed on
|
|||
Starting A Peerplays Node
|
||||
-----------------
|
||||
|
||||
For Ubuntu 14.04 LTS and up users, see this link first:
|
||||
https://github.com/cryptonomex/graphene/wiki/build-ubuntu
|
||||
|
||||
and then proceed with:
|
||||
For Ubuntu 14.04 LTS and up users, see
|
||||
[this](https://github.com/cryptonomex/graphene/wiki/build-ubuntu) and
|
||||
then proceed with:
|
||||
|
||||
git clone https://github.com/pbsa/peerplays.git
|
||||
cd peerplays
|
||||
|
|
@ -20,7 +19,7 @@ and then proceed with:
|
|||
|
||||
Launching the witness creates required directories. Next, **stop the witness** and continue.
|
||||
|
||||
vi witness_node_data_dir/config.ini
|
||||
$ vi witness_node_data_dir/config.ini
|
||||
p2p-endpoint = 0.0.0.0:9777
|
||||
rpc-endpoint = 127.0.0.1:8090
|
||||
seed-node = 213.184.225.234:59500
|
||||
|
|
@ -29,6 +28,14 @@ Start the witness back up
|
|||
|
||||
./programs/witness_node/witness_node
|
||||
|
||||
Upgrading A Peerplays Node
|
||||
-----------------
|
||||
To minimize downtime of your peerplays node when upgrading, one upgrade
|
||||
idea was written in [this steemit
|
||||
article](https://steemit.com/peerplays/@joseph/peerplays-update-setting-a-backup-witness-server-switching-servers).
|
||||
|
||||
Wallet Setup
|
||||
-----------------
|
||||
Then, in a separate terminal window, start the command-line wallet `cli_wallet`:
|
||||
|
||||
./programs/cli_wallet/cli_wallet
|
||||
|
|
@ -38,21 +45,15 @@ To set your initial password to 'password' use:
|
|||
>>> set_password password
|
||||
>>> unlock password
|
||||
|
||||
|
||||
A list of CLI wallet commands is available
|
||||
[here](https://github.com/PBSA/peerplays/blob/master/libraries/wallet/include/graphene/wallet/wallet.hpp).
|
||||
|
||||
|
||||
Testnet
|
||||
Testnet - "Beatrice"
|
||||
----------------------
|
||||
- chain-id - 5b37954aa0d33a8e0d57b084995c262a7c13dbc0693d3e96654e63ff45a9ceec
|
||||
- chain-id - T.B.D.
|
||||
|
||||
Register your username at the faucet address
|
||||
---------------------------
|
||||
https://595-dev.pixelplex.by/
|
||||
|
||||
|
||||
Use the get_private_key_from_password command
|
||||
Use the `get_private_key_from_password` command
|
||||
---------------------------------
|
||||
You will to generate owner and active keys
|
||||
|
||||
|
|
@ -139,9 +140,6 @@ You will get logs that look like this:
|
|||
|
||||
```
|
||||
2070264ms th_a application.cpp:506 handle_block ] Got block: #87913 time: 2017-05-27T16:34:30 latency: 264 ms from: bhuz-witness irreversible: 87903 (-10)
|
||||
2071000ms th_a witness.cpp:204 block_production_loo ] Not producing block because slot has not yet arrived
|
||||
2072000ms th_a witness.cpp:204 block_production_loo ] Not producing block because slot has not yet arrived
|
||||
2073000ms th_a witness.cpp:201 block_production_loo ] Not producing block because it isn't my turn
|
||||
```
|
||||
|
||||
Assuming you've received votes, you will start producing as a witness at the next maintenance interval (once per hour). You can check your votes with.
|
||||
|
|
@ -200,11 +198,7 @@ Check your logfile for entries
|
|||
tail -f /var/log/peerplays.log
|
||||
```
|
||||
|
||||
|
||||
Running specific tests
|
||||
----------------------
|
||||
|
||||
- `tests/chain_tests -t block_tests/name_of_test`
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
112
Vagrantfile
vendored
112
Vagrantfile
vendored
|
|
@ -1,112 +0,0 @@
|
|||
# Configures Ubuntu 14.04 VM to be used with BitShares 2.0 (Graphene)
|
||||
# Downloads and builds all necessary software to run witness node and web GUI
|
||||
# Use with Vagrant (http://docs.vagrantup.com/v2/getting-started/index.html)
|
||||
# or just execute the shell script below.
|
||||
# Vagrant setup supports the following providers: Virtual Box, Digital Ocean, Amazon EC2
|
||||
|
||||
$script = <<SCRIPT
|
||||
# ------ shell script begin ------
|
||||
|
||||
echo_msg() {
|
||||
/bin/echo -e "\e[1;36m*** $1 ***\e[0m"
|
||||
}
|
||||
|
||||
echo_msg "Current user: `id`"
|
||||
echo_msg "Current dir: `pwd`"
|
||||
|
||||
echo_msg "updating system.."
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -yfV dist-upgrade
|
||||
|
||||
echo_msg "installing required packages.."
|
||||
sudo apt-get install -yfV git libreadline-dev uuid-dev g++ libdb++-dev libdb-dev zip
|
||||
sudo apt-get install -yfV libssl-dev openssl build-essential python-dev autotools-dev libicu-dev build-essential
|
||||
sudo apt-get install -yfV libbz2-dev automake doxygen cmake ncurses-dev libtool nodejs nodejs-legacy npm mc
|
||||
sudo apt-get -y autoremove
|
||||
|
||||
[ ! -d "bts" ] && mkdir bts && cd bts
|
||||
[ ! -d "tmp" ] && mkdir tmp
|
||||
[ ! -d "build" ] && mkdir build
|
||||
|
||||
if [ ! -d "tmp/boost_1_57_0" ]; then
|
||||
echo_msg "building boost.."
|
||||
cd tmp/
|
||||
wget -nv 'http://sourceforge.net/projects/boost/files/boost/1.57.0/boost_1_57_0.tar.bz2/download'
|
||||
tar -xf download
|
||||
cd boost_1_57_0/
|
||||
./bootstrap.sh --prefix=/usr/local/ > /dev/null
|
||||
sudo ./b2 install > /dev/null
|
||||
cd ~/bts
|
||||
fi
|
||||
|
||||
if [ ! -d "graphene" ]; then
|
||||
echo_msg "building bitshares graphene toolkit.."
|
||||
git clone https://github.com/cryptonomex/graphene.git
|
||||
cd graphene
|
||||
git submodule update --init --recursive
|
||||
cmake .
|
||||
make
|
||||
cd ~/bts
|
||||
fi
|
||||
|
||||
if [ ! -d "graphene-ui" ]; then
|
||||
echo_msg "installing ui dependencies.."
|
||||
git clone https://github.com/cryptonomex/graphene-ui.git
|
||||
cd graphene-ui/dl
|
||||
npm install --silent
|
||||
cd ../web
|
||||
npm install --silent
|
||||
npm run-script build
|
||||
cd ~/bts
|
||||
fi
|
||||
|
||||
# ------ shell script end ------
|
||||
SCRIPT
|
||||
|
||||
|
||||
|
||||
Dir["*.sh"].each {|s| File.open(s,"r"){|f| $script << f.read()} } # includes additional .sh files (plug-ins)
|
||||
|
||||
Vagrant.configure(2) do |config|
|
||||
|
||||
config.vm.box = 'ubuntu_trusty_x64'
|
||||
config.vm.provision 'shell', inline: $script, privileged: false
|
||||
config.ssh.username = 'vagrant'
|
||||
|
||||
# to use with Digital Ocean please install this plugin https://github.com/smdahlen/vagrant-digitalocean
|
||||
# note: due to bug in vagrant-digitalocean you need to run provision separetly:
|
||||
# vagrant up --provider digital_ocean --no-provision
|
||||
# vagrant provision
|
||||
config.vm.provider :digital_ocean do |provider, override|
|
||||
override.vm.hostname = 'graphene'
|
||||
override.ssh.private_key_path = ENV['VAGRANT_KEY_PATH']
|
||||
override.vm.box = 'digital_ocean'
|
||||
override.vm.box_url = 'https://github.com/smdahlen/vagrant-digitalocean/raw/master/box/digital_ocean.box'
|
||||
provider.setup = true
|
||||
provider.region = 'nyc2'
|
||||
provider.image = 'ubuntu-14-04-x64'
|
||||
provider.size = '4GB' # 2GB may be not enought to compile graphene toolkit
|
||||
provider.token = ENV['DIGITALOCEAN_TOKEN']
|
||||
provider.ssh_key_name = 'vagrant'
|
||||
end
|
||||
|
||||
config.vm.provider :aws do |aws, override|
|
||||
aws.access_key_id = ENV['AWS_ACCESS_KEY']
|
||||
aws.secret_access_key = ENV['AWS_SECRET_KEY']
|
||||
aws.keypair_name = $1 if ENV['VAGRANT_KEY_PATH'] =~ /([\w]+)[\.\w]*$/
|
||||
aws.region = "us-east-1"
|
||||
aws.ami = 'ami-018c9568'
|
||||
aws.instance_type = 'm1.small'
|
||||
aws.security_groups = [ 'bitsharesxt' ]
|
||||
override.vm.hostname = 'bitsharesxt-aws'
|
||||
override.ssh.username = 'ubuntu'
|
||||
override.ssh.private_key_path = ENV['VAGRANT_KEY_PATH']
|
||||
override.vm.box = 'dummy'
|
||||
end
|
||||
|
||||
config.vm.provider 'virtualbox' do |v|
|
||||
v.customize ['modifyvm', :id, '--memory', '4096']
|
||||
v.customize ['modifyvm', :id, '--cpus', 4]
|
||||
end
|
||||
|
||||
end
|
||||
1113
betting_simulator.html
Normal file
1113
betting_simulator.html
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -47,28 +47,15 @@ required-participation = false
|
|||
# track-account =
|
||||
|
||||
# Track market history by grouping orders into buckets of equal size measured in seconds specified as a JSON array of numbers
|
||||
bucket-size = [15,60,300,3600,86400]
|
||||
# bucket-size = [15,60,300,3600,86400]
|
||||
bucket-size = [60,300,900,1800,3600,14400,86400]
|
||||
# for 1 min, 5 mins, 30 mins, 1h, 4 hs and 1 day. i think this should be the default.
|
||||
|
||||
# How far back in time to track history for each bucket size, measured in the number of buckets (default: 1000)
|
||||
history-per-size = 1000
|
||||
|
||||
# declare an appender named "stderr" that writes messages to the console
|
||||
[log.console_appender.stderr]
|
||||
stream=std_error
|
||||
|
||||
# declare an appender named "p2p" that writes messages to p2p.log
|
||||
[log.file_appender.p2p]
|
||||
filename=logs/p2p/p2p.log
|
||||
# filename can be absolute or relative to this config file
|
||||
|
||||
# route any messages logged to the default logger to the "stderr" logger we
|
||||
# declared above, if they are info level are higher
|
||||
[logger.default]
|
||||
level=info
|
||||
appenders=stderr
|
||||
|
||||
# route messages sent to the "p2p" logger to the p2p appender declared above
|
||||
[logger.p2p]
|
||||
level=debug
|
||||
appenders=p2p
|
||||
# Max amount of operations to store in the database, per account (drastically reduces RAM requirements)
|
||||
max-ops-per-account = 1000
|
||||
|
||||
# Remove old operation history # objects from RAM
|
||||
partial-operations = true
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
[ -e /data_dir/config.ini ] || cp /default_config.ini /data_dir/config.ini
|
||||
|
||||
[ -e /data_dir/pre_exec ] && bash /data_dir/pre_exec
|
||||
if [ -e /data_dir/extra_args ]; then
|
||||
/bitshares-2/programs/witness_node/witness_node --data-dir /data_dir `cat /data_dir/extra_args`
|
||||
else
|
||||
/bitshares-2/programs/witness_node/witness_node --data-dir /data_dir
|
||||
fi
|
||||
[ -e /data_dir/post_exec ] && bash /data_dir/post_exec
|
||||
82
docker/peerplaysentry.sh
Normal file
82
docker/peerplaysentry.sh
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#!/bin/bash
|
||||
PEERPLAYSD="/usr/local/bin/witness_node"
|
||||
|
||||
# For blockchain download
|
||||
VERSION=`cat /etc/peerplays/version`
|
||||
|
||||
## Supported Environmental Variables
|
||||
#
|
||||
# * $PEERPLAYSD_SEED_NODES
|
||||
# * $PEERPLAYSD_RPC_ENDPOINT
|
||||
# * $PEERPLAYSD_PLUGINS
|
||||
# * $PEERPLAYSD_REPLAY
|
||||
# * $PEERPLAYSD_RESYNC
|
||||
# * $PEERPLAYSD_P2P_ENDPOINT
|
||||
# * $PEERPLAYSD_WITNESS_ID
|
||||
# * $PEERPLAYSD_PRIVATE_KEY
|
||||
# * $PEERPLAYSD_TRACK_ACCOUNTS
|
||||
# * $PEERPLAYSD_PARTIAL_OPERATIONS
|
||||
# * $PEERPLAYSD_MAX_OPS_PER_ACCOUNT
|
||||
# * $PEERPLAYSD_TRUSTED_NODE
|
||||
#
|
||||
|
||||
ARGS=""
|
||||
# Translate environmental variables
|
||||
if [[ ! -z "$PEERPLAYSD_SEED_NODES" ]]; then
|
||||
for NODE in $PEERPLAYSD_SEED_NODES ; do
|
||||
ARGS+=" --seed-node=$NODE"
|
||||
done
|
||||
fi
|
||||
if [[ ! -z "$PEERPLAYSD_RPC_ENDPOINT" ]]; then
|
||||
ARGS+=" --rpc-endpoint=${PEERPLAYSD_RPC_ENDPOINT}"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_REPLAY" ]]; then
|
||||
ARGS+=" --replay-blockchain"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_RESYNC" ]]; then
|
||||
ARGS+=" --resync-blockchain"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_P2P_ENDPOINT" ]]; then
|
||||
ARGS+=" --p2p-endpoint=${PEERPLAYSD_P2P_ENDPOINT}"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_WITNESS_ID" ]]; then
|
||||
ARGS+=" --witness-id=$PEERPLAYSD_WITNESS_ID"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_PRIVATE_KEY" ]]; then
|
||||
ARGS+=" --private-key=$PEERPLAYSD_PRIVATE_KEY"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_TRACK_ACCOUNTS" ]]; then
|
||||
for ACCOUNT in $PEERPLAYSD_TRACK_ACCOUNTS ; do
|
||||
ARGS+=" --track-account=$ACCOUNT"
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_PARTIAL_OPERATIONS" ]]; then
|
||||
ARGS+=" --partial-operations=${PEERPLAYSD_PARTIAL_OPERATIONS}"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_MAX_OPS_PER_ACCOUNT" ]]; then
|
||||
ARGS+=" --max-ops-per-account=${PEERPLAYSD_MAX_OPS_PER_ACCOUNT}"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_TRUSTED_NODE" ]]; then
|
||||
ARGS+=" --trusted-node=${PEERPLAYSD_TRUSTED_NODE}"
|
||||
fi
|
||||
|
||||
## Link the peerplays config file into home
|
||||
## This link has been created in Dockerfile, already
|
||||
ln -f -s /etc/peerplays/config.ini /var/lib/peerplays
|
||||
|
||||
# Plugins need to be provided in a space-separated list, which
|
||||
# makes it necessary to write it like this
|
||||
if [[ ! -z "$PEERPLAYSD_PLUGINS" ]]; then
|
||||
$PEERPLAYSD --data-dir ${HOME} ${ARGS} ${PEERPLAYSD_ARGS} --plugins "${PEERPLAYSD_PLUGINS}"
|
||||
else
|
||||
$PEERPLAYSD --data-dir ${HOME} ${ARGS} ${PEERPLAYSD_ARGS}
|
||||
fi
|
||||
222006
genesis.json
222006
genesis.json
File diff suppressed because it is too large
Load diff
216158
genesis/alice-genesis.json
Normal file
216158
genesis/alice-genesis.json
Normal file
File diff suppressed because it is too large
Load diff
181
genesis/genesis.py
Normal file
181
genesis/genesis.py
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
import json
|
||||
import yaml
|
||||
from peerplays import PeerPlays
|
||||
from peerplaysbase.account import BrainKey, Address
|
||||
|
||||
|
||||
def pubkey():
|
||||
k = next(key)
|
||||
pub = format(k.get_public_key(), "TEST")
|
||||
pri = str(k.get_private_key())
|
||||
print("[\"{}\", \"{}\"]".format(pub, pri))
|
||||
return pub
|
||||
|
||||
|
||||
def address():
|
||||
brain = next(key)
|
||||
pri = brain.get_private_key()
|
||||
pub = brain.get_public_key()
|
||||
print(
|
||||
"[\"{}\", \"{}\"]".format(
|
||||
format(pub, "TEST"), str(pri))
|
||||
)
|
||||
return format(Address.from_pubkey(pub), "TEST")
|
||||
|
||||
|
||||
# We define a new BrainKey which is used to generate some new keys
|
||||
# for genesis block
|
||||
key = BrainKey("secret!")
|
||||
|
||||
# Let's load Alice(main net) genesis block as a basis
|
||||
genesis = json.load(open("alice-genesis.json", "r"))
|
||||
|
||||
# Initial accounts are init* + faucet
|
||||
genesis_accounts = list()
|
||||
for i in range(11):
|
||||
genesis_accounts.append(dict(
|
||||
active_key=pubkey(),
|
||||
owner_key=pubkey(),
|
||||
is_lifetime_member=True,
|
||||
name="init{}".format(i)))
|
||||
|
||||
genesis_accounts.append(dict(
|
||||
active_key=pubkey(),
|
||||
owner_key=pubkey(),
|
||||
is_lifetime_member=True,
|
||||
name="faucet"))
|
||||
genesis_accounts.append(dict(
|
||||
active_key=pubkey(),
|
||||
owner_key=pubkey(),
|
||||
is_lifetime_member=True,
|
||||
name="pbsa"))
|
||||
genesis["initial_accounts"] = genesis_accounts
|
||||
|
||||
# BTF Asset
|
||||
genesis["initial_assets"] = [
|
||||
{
|
||||
"accumulated_fees": 0,
|
||||
"collateral_records": [],
|
||||
"description": "Fun token. Supposed to be worthless!",
|
||||
"is_bitasset": False,
|
||||
"issuer_name": "pbsa",
|
||||
"max_supply": 21000000 * 10 ** 8, # 21 Mio with precision 8
|
||||
"precision": 8, # precision 8
|
||||
"symbol": "BTF"
|
||||
}
|
||||
]
|
||||
|
||||
# Inicial balances (claimable) - replace PPY prefix with TEST prefix
|
||||
btf_balances = []
|
||||
for i, _ in enumerate(genesis["initial_balances"]):
|
||||
genesis["initial_balances"][i]["asset_symbol"] = "TEST"
|
||||
genesis["initial_balances"][i]["owner"] = "TEST" + genesis["initial_balances"][i]["owner"][3:]
|
||||
|
||||
# Let's distribute extra stake for sake of governance
|
||||
sum_distributed = sum([int(x["amount"]) for x in genesis["initial_balances"]])
|
||||
# 34% of supply are left for witnesses etc
|
||||
initial_pbsa = str(int(int(genesis["max_core_supply"]) * 0.66 - sum_distributed))
|
||||
genesis["initial_balances"].append(dict(
|
||||
amount=initial_pbsa,
|
||||
asset_symbol="TEST",
|
||||
owner=address()
|
||||
))
|
||||
|
||||
# Update sining keys of initial witnesses
|
||||
for i, _ in enumerate(genesis["initial_witness_candidates"]):
|
||||
genesis["initial_witness_candidates"][i]["block_signing_key"] = pubkey()
|
||||
|
||||
# Other stuff
|
||||
genesis["initial_vesting_balances"] = []
|
||||
genesis["initial_bts_accounts"] = []
|
||||
genesis["max_core_supply"] = '400000000000000'
|
||||
|
||||
# Read params from Alice network
|
||||
peerplays = PeerPlays("wss://node.peerplays.download")
|
||||
params = peerplays.rpc.get_object("2.0.0")["parameters"]
|
||||
|
||||
# Missing Fees from Alice
|
||||
params["current_fees"]["parameters"].extend([
|
||||
[50, {}],
|
||||
[51, {"fee": 100000}],
|
||||
[52, {"fee": 100000}],
|
||||
[53, {"fee": 100000}],
|
||||
[54, {"fee": 100000}],
|
||||
[55, {"fee": 100000}],
|
||||
[56, {"fee": 100000}],
|
||||
[57, {"fee": 100000}],
|
||||
[58, {"fee": 100000}],
|
||||
[59, {"fee": 100000}],
|
||||
[60, {"fee": 100000}],
|
||||
[61, {"fee": 100000}],
|
||||
[62, {"fee": 100000}],
|
||||
[63, {"fee": 100000}],
|
||||
[64, {}],
|
||||
[65, {}],
|
||||
[66, {"fee": 100000}],
|
||||
[67, {}],
|
||||
[68, {"fee": 100000}],
|
||||
[69, {}],
|
||||
[70, {"fee": 100000}],
|
||||
[71, {"fee": 100000}],
|
||||
[72, {"fee": 100000}],
|
||||
[73, {"fee": 100000}],
|
||||
[74, {"fee": 100000}],
|
||||
[75, {}],
|
||||
[76, {}],
|
||||
|
||||
])
|
||||
genesis["initial_parameters"] = params
|
||||
# Updates to parameters and add new parameters
|
||||
genesis["initial_parameters"].update({
|
||||
"block_interval": 3,
|
||||
"maintenance_interval": 60 * 60 * 24,
|
||||
"maintenance_skip_slots": 3,
|
||||
"committee_proposal_review_period": 1209600,
|
||||
"maximum_transaction_size": 2048,
|
||||
"maximum_block_size": 1228800000,
|
||||
"maximum_time_until_expiration": 86400,
|
||||
"maximum_proposal_lifetime": 2419200,
|
||||
"maximum_asset_whitelist_authorities": 10,
|
||||
"maximum_asset_feed_publishers": 10,
|
||||
"maximum_witness_count": 1001,
|
||||
"maximum_committee_count": 1001,
|
||||
"maximum_authority_membership": 10,
|
||||
"reserve_percent_of_fee": 2000,
|
||||
"network_percent_of_fee": 2000,
|
||||
"lifetime_referrer_percent_of_fee": 3000,
|
||||
"cashback_vesting_period_seconds": 31536000,
|
||||
"cashback_vesting_threshold": 10000000,
|
||||
"count_non_member_votes": True,
|
||||
"allow_non_member_whitelists": False,
|
||||
"witness_pay_per_block": 1000000,
|
||||
"worker_budget_per_day": "50000000000",
|
||||
"max_predicate_opcode": 1,
|
||||
"fee_liquidation_threshold": 10000000,
|
||||
"accounts_per_fee_scale": 1000,
|
||||
"account_fee_scale_bitshifts": 4,
|
||||
"max_authority_depth": 2,
|
||||
"witness_schedule_algorithm": 1,
|
||||
"min_round_delay": 0,
|
||||
"max_round_delay": 600,
|
||||
"min_time_per_commit_move": 0,
|
||||
"max_time_per_commit_move": 600,
|
||||
"min_time_per_reveal_move": 0,
|
||||
"max_time_per_reveal_move": 600,
|
||||
"rake_fee_percentage": 300,
|
||||
"maximum_registration_deadline": 2592000,
|
||||
"maximum_players_in_tournament": 256,
|
||||
"maximum_tournament_whitelist_length": 1000,
|
||||
"maximum_tournament_start_time_in_future": 2419200,
|
||||
"maximum_tournament_start_delay": 604800,
|
||||
"maximum_tournament_number_of_wins": 100,
|
||||
"extensions": {}
|
||||
})
|
||||
|
||||
# Store new genesis file as YAML file for better readbility
|
||||
yaml.dump(genesis, open("genesis.yaml", "w"))
|
||||
|
||||
# .. and as json
|
||||
json.dump(genesis, open("genesis.json", "w"))
|
||||
|
||||
print("Last sequence number: {}".format(key.sequence))
|
||||
|
|
@ -1 +1 @@
|
|||
2.0.160208
|
||||
0.0.1
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ add_library( graphene_app
|
|||
)
|
||||
|
||||
# need to link graphene_debug_witness because plugins aren't sufficiently isolated #246
|
||||
target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness )
|
||||
#target_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_affiliate_stats 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" )
|
||||
|
|
@ -28,3 +29,4 @@ INSTALL( TARGETS
|
|||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/app" )
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <fc/thread/future.hpp>
|
||||
|
||||
namespace graphene { namespace app {
|
||||
|
||||
|
|
@ -80,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 ) );
|
||||
|
|
@ -96,15 +101,45 @@ 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));
|
||||
}
|
||||
else if( api_name == "affiliate_stats_api" )
|
||||
{
|
||||
// can only enable this API if the plugin was loaded
|
||||
if( _app.get_plugin( "affiliate_stats" ) )
|
||||
_affiliate_stats_api = std::make_shared<graphene::affiliate_stats::affiliate_stats_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); });
|
||||
|
|
@ -136,10 +171,23 @@ namespace graphene { namespace app {
|
|||
void network_broadcast_api::broadcast_transaction(const signed_transaction& trx)
|
||||
{
|
||||
trx.validate();
|
||||
_app.chain_database()->check_tansaction_for_duplicated_operations(trx);
|
||||
_app.chain_database()->push_transaction(trx);
|
||||
_app.p2p_node()->broadcast_transaction(trx);
|
||||
}
|
||||
|
||||
fc::variant network_broadcast_api::broadcast_transaction_synchronous(const signed_transaction& trx)
|
||||
{
|
||||
_app.chain_database()->check_tansaction_for_duplicated_operations(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);
|
||||
|
|
@ -156,6 +204,45 @@ namespace graphene { namespace app {
|
|||
|
||||
network_node_api::network_node_api( application& a ) : _app( a )
|
||||
{
|
||||
_pending_trx_connection = _app.chain_database()->on_pending_transaction.connect([this]( const signed_transaction& transaction ){
|
||||
|
||||
auto transaction_it = _pending_transactions.find(transaction.id());
|
||||
if (_pending_transactions.end() == transaction_it)
|
||||
{
|
||||
_pending_transactions[transaction.id()] = transaction;
|
||||
}
|
||||
|
||||
if (_on_pending_transaction)
|
||||
{
|
||||
_on_pending_transaction(fc::variant(transaction));
|
||||
}
|
||||
});
|
||||
|
||||
_applied_block_connection = _app.chain_database()->applied_block.connect([this]( const signed_block& block ){
|
||||
for (const auto& transaction: block.transactions)
|
||||
{
|
||||
auto transaction_it = _pending_transactions.find(transaction.id());
|
||||
if (_pending_transactions.end() != transaction_it)
|
||||
{
|
||||
_pending_transactions.erase(transaction_it);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove expired transactions from pending_transactions
|
||||
*/
|
||||
for (const auto& transaction: _pending_transactions)
|
||||
{
|
||||
if (transaction.second.expiration < block.timestamp)
|
||||
{
|
||||
auto transaction_it = _pending_transactions.find(transaction.second.id());
|
||||
if (_pending_transactions.end() != transaction_it)
|
||||
{
|
||||
_pending_transactions.erase(transaction_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fc::variant_object network_node_api::get_info() const
|
||||
|
|
@ -190,12 +277,33 @@ namespace graphene { namespace app {
|
|||
return _app.p2p_node()->set_advanced_node_parameters(params);
|
||||
}
|
||||
|
||||
map<transaction_id_type, signed_transaction> network_node_api::list_pending_transactions() const
|
||||
{
|
||||
return _pending_transactions;
|
||||
}
|
||||
|
||||
void network_node_api::subscribe_to_pending_transactions( std::function<void(const variant&)> callback )
|
||||
{
|
||||
_on_pending_transaction = callback;
|
||||
}
|
||||
|
||||
void network_node_api::unsubscribe_from_pending_transactions()
|
||||
{
|
||||
_on_pending_transaction = std::function<void(const variant&)>();
|
||||
}
|
||||
|
||||
fc::api<network_broadcast_api> login_api::network_broadcast()const
|
||||
{
|
||||
FC_ASSERT(_network_broadcast_api);
|
||||
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);
|
||||
|
|
@ -220,12 +328,30 @@ 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;
|
||||
}
|
||||
|
||||
fc::api<graphene::affiliate_stats::affiliate_stats_api> login_api::affiliate_stats() const
|
||||
{
|
||||
FC_ASSERT(_affiliate_stats_api);
|
||||
return *_affiliate_stats_api;
|
||||
}
|
||||
|
||||
#if 0
|
||||
vector<account_id_type> get_relevant_accounts( const object* obj )
|
||||
{
|
||||
|
|
@ -308,6 +434,18 @@ namespace graphene { namespace app {
|
|||
} case balance_object_type:{
|
||||
/** 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);
|
||||
|
|
@ -374,6 +512,10 @@ 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;
|
||||
|
|
@ -432,6 +574,37 @@ namespace graphene { namespace app {
|
|||
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,
|
||||
|
|
@ -441,23 +614,36 @@ namespace graphene { namespace app {
|
|||
const auto& db = *_app.chain_database();
|
||||
FC_ASSERT(limit <= 100);
|
||||
vector<operation_history_object> result;
|
||||
const auto& stats = account(db).statistics(db);
|
||||
if( start == 0 )
|
||||
start = account(db).statistics(db).total_ops;
|
||||
else start = min( account(db).statistics(db).total_ops, start );
|
||||
start = stats.total_ops;
|
||||
else
|
||||
start = min( stats.total_ops, start );
|
||||
|
||||
|
||||
if( start >= stop && start > stats.removed_ops && limit > 0 )
|
||||
{
|
||||
const auto& hist_idx = db.get_index_type<account_transaction_history_index>();
|
||||
const auto& by_seq_idx = hist_idx.indices().get<by_seq>();
|
||||
|
||||
auto itr = by_seq_idx.upper_bound( boost::make_tuple( account, start ) );
|
||||
auto itr_stop = by_seq_idx.lower_bound( boost::make_tuple( account, stop ) );
|
||||
--itr;
|
||||
|
||||
while ( itr != itr_stop && result.size() < limit )
|
||||
do
|
||||
{
|
||||
result.push_back( itr->operation_id(db) );
|
||||
--itr;
|
||||
result.push_back( itr->operation_id(db) );
|
||||
}
|
||||
while ( itr != itr_stop && result.size() < limit );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -563,4 +749,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>
|
||||
|
||||
|
|
@ -115,7 +114,7 @@ namespace detail {
|
|||
|
||||
void reset_p2p_node(const fc::path& data_dir)
|
||||
{ try {
|
||||
_p2p_network = std::make_shared<net::node>("BitShares Reference Implementation");
|
||||
_p2p_network = std::make_shared<net::node>("PeerPlays Reference Implementation");
|
||||
|
||||
_p2p_network->load_configuration(data_dir / "p2p");
|
||||
_p2p_network->set_node_delegate(this);
|
||||
|
|
@ -163,24 +162,12 @@ namespace detail {
|
|||
{
|
||||
// t.me/peerplays #seednodes
|
||||
vector<string> seeds = {
|
||||
// "seed.ppy.blckchnd.com:6112", // blckchnd
|
||||
// "ppy.esteem.ws:7777", // good-karma
|
||||
// "peerplays.bitcoiner.me:9777", // bitcoiner
|
||||
// "peerplays.roelandp.nl:9777", // roelandp
|
||||
// "78.46.95.153:7777", // theprophet0
|
||||
// "ppyseed.bacchist.me:42420", // bacchist-witness
|
||||
// "5.9.18.213:18828", // pfunk
|
||||
// "31.171.244.121:7777", // taconator
|
||||
// "seed.peerplaysdb.com:9777", // jesta
|
||||
// "ppy-seed.xeldal.com:19777", // xeldal
|
||||
// "peerplays-seed.altcap.io:61388", // winner.winner.chicken.dinner
|
||||
// "seed.peerplaysnodes.com:9777", // wackou
|
||||
// "peerplays-seed.privex.io:7777", // someguy123/privex
|
||||
// "51.15.78.16:9777", // agoric.systems
|
||||
// "212.71.253.163:9777", // xtar
|
||||
// "51.15.35.96:9777", // lafona
|
||||
// "anyx.ca:9777" // anyx
|
||||
"ppy-beatrice-seed.blckchnd.com:6666",
|
||||
"159.69.223.206:7777",
|
||||
"51.38.237.243:9666",
|
||||
"pbsa-beatrice.blockchainprojectsbv.com:9195"
|
||||
};
|
||||
|
||||
for( const string& endpoint_string : seeds )
|
||||
{
|
||||
try {
|
||||
|
|
@ -237,23 +224,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;
|
||||
|
||||
bool enable_deflate_compression = _options->count("enable-permessage-deflate") != 0;
|
||||
_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 = std::make_shared<fc::http::websocket_server>(enable_deflate_compression);
|
||||
|
||||
_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();
|
||||
|
|
@ -271,17 +284,9 @@ namespace detail {
|
|||
}
|
||||
|
||||
string password = _options->count("server-pem-password") ? _options->at("server-pem-password").as<string>() : "";
|
||||
bool enable_deflate_compression = _options->count("enable-permessage-deflate") != 0;
|
||||
_websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>( _options->at("server-pem").as<string>(), password, enable_deflate_compression );
|
||||
_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();
|
||||
|
|
@ -322,7 +327,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";
|
||||
|
|
@ -373,34 +378,41 @@ namespace detail {
|
|||
}
|
||||
_chain_db->add_checkpoints( loaded_checkpoints );
|
||||
|
||||
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() )
|
||||
{
|
||||
if( _options->count("replay-blockchain") )
|
||||
{
|
||||
ilog("Replaying blockchain on user request.");
|
||||
_chain_db->reindex(_data_dir/"blockchain", initial_state());
|
||||
} else if( clean ) {
|
||||
|
||||
auto is_new = [&]() -> bool
|
||||
replay = true;
|
||||
replay_reason = "replay-blockchain argument specified";
|
||||
}
|
||||
else if( !clean )
|
||||
{
|
||||
// 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 = "unclean shutdown detected";
|
||||
}
|
||||
else if( !fc::exists( _data_dir / "db_version" ) )
|
||||
{
|
||||
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);
|
||||
};
|
||||
replay = true;
|
||||
replay_reason = "db_version file not found";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string version_string;
|
||||
fc::read_file_contents( _data_dir / "db_version", version_string );
|
||||
|
||||
bool need_reindex = (!is_new() && is_outdated());
|
||||
std::string reindex_reason = "version upgrade";
|
||||
if( version_string != GRAPHENE_CURRENT_DB_VERSION )
|
||||
{
|
||||
replay = true;
|
||||
replay_reason = "db_version file content mismatch";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !need_reindex )
|
||||
if( !replay )
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -408,45 +420,26 @@ namespace detail {
|
|||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
ilog( "caught exception ${e} in open()", ("e", e.to_detail_string()) );
|
||||
need_reindex = true;
|
||||
reindex_reason = "exception in open()";
|
||||
ilog( "Caught exception ${e} in open()", ("e", e.to_detail_string()) );
|
||||
|
||||
replay = true;
|
||||
replay_reason = "exception in open()";
|
||||
}
|
||||
}
|
||||
|
||||
if( need_reindex )
|
||||
if( replay )
|
||||
{
|
||||
ilog("Replaying blockchain due to ${reason}", ("reason", reindex_reason) );
|
||||
ilog( "Replaying blockchain due to: ${reason}", ("reason", replay_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 );
|
||||
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();
|
||||
}
|
||||
}
|
||||
} 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( _options->count("force-validate") )
|
||||
{
|
||||
|
|
@ -454,8 +447,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>();
|
||||
|
|
@ -471,6 +462,8 @@ 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" );
|
||||
wild_access.allowed_apis.push_back( "affiliate_stats_api" );
|
||||
_apiaccess.permission_map["*"] = wild_access;
|
||||
}
|
||||
|
||||
|
|
@ -479,6 +472,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;
|
||||
|
|
@ -523,8 +517,8 @@ namespace detail {
|
|||
virtual bool handle_block(const graphene::net::block_message& blk_msg, bool sync_mode,
|
||||
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;
|
||||
FC_ASSERT( (latency.count()/1000) > -5000, "Rejecting block with timestamp in the future" );
|
||||
if (!sync_mode || blk_msg.block.block_num() % 10000 == 0)
|
||||
{
|
||||
const auto& witness = blk_msg.block.witness(*_chain_db);
|
||||
|
|
@ -537,6 +531,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,
|
||||
|
|
@ -898,12 +893,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();
|
||||
|
|
@ -969,8 +958,6 @@ void application::set_program_options(boost::program_options::options_descriptio
|
|||
("checkpoint,c", bpo::value<vector<string>>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.")
|
||||
("rpc-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8090"), "Endpoint for websocket RPC to listen on")
|
||||
("rpc-tls-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8089"), "Endpoint for TLS websocket RPC to listen on")
|
||||
("enable-permessage-deflate", "Enable support for per-message deflate compression in the websocket servers "
|
||||
"(--rpc-endpoint and --rpc-tls-endpoint), disabled by default")
|
||||
("server-pem,p", bpo::value<string>()->implicit_value("server.pem"), "The TLS certificate file for this server")
|
||||
("server-pem-password,P", bpo::value<string>()->implicit_value(""), "Password for this certificate")
|
||||
("genesis-json", bpo::value<boost::filesystem::path>(), "File to read Genesis State from")
|
||||
|
|
|
|||
|
|
@ -43,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;
|
||||
|
|
@ -58,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;
|
||||
|
||||
|
|
@ -74,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;
|
||||
|
|
@ -99,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;
|
||||
|
||||
// Lottery Assets
|
||||
vector<asset_object> get_lotteries( asset_id_type stop = asset_id_type(),
|
||||
unsigned limit = 100,
|
||||
|
|
@ -179,22 +193,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();
|
||||
|
||||
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;
|
||||
|
|
@ -217,11 +261,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(); });
|
||||
|
||||
|
|
@ -257,10 +304,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());
|
||||
|
|
@ -281,25 +324,25 @@ 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 )
|
||||
{
|
||||
_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/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 )
|
||||
{
|
||||
|
|
@ -350,6 +393,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
|
||||
{
|
||||
|
|
@ -439,6 +496,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 //
|
||||
|
|
@ -509,6 +577,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 //
|
||||
|
|
@ -561,7 +658,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 );
|
||||
}
|
||||
|
||||
|
|
@ -623,6 +721,25 @@ 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));
|
||||
|
|
@ -1002,6 +1119,86 @@ asset database_api_impl::get_sweeps_vesting_balance_available_for_claim( account
|
|||
return account_balance->available_for_claim();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// 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 //
|
||||
|
|
@ -1127,63 +1324,61 @@ market_ticker database_api::get_ticker( const string& base, const string& quote
|
|||
|
||||
market_ticker database_api_impl::get_ticker( const string& base, const string& quote )const
|
||||
{
|
||||
auto assets = lookup_asset_symbols( {base, 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.base_volume = 0;
|
||||
result.quote_volume = 0;
|
||||
result.percent_change = 0;
|
||||
result.latest = 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 ); };
|
||||
result.percent_change = 0;
|
||||
result.base_volume = 0;
|
||||
result.quote_volume = 0;
|
||||
|
||||
try {
|
||||
if( base_id > quote_id ) std::swap(base_id, quote_id);
|
||||
|
||||
uint32_t day = 86400;
|
||||
auto now = fc::time_point_sec( fc::time_point::now() );
|
||||
auto orders = get_order_book( base, quote, 1 );
|
||||
auto trades = get_trade_history( base, quote, now, fc::time_point_sec( now.sec_since_epoch() - day ), 100 );
|
||||
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;
|
||||
|
||||
vector<market_trade> trades = get_trade_history( base, quote, now, yesterday, batch_size );
|
||||
if( !trades.empty() )
|
||||
{
|
||||
result.latest = trades[0].price;
|
||||
|
||||
for ( market_trade t: trades )
|
||||
while( !trades.empty() )
|
||||
{
|
||||
for( const market_trade& t: trades )
|
||||
{
|
||||
result.base_volume += t.value;
|
||||
result.quote_volume += t.amount;
|
||||
}
|
||||
|
||||
while (trades.size() == 100)
|
||||
{
|
||||
trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - day ), 100 );
|
||||
|
||||
for ( 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.back().date, fc::time_point_sec(), 1 );
|
||||
result.percent_change = trades.size() > 0 ? ( ( result.latest / trades.back().price ) - 1 ) * 100 : 0;
|
||||
|
||||
//if (assets[0]->id == base_id)
|
||||
const auto last_trade_yesterday = get_trade_history( base, quote, yesterday, fc::time_point_sec(), 1 );
|
||||
if( !last_trade_yesterday.empty() )
|
||||
{
|
||||
result.lowest_ask = orders.asks[0].price;
|
||||
result.highest_bid = orders.bids[0].price;
|
||||
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) )
|
||||
}
|
||||
|
||||
market_volume database_api::get_24_volume( const string& base, const string& quote )const
|
||||
|
|
@ -1193,46 +1388,15 @@ market_volume database_api::get_24_volume( const string& base, const string& quo
|
|||
|
||||
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) );
|
||||
|
||||
auto base_id = assets[0]->id;
|
||||
auto quote_id = assets[1]->id;
|
||||
const auto ticker = get_ticker( base, quote );
|
||||
|
||||
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.value;
|
||||
result.quote_volume += t.amount;
|
||||
}
|
||||
|
||||
while (trades.size() == 100)
|
||||
{
|
||||
trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 );
|
||||
|
||||
for ( market_trade t: trades )
|
||||
{
|
||||
result.base_volume += t.value;
|
||||
result.quote_volume += t.amount;
|
||||
}
|
||||
}
|
||||
result.base = ticker.base;
|
||||
result.quote = ticker.quote;
|
||||
result.base_volume = ticker.base_volume;
|
||||
result.quote_volume = ticker.quote_volume;
|
||||
|
||||
return result;
|
||||
} FC_CAPTURE_AND_RETHROW( (base)(quote) )
|
||||
}
|
||||
|
||||
order_book database_api::get_order_book( const string& base, const string& quote, unsigned limit )const
|
||||
|
|
@ -1282,7 +1446,7 @@ order_book database_api_impl::get_order_book( const string& base, const string&
|
|||
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( ( uint64_t( o.for_sale.value ) * o.sell_price.quote.amount.value ) / o.sell_price.base.amount.value ), assets[0]->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 );
|
||||
}
|
||||
}
|
||||
|
|
@ -1972,109 +2136,113 @@ vector<tournament_id_type> database_api_impl::get_registered_tournaments(account
|
|||
|
||||
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](){
|
||||
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());
|
||||
|
||||
for( auto obj : objs )
|
||||
updates.emplace_back( obj->id );
|
||||
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
|
||||
|
|
|
|||
|
|
@ -208,6 +208,50 @@ 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 sport_delete_operation& op ) {}
|
||||
void operator()( const event_group_create_operation& op ) {}
|
||||
void operator()( const event_group_update_operation& op ) {}
|
||||
void operator()( const event_group_delete_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 );
|
||||
|
|
@ -233,6 +277,11 @@ struct get_impacted_account_visitor
|
|||
{
|
||||
_impacted.insert( op.payout_account_id );
|
||||
}
|
||||
void operator()( const affiliate_payout_operation& op )
|
||||
{
|
||||
_impacted.insert( op.affiliate );
|
||||
}
|
||||
void operator()( const affiliate_referral_payout_operation& op ) { }
|
||||
void operator()( const ticket_purchase_operation& op )
|
||||
{
|
||||
_impacted.insert( op.buyer );
|
||||
|
|
|
|||
|
|
@ -29,8 +29,11 @@
|
|||
#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/affiliate_stats/affiliate_stats_api.hpp>
|
||||
#include <graphene/bookie/bookie_api.hpp>
|
||||
|
||||
#include <graphene/net/node.hpp>
|
||||
|
||||
|
|
@ -49,6 +52,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;
|
||||
|
||||
|
|
@ -71,6 +75,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
|
||||
*
|
||||
|
|
@ -93,6 +109,22 @@ namespace graphene { namespace app {
|
|||
operation_history_id_type stop = operation_history_id_type(),
|
||||
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
|
||||
|
|
@ -113,11 +145,28 @@ namespace graphene { namespace app {
|
|||
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.
|
||||
*/
|
||||
|
|
@ -151,6 +200,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 );
|
||||
|
||||
/**
|
||||
|
|
@ -210,8 +265,28 @@ namespace graphene { namespace app {
|
|||
*/
|
||||
std::vector<net::potential_peer_record> get_potential_peers() const;
|
||||
|
||||
/**
|
||||
* @brief Return list of pending transactions.
|
||||
*/
|
||||
map<transaction_id_type, signed_transaction> list_pending_transactions() const;
|
||||
|
||||
/**
|
||||
* @brief Subscribes caller for notifications about pending transactions.
|
||||
* @param callback a functional object which will be called when new transaction is created.
|
||||
*/
|
||||
void subscribe_to_pending_transactions(std::function<void(const variant&)> callback);
|
||||
|
||||
/**
|
||||
* @brief Unsubscribes caller from notifications about pending transactions.
|
||||
*/
|
||||
void unsubscribe_from_pending_transactions();
|
||||
|
||||
private:
|
||||
application& _app;
|
||||
map<transaction_id_type, signed_transaction> _pending_transactions;
|
||||
boost::signals2::scoped_connection _pending_trx_connection;
|
||||
boost::signals2::scoped_connection _applied_block_connection;
|
||||
std::function<void(const variant&)> _on_pending_transaction;
|
||||
};
|
||||
|
||||
class crypto_api
|
||||
|
|
@ -252,6 +327,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
|
||||
*
|
||||
|
|
@ -273,6 +365,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
|
||||
|
|
@ -283,20 +377,30 @@ 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;
|
||||
/// @brief Retrieve the affiliate_stats API (if available)
|
||||
fc::api<graphene::affiliate_stats::affiliate_stats_api> affiliate_stats()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;
|
||||
optional< fc::api<graphene::affiliate_stats::affiliate_stats_api> > _affiliate_stats_api;
|
||||
};
|
||||
|
||||
}} // graphene::app
|
||||
|
|
@ -310,16 +414,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,
|
||||
|
|
@ -329,6 +442,9 @@ FC_API(graphene::app::network_node_api,
|
|||
(get_potential_peers)
|
||||
(get_advanced_node_parameters)
|
||||
(set_advanced_node_parameters)
|
||||
(list_pending_transactions)
|
||||
(subscribe_to_pending_transactions)
|
||||
(unsubscribe_from_pending_transactions)
|
||||
)
|
||||
FC_API(graphene::app::crypto_api,
|
||||
(blind_sign)
|
||||
|
|
@ -341,12 +457,21 @@ 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)
|
||||
(affiliate_stats)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,12 @@
|
|||
#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>
|
||||
|
|
@ -159,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
|
||||
|
|
@ -213,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 //
|
||||
//////////////
|
||||
|
|
@ -342,6 +365,50 @@ class database_api
|
|||
asset get_lottery_balance( asset_id_type lottery_id ) 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 //
|
||||
/////////////////////
|
||||
|
|
@ -624,6 +691,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)
|
||||
|
|
@ -637,6 +705,7 @@ FC_API(graphene::app::database_api,
|
|||
|
||||
// Keys
|
||||
(get_key_references)
|
||||
(is_public_key_registered)
|
||||
|
||||
// Accounts
|
||||
(get_accounts)
|
||||
|
|
@ -659,6 +728,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)
|
||||
|
||||
// Sweeps
|
||||
(get_lotteries)
|
||||
(get_account_lotteries)
|
||||
|
|
|
|||
|
|
@ -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,11 @@ 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;
|
||||
};
|
||||
|
||||
|
|
@ -61,6 +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
|
||||
|
|
@ -96,7 +98,23 @@ add_library( graphene_chain
|
|||
|
||||
is_authorized_asset.cpp
|
||||
|
||||
protocol/sport.cpp
|
||||
sport_evaluator.cpp
|
||||
protocol/event_group.cpp
|
||||
event_group_evaluator.cpp
|
||||
event_group_object.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
|
||||
|
||||
affiliate_payout.cpp
|
||||
|
||||
${HEADERS}
|
||||
${PROTOCOL_HEADERS}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp"
|
||||
)
|
||||
|
||||
|
|
@ -116,3 +134,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" )
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ void verify_account_votes( const database& db, const account_options& options )
|
|||
"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) );
|
||||
FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." );
|
||||
|
||||
uint32_t max_vote_id = gpo.next_available_vote_id;
|
||||
bool has_worker_votes = false;
|
||||
|
|
@ -107,8 +108,9 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
|
|||
FC_ASSERT( !op.extensions.value.active_special_authority.valid() );
|
||||
FC_ASSERT( !op.extensions.value.buyback_options.valid() );
|
||||
}
|
||||
if( d.head_block_time() < HARDFORK_999_TIME )
|
||||
FC_ASSERT( !op.extensions.value.affiliate_distributions.valid(), "Affiliate reward distributions not allowed yet" );
|
||||
|
||||
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." );
|
||||
|
||||
|
|
@ -186,6 +188,7 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio
|
|||
obj.allowed_assets = o.extensions.value.buyback_options->markets;
|
||||
obj.allowed_assets->emplace( o.extensions.value.buyback_options->asset_to_buy );
|
||||
}
|
||||
obj.affiliate_distributions = o.extensions.value.affiliate_distributions;
|
||||
});
|
||||
|
||||
if( has_small_percent )
|
||||
|
|
|
|||
95
libraries/chain/affiliate_payout.cpp
Normal file
95
libraries/chain/affiliate_payout.cpp
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/affiliate_payout.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
#include <fc/uint128.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
share_type affiliate_payout_helper::payout( account_id_type player, share_type amount )
|
||||
{
|
||||
return payout( player(_db), amount );
|
||||
}
|
||||
|
||||
share_type affiliate_payout_helper::payout( const account_object& player, share_type amount )
|
||||
{
|
||||
if( !player.affiliate_distributions.valid() )
|
||||
return 0;
|
||||
const auto& dist = player.affiliate_distributions->_dists.find( tag );
|
||||
if( dist == player.affiliate_distributions->_dists.end() || dist->second._dist.empty() )
|
||||
return 0;
|
||||
|
||||
amount = amount.value / 5; // 20% fixed
|
||||
if( amount <= 0 )
|
||||
return 0;
|
||||
|
||||
uint16_t remaining = GRAPHENE_100_PERCENT;
|
||||
share_type paid = 0;
|
||||
share_type to_pay = amount;
|
||||
for( const auto& entry : dist->second._dist )
|
||||
{
|
||||
const account_id_type affiliate = entry.first;
|
||||
const uint16_t share = entry.second;
|
||||
fc::uint128_t payout = to_pay.value;
|
||||
if( share != remaining )
|
||||
{
|
||||
FC_ASSERT( share < remaining );
|
||||
payout *= share;
|
||||
payout /= remaining;
|
||||
//ilog("Paying ${p} of ${P} for ${s} of ${r}", ("p",payout.to_uint64())("P",to_pay.value)("s",share)("r",remaining) );
|
||||
remaining -= share;
|
||||
}
|
||||
FC_ASSERT( payout.to_uint64() <= to_pay );
|
||||
if( payout > 0 )
|
||||
{
|
||||
if ( accumulator.find(affiliate) == accumulator.end() )
|
||||
accumulator[affiliate] = payout.to_uint64();
|
||||
else
|
||||
accumulator[affiliate] += payout.to_uint64();
|
||||
to_pay -= payout.to_uint64();
|
||||
paid += payout.to_uint64();
|
||||
}
|
||||
}
|
||||
FC_ASSERT( to_pay == 0 );
|
||||
FC_ASSERT( paid == amount );
|
||||
|
||||
_db.push_applied_operation( affiliate_referral_payout_operation( player.id, asset( amount, payout_asset ) ) );
|
||||
|
||||
return paid;
|
||||
}
|
||||
|
||||
void affiliate_payout_helper::commit()
|
||||
{
|
||||
for( const auto& entry : accumulator )
|
||||
{
|
||||
asset payout = asset( entry.second, payout_asset );
|
||||
_db.adjust_balance( entry.first, payout );
|
||||
_db.push_applied_operation( affiliate_payout_operation( entry.first, tag, payout ) );
|
||||
}
|
||||
accumulator.clear();
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
393
libraries/chain/betting_market_evaluator.cpp
Normal file
393
libraries/chain/betting_market_evaluator.cpp
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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(db().head_block_time() >= HARDFORK_1000_TIME);
|
||||
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(db().head_block_time() >= HARDFORK_1000_TIME);
|
||||
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(d.head_block_time() >= HARDFORK_1000_TIME);
|
||||
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(d.head_block_time() >= HARDFORK_1000_TIME);
|
||||
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(db().head_block_time() >= HARDFORK_1000_TIME);
|
||||
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(d.head_block_time() >= HARDFORK_1000_TIME);
|
||||
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();
|
||||
FC_ASSERT(d.head_block_time() >= HARDFORK_1000_TIME);
|
||||
_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" );
|
||||
FC_ASSERT( _betting_market_group->get_status() != betting_market_group_status::closed,
|
||||
"Unable to place bets while the market is closed" );
|
||||
FC_ASSERT( _betting_market_group->get_status() != betting_market_group_status::graded,
|
||||
"Unable to place bets while the market is graded" );
|
||||
FC_ASSERT( _betting_market_group->get_status() != betting_market_group_status::re_grading,
|
||||
"Unable to place bets while the market is re-grading" );
|
||||
FC_ASSERT( _betting_market_group->get_status() != betting_market_group_status::settled,
|
||||
"Unable to place bets while the market is settled" );
|
||||
|
||||
_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");
|
||||
|
||||
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()
|
||||
|
||||
// place the bet, this may return guaranteed winnings
|
||||
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);
|
||||
|
||||
// now that their guaranteed winnings have been returned, check whether 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) );
|
||||
|
||||
// pay for it
|
||||
d.adjust_balance(fee_paying_account->id, -op.amount_to_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();
|
||||
FC_ASSERT(d.head_block_time() >= HARDFORK_1000_TIME);
|
||||
_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();
|
||||
FC_ASSERT(d.head_block_time() >= HARDFORK_1000_TIME);
|
||||
_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 {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_1000_TIME);
|
||||
_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
|
||||
578
libraries/chain/betting_market_group_object.cpp
Normal file
578
libraries/chain/betting_market_group_object.cpp
Normal file
|
|
@ -0,0 +1,578 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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 <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");
|
||||
}
|
||||
|
||||
template <class Fsm>
|
||||
void no_transition(canceled_event const& e, Fsm&, int state)
|
||||
{
|
||||
//ignore transitions from settled to canceled state
|
||||
//and from canceled to canceled state
|
||||
}
|
||||
|
||||
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 (!strstr(filled_state_names[i], fc_reflected_value_name))
|
||||
{
|
||||
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));
|
||||
++error_count;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (error_count == 0)
|
||||
dlog("Betting market group status constants are correct");
|
||||
else
|
||||
wlog("There were ${count} errors in the betting market group status constants", ("count", error_count));
|
||||
|
||||
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
|
||||
|
||||
496
libraries/chain/betting_market_object.cpp
Normal file
496
libraries/chain/betting_market_object.cpp
Normal file
|
|
@ -0,0 +1,496 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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 <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,
|
||||
graded,
|
||||
canceled,
|
||||
settled
|
||||
};
|
||||
} }
|
||||
FC_REFLECT_ENUM(graphene::chain::betting_market_state,
|
||||
(unresolved)
|
||||
(frozen)
|
||||
(closed)
|
||||
(graded)
|
||||
(canceled)
|
||||
(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;
|
||||
fsm.betting_market_obj->cancel_all_bets(event.db);
|
||||
}
|
||||
};
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
template <class Fsm>
|
||||
void no_transition(canceled_event const& e, Fsm&, int state)
|
||||
{
|
||||
//ignore transitions from settled to canceled state
|
||||
//and from canceled to canceled state
|
||||
}
|
||||
|
||||
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),
|
||||
resolution(rhs.resolution),
|
||||
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;
|
||||
resolution = rhs.resolution;
|
||||
|
||||
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 (!strstr(filled_state_names[i], fc_reflected_value_name))
|
||||
{
|
||||
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));
|
||||
++error_count;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (error_count == 0)
|
||||
dlog("Betting market status constants are correct");
|
||||
else
|
||||
wlog("There were ${count} errors in the betting market status constants", ("count", error_count));
|
||||
|
||||
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::cancel_all_bets(database& db) const
|
||||
{
|
||||
const auto& bets_by_market_id = db.get_index_type<bet_object_index>().indices().get<by_betting_market_id>();
|
||||
|
||||
auto bet_it = bets_by_market_id.lower_bound(id);
|
||||
auto bet_it_end = bets_by_market_id.upper_bound(id);
|
||||
while (bet_it != bet_it_end)
|
||||
{
|
||||
auto old_bet_it = bet_it;
|
||||
++bet_it;
|
||||
db.cancel_bet(*old_bet_it, 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
|
||||
|
||||
|
|
@ -25,6 +25,7 @@
|
|||
#include <graphene/chain/committee_member_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
#include <graphene/chain/protocol/vote.hpp>
|
||||
#include <graphene/chain/transaction_evaluation_state.hpp>
|
||||
|
|
@ -77,12 +78,28 @@ void_result committee_member_update_global_parameters_evaluator::do_evaluate(con
|
|||
{ try {
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
|
||||
if( db().head_block_time() < HARDFORK_1000_TIME ) // TODO: remove after hf
|
||||
FC_ASSERT( !o.new_parameters.extensions.value.min_bet_multiplier.valid()
|
||||
&& !o.new_parameters.extensions.value.max_bet_multiplier.valid()
|
||||
&& !o.new_parameters.extensions.value.betting_rake_fee_percentage.valid()
|
||||
&& !o.new_parameters.extensions.value.permitted_betting_odds_increments.valid()
|
||||
&& !o.new_parameters.extensions.value.live_betting_delay_time.valid(),
|
||||
"Parameter extensions are not allowed yet!" );
|
||||
|
||||
dgpo = &db().get_global_properties();
|
||||
if( o.new_parameters.extensions.value.min_bet_multiplier.valid()
|
||||
&& !o.new_parameters.extensions.value.max_bet_multiplier.valid() )
|
||||
FC_ASSERT( *o.new_parameters.extensions.value.min_bet_multiplier < dgpo->parameters.max_bet_multiplier() );
|
||||
if( !o.new_parameters.extensions.value.min_bet_multiplier.valid()
|
||||
&& o.new_parameters.extensions.value.max_bet_multiplier.valid() )
|
||||
FC_ASSERT( dgpo->parameters.min_bet_multiplier() < *o.new_parameters.extensions.value.max_bet_multiplier );
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void_result committee_member_update_global_parameters_evaluator::do_apply(const committee_member_update_global_parameters_operation& o)
|
||||
{ try {
|
||||
db().modify(db().get_global_properties(), [&o](global_property_object& p) {
|
||||
db().modify(*dgpo, [&o](global_property_object& p) {
|
||||
p.pending_parameters = o.new_parameters;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
646
libraries/chain/db_bet.cpp
Normal file
646
libraries/chain/db_bet.cpp
Normal file
|
|
@ -0,0 +1,646 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/affiliate_payout.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/range/join.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);
|
||||
//fc_idump(fc::logger::get("betting"), (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)
|
||||
{
|
||||
fc_ilog(fc::logger::get("betting"), "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;
|
||||
}
|
||||
|
||||
affiliate_payout_helper payout_helper( *this, betting_market_group );
|
||||
|
||||
// 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();
|
||||
share_type affiliates_share;
|
||||
if (rake_amount.value)
|
||||
affiliates_share = payout_helper.payout( bettor_id, rake_amount );
|
||||
FC_ASSERT( rake_amount.value >= affiliates_share.value );
|
||||
if (rake_amount.value > affiliates_share.value)
|
||||
adjust_balance(*rake_account_id, asset(rake_amount - affiliates_share, betting_market_group.asset_id));
|
||||
}
|
||||
|
||||
// pay winning - rake
|
||||
adjust_balance(bettor_id, asset(payout_amounts - rake_amount, betting_market_group.asset_id));
|
||||
// [ROL]
|
||||
//fc_idump(fc::logger::get("betting"), (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;
|
||||
fc_dlog(fc::logger::get("betting"), "removing betting market ${id}", ("id", betting_market.id));
|
||||
remove(betting_market);
|
||||
}
|
||||
|
||||
const event_object& event = betting_market_group.event_id(*this);
|
||||
|
||||
fc_dlog(fc::logger::get("betting"), "removing betting market group ${id}", ("id", betting_market_group.id));
|
||||
remove(betting_market_group);
|
||||
|
||||
payout_helper.commit();
|
||||
}
|
||||
|
||||
void database::remove_completed_events()
|
||||
{
|
||||
const auto& event_index = get_index_type<event_object_index>().indices().get<by_event_status>();
|
||||
|
||||
auto canceled_event_iter = event_index.lower_bound(event_status::canceled);
|
||||
while (canceled_event_iter != event_index.end() && canceled_event_iter->get_status() == event_status::canceled)
|
||||
{
|
||||
const event_object& event = *canceled_event_iter;
|
||||
++canceled_event_iter;
|
||||
fc_dlog(fc::logger::get("betting"), "removing canceled event ${id}", ("id", event.id));
|
||||
remove(event);
|
||||
}
|
||||
|
||||
auto settled_event_iter = event_index.lower_bound(event_status::settled);
|
||||
while (settled_event_iter != event_index.end() && settled_event_iter->get_status() == event_status::settled)
|
||||
{
|
||||
const event_object& event = *settled_event_iter;
|
||||
++settled_event_iter;
|
||||
fc_dlog(fc::logger::get("betting"), "removing settled 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);
|
||||
//fc_edump(fc::logger::get("betting"), (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 )
|
||||
{
|
||||
//fc_idump(fc::logger::get("betting"), (taker_bet)(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;
|
||||
//fc_idump(fc::logger::get("betting"), (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;
|
||||
// we need to figure out how much of the bet matches. the smallest amount
|
||||
// that could match is one maker_odds_ratio to one taker_odds_ratio,
|
||||
// but we can match any integer multiple of that ratio (called the 'factor' below),
|
||||
// limited only by the bet amounts.
|
||||
//
|
||||
//fc_idump(fc::logger::get("betting"), (back_odds_ratio)(lay_odds_ratio));
|
||||
//fc_idump(fc::logger::get("betting"), (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_factor_taker_is_willing_to_pay = taker_bet.amount_to_bet.amount / taker_odds_ratio;
|
||||
|
||||
share_type maximum_taker_factor = maximum_factor_taker_is_willing_to_pay;
|
||||
if (taker_bet.back_or_lay == bet_type::lay) {
|
||||
share_type maximum_factor_taker_is_willing_to_receive = taker_bet.get_exact_matching_amount() / maker_odds_ratio;
|
||||
//fc_idump(fc::logger::get("betting"), (maximum_factor_taker_is_willing_to_pay));
|
||||
bool taker_was_limited_by_matching_amount = maximum_factor_taker_is_willing_to_receive < maximum_factor_taker_is_willing_to_pay;
|
||||
if (taker_was_limited_by_matching_amount)
|
||||
maximum_taker_factor = maximum_factor_taker_is_willing_to_receive;
|
||||
}
|
||||
//fc_idump(fc::logger::get("betting"), (maximum_factor_taker_is_willing_to_pay)(maximum_taker_factor));
|
||||
|
||||
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;
|
||||
fc_idump(fc::logger::get("betting"), (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
|
||||
|
||||
//fc_idump(fc::logger::get("betting"), (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 (maker_bet_will_completely_match && taker_amount_to_match != taker_bet.amount_to_bet.amount)
|
||||
{
|
||||
// then the taker bet will stay on the books. If the taker odds != the maker odds, we will
|
||||
// need to refund the stake the taker was expecting to pay but didn't.
|
||||
// compute how much of the taker's bet should still be left on the books and how much
|
||||
// the taker should pay for the remaining amount; refund any amount that won't remain
|
||||
// on the books and isn't used to pay the bet we're currently matching.
|
||||
|
||||
share_type takers_odds_back_odds_ratio;
|
||||
share_type takers_odds_lay_odds_ratio;
|
||||
std::tie(takers_odds_back_odds_ratio, takers_odds_lay_odds_ratio) = taker_bet.get_ratio();
|
||||
const share_type& takers_odds_taker_odds_ratio = taker_bet.back_or_lay == bet_type::back ? takers_odds_back_odds_ratio : takers_odds_lay_odds_ratio;
|
||||
const share_type& takers_odds_maker_odds_ratio = taker_bet.back_or_lay == bet_type::back ? takers_odds_lay_odds_ratio : takers_odds_back_odds_ratio;
|
||||
share_type taker_refund_amount;
|
||||
|
||||
if (taker_bet.back_or_lay == bet_type::back)
|
||||
{
|
||||
// because we matched at the maker's odds and not the taker's odds, the remaining amount to match
|
||||
// may not be an even multiple of the taker's odds; round it down.
|
||||
share_type taker_remaining_factor = (taker_bet.amount_to_bet.amount - taker_amount_to_match) / takers_odds_taker_odds_ratio;
|
||||
share_type taker_remaining_bet_amount = taker_remaining_factor * takers_odds_taker_odds_ratio;
|
||||
taker_refund_amount = taker_bet.amount_to_bet.amount - taker_amount_to_match - taker_remaining_bet_amount;
|
||||
//idump((taker_remaining_factor)(taker_remaining_bet_amount)(taker_refund_amount));
|
||||
}
|
||||
else
|
||||
{
|
||||
// the taker bet is a lay bet. because we matched at the maker's odds and not the taker's odds,
|
||||
// there are two things we need to take into account. First, we may have achieved more of a position
|
||||
// than we expected had we matched at our taker odds. If so, we can refund the unused stake.
|
||||
// Second, the remaining amount to match may not be an even multiple of the taker's odds; round it down.
|
||||
share_type unrounded_taker_remaining_amount_to_match = taker_bet.get_exact_matching_amount() - maker_amount_to_match;
|
||||
//idump((unrounded_taker_remaining_amount_to_match));
|
||||
|
||||
// because we matched at the maker's odds and not the taker's odds, the remaining amount to match
|
||||
// may not be an even multiple of the taker's odds; round it down.
|
||||
share_type taker_remaining_factor = unrounded_taker_remaining_amount_to_match / takers_odds_maker_odds_ratio;
|
||||
share_type taker_remaining_maker_amount_to_match = taker_remaining_factor * takers_odds_maker_odds_ratio;
|
||||
share_type taker_remaining_bet_amount = taker_remaining_factor * takers_odds_taker_odds_ratio;
|
||||
|
||||
taker_refund_amount = taker_bet.amount_to_bet.amount - taker_amount_to_match - taker_remaining_bet_amount;
|
||||
//idump((taker_remaining_factor)(taker_remaining_maker_amount_to_match)(taker_remaining_bet_amount)(taker_refund_amount));
|
||||
}
|
||||
|
||||
if (taker_refund_amount > share_type())
|
||||
{
|
||||
db.modify(taker_bet, [&taker_refund_amount](bet_object& taker_bet_object) {
|
||||
taker_bet_object.amount_to_bet.amount -= taker_refund_amount;
|
||||
});
|
||||
fc_dlog(fc::logger::get("betting"), "Refunding ${taker_refund_amount} to taker because we matched at the maker's odds of "
|
||||
"${maker_odds} instead of the taker's odds ${taker_odds}",
|
||||
("taker_refund_amount", taker_refund_amount)
|
||||
("maker_odds", maker_bet.backer_multiplier)
|
||||
("taker_odds", taker_bet.backer_multiplier));
|
||||
fc_ddump(fc::logger::get("betting"), (taker_bet));
|
||||
|
||||
db.adjust_balance(taker_bet.bettor_id, asset(taker_refund_amount, taker_bet.amount_to_bet.asset_id));
|
||||
// TODO: update global statistics
|
||||
bet_adjusted_operation bet_adjusted_op(taker_bet.bettor_id, taker_bet.id,
|
||||
asset(taker_refund_amount, taker_bet.amount_to_bet.asset_id));
|
||||
// fc_idump(fc::logger::get("betting"), (bet_adjusted_op)(new_bet_object));
|
||||
db.push_applied_operation(std::move(bet_adjusted_op));
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// We allow users to place bets for any amount, but only amounts that are exact multiples of the odds
|
||||
// ratio can be matched. Immediately return any unmatchable amount in this bet.
|
||||
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;
|
||||
|
||||
if (rounded_bet_amount == share_type())
|
||||
{
|
||||
// the bet was too small to match at all, 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);
|
||||
// fc_idump(fc::logger::get("betting"), (bet_adjusted_op)(new_bet_object));
|
||||
push_applied_operation(std::move(bet_adjusted_op));
|
||||
|
||||
fc_dlog(fc::logger::get("betting"), "Refunded ${refund_amount} to round the bet down to something that can match exactly, new bet: ${new_bet}",
|
||||
("refund_amount", stake_returned.amount)
|
||||
("new_bet", 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));
|
||||
|
||||
// fc_ilog(fc::logger::get("betting"), "");
|
||||
// fc_ilog(fc::logger::get("betting"), "------------ order book ------------------");
|
||||
// for (auto itr = book_itr; itr != book_end; ++itr)
|
||||
// fc_idump(fc::logger::get("betting"), (*itr));
|
||||
// fc_ilog(fc::logger::get("betting"), "------------ 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))
|
||||
fc_ddump(fc::logger::get("betting"), (new_bet_object));
|
||||
|
||||
|
||||
// return true if the taker bet was completely consumed
|
||||
return (orders_matched_flags & 1) != 0;
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
@ -26,18 +26,63 @@
|
|||
#include <graphene/chain/db_with.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/chain/protocol/betting_market.hpp>
|
||||
|
||||
#include <graphene/chain/block_summary_object.hpp>
|
||||
#include <graphene/chain/global_property_object.hpp>
|
||||
#include <graphene/chain/operation_history_object.hpp>
|
||||
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/transaction_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <fc/crypto/digest.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
struct proposed_operations_digest_accumulator
|
||||
{
|
||||
typedef void result_type;
|
||||
|
||||
void operator()(const graphene::chain::proposal_create_operation& proposal)
|
||||
{
|
||||
for (auto& operation: proposal.proposed_ops)
|
||||
{
|
||||
proposed_operations_digests.push_back(fc::digest(operation.op));
|
||||
}
|
||||
}
|
||||
|
||||
//empty template method is needed for all other operation types
|
||||
//we can ignore them, we are interested in only proposal_create_operation
|
||||
template<class T>
|
||||
void operator()(const T&)
|
||||
{}
|
||||
|
||||
std::vector<fc::sha256> proposed_operations_digests;
|
||||
};
|
||||
|
||||
std::vector<fc::sha256> gather_proposed_operations_digests(const graphene::chain::transaction& trx)
|
||||
{
|
||||
proposed_operations_digest_accumulator digest_accumulator;
|
||||
|
||||
for (auto& operation: trx.operations)
|
||||
{
|
||||
if( operation.which() != graphene::chain::operation::tag<graphene::chain::betting_market_group_create_operation>::value
|
||||
&& operation.which() != graphene::chain::operation::tag<graphene::chain::betting_market_create_operation>::value )
|
||||
operation.visit(digest_accumulator);
|
||||
else
|
||||
edump( ("Found dup"));
|
||||
}
|
||||
|
||||
return digest_accumulator.proposed_operations_digests;
|
||||
}
|
||||
}
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
bool database::is_known_block( const block_id_type& id )const
|
||||
|
|
@ -104,6 +149,30 @@ std::vector<block_id_type> database::get_block_ids_on_fork(block_id_type head_of
|
|||
return result;
|
||||
}
|
||||
|
||||
void database::check_tansaction_for_duplicated_operations(const signed_transaction& trx)
|
||||
{
|
||||
const auto& proposal_index = get_index<proposal_object>();
|
||||
std::set<fc::sha256> existed_operations_digests;
|
||||
|
||||
proposal_index.inspect_all_objects( [&](const object& obj){
|
||||
const proposal_object& proposal = static_cast<const proposal_object&>(obj);
|
||||
auto proposed_operations_digests = gather_proposed_operations_digests( proposal.proposed_transaction );
|
||||
existed_operations_digests.insert( proposed_operations_digests.begin(), proposed_operations_digests.end() );
|
||||
});
|
||||
|
||||
for (auto& pending_transaction: _pending_tx)
|
||||
{
|
||||
auto proposed_operations_digests = gather_proposed_operations_digests(pending_transaction);
|
||||
existed_operations_digests.insert(proposed_operations_digests.begin(), proposed_operations_digests.end());
|
||||
}
|
||||
|
||||
auto proposed_operations_digests = gather_proposed_operations_digests(trx);
|
||||
for (auto& digest: proposed_operations_digests)
|
||||
{
|
||||
FC_ASSERT(existed_operations_digests.count(digest) == 0, "Proposed operation is already pending for approval.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Push block "may fail" in which case every partial change is unwound. After
|
||||
* push block is successful the block is appended to the chain database on disk.
|
||||
|
|
@ -241,7 +310,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();
|
||||
|
||||
|
|
@ -285,13 +354,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,
|
||||
|
|
@ -477,6 +546,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 )
|
||||
|
|
@ -540,12 +613,14 @@ void database::_apply_block( const signed_block& next_block )
|
|||
check_ending_lotteries();
|
||||
|
||||
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
|
||||
|
|
@ -563,27 +638,9 @@ void database::_apply_block( const signed_block& next_block )
|
|||
_applied_ops.clear();
|
||||
|
||||
notify_changed_objects();
|
||||
|
||||
} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) }
|
||||
|
||||
void database::notify_changed_objects()
|
||||
{ try {
|
||||
if( _undo_db.enabled() )
|
||||
{
|
||||
const auto& head_undo = _undo_db.head();
|
||||
vector<object_id_type> changed_ids; changed_ids.reserve(head_undo.old_values.size());
|
||||
for( const auto& item : head_undo.old_values ) changed_ids.push_back(item.first);
|
||||
for( const auto& item : head_undo.new_ids ) changed_ids.push_back(item);
|
||||
vector<const object*> removed;
|
||||
removed.reserve( head_undo.removed.size() );
|
||||
for( const auto& item : head_undo.removed )
|
||||
{
|
||||
changed_ids.push_back( item.first );
|
||||
removed.emplace_back( item.second.get() );
|
||||
}
|
||||
changed_objects(changed_ids);
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
|
||||
processed_transaction database::apply_transaction(const signed_transaction& trx, uint32_t skip)
|
||||
{
|
||||
|
|
@ -670,13 +727,10 @@ operation_result database::apply_operation(transaction_evaluation_state& eval_st
|
|||
{ try {
|
||||
int i_which = op.which();
|
||||
uint64_t u_which = uint64_t( i_which );
|
||||
if( i_which < 0 )
|
||||
assert( "Negative operation tag" && false );
|
||||
if( u_which >= _operation_evaluators.size() )
|
||||
assert( "No registered evaluator for this operation" && false );
|
||||
FC_ASSERT( i_which >= 0, "Negative operation tag in operation ${op}", ("op",op) );
|
||||
FC_ASSERT( u_which < _operation_evaluators.size(), "No registered evaluator for operation ${op}", ("op",op) );
|
||||
unique_ptr<op_evaluator>& eval = _operation_evaluators[ u_which ];
|
||||
if( !eval )
|
||||
assert( "No registered evaluator for this operation" && false );
|
||||
FC_ASSERT( eval, "No registered evaluator for operation ${op}", ("op",op) );
|
||||
auto op_id = push_applied_operation( op );
|
||||
auto result = eval->evaluate( eval_state, op, true );
|
||||
set_applied_operation_result( op_id, result );
|
||||
|
|
|
|||
|
|
@ -50,6 +50,13 @@
|
|||
#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>
|
||||
#include <graphene/chain/lottery_evaluator.hpp>
|
||||
|
|
@ -65,6 +72,10 @@
|
|||
#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>
|
||||
|
|
@ -131,6 +142,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()
|
||||
{
|
||||
|
|
@ -177,6 +215,25 @@ 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<sport_delete_evaluator>();
|
||||
register_evaluator<event_group_create_evaluator>();
|
||||
register_evaluator<event_group_update_evaluator>();
|
||||
register_evaluator<event_group_delete_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>();
|
||||
|
|
@ -213,6 +270,13 @@ 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> >();
|
||||
|
|
@ -235,8 +299,11 @@ 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 > >();
|
||||
|
||||
|
|
@ -338,7 +405,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT;
|
||||
}).get_id() == GRAPHENE_PROXY_TO_SELF_ACCOUNT);
|
||||
FC_ASSERT(create<account_object>([this](account_object& a) {
|
||||
a.name = "test-dividend-distribution";
|
||||
a.name = "default-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;
|
||||
|
|
@ -346,7 +413,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
a.membership_expiration_date = time_point_sec::maximum();
|
||||
a.network_fee_percentage = 0;
|
||||
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT;
|
||||
}).get_id() == TOURNAMENT_RAKE_FEE_ACCOUNT_ID);
|
||||
}).get_id() == GRAPHENE_RAKE_FEE_ACCOUNT_ID);
|
||||
// Create more special accounts
|
||||
while( true )
|
||||
{
|
||||
|
|
@ -379,7 +446,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
a.options.minimum_fee_percentage = 10*GRAPHENE_1_PERCENT;
|
||||
a.options.next_payout_time = genesis_state.initial_timestamp + fc::days(1);
|
||||
a.options.payout_interval = 30*24*60*60;
|
||||
a.dividend_distribution_account = TOURNAMENT_RAKE_FEE_ACCOUNT_ID;
|
||||
a.dividend_distribution_account = GRAPHENE_RAKE_FEE_ACCOUNT_ID;
|
||||
});
|
||||
|
||||
const asset_object& core_asset =
|
||||
|
|
@ -412,18 +479,18 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
a.options.minimum_fee_percentage = 10*GRAPHENE_1_PERCENT;
|
||||
a.options.next_payout_time = genesis_state.initial_timestamp + fc::hours(1);
|
||||
a.options.payout_interval = 7*24*60*60;
|
||||
a.dividend_distribution_account = TOURNAMENT_RAKE_FEE_ACCOUNT_ID;
|
||||
a.dividend_distribution_account = GRAPHENE_RAKE_FEE_ACCOUNT_ID;
|
||||
});
|
||||
|
||||
const asset_object& default_asset =
|
||||
create<asset_object>( [&]( asset_object& a ) {
|
||||
a.symbol = "DEFAULT";
|
||||
a.symbol = "DEF";
|
||||
a.options.max_market_fee =
|
||||
a.options.max_supply = genesis_state.max_core_supply;
|
||||
a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;
|
||||
a.options.flags = 0;
|
||||
a.options.issuer_permissions = 79;
|
||||
a.issuer = TOURNAMENT_RAKE_FEE_ACCOUNT_ID;
|
||||
a.issuer = GRAPHENE_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;
|
||||
|
|
@ -433,6 +500,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
});
|
||||
assert( default_asset.id == asset_id_type(1) );
|
||||
#endif
|
||||
|
||||
// Create more special assets
|
||||
while( true )
|
||||
{
|
||||
|
|
@ -476,6 +544,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" );
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@
|
|||
#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>
|
||||
|
|
@ -226,7 +228,7 @@ void database::update_active_witnesses()
|
|||
{
|
||||
vote_counter vc;
|
||||
for( const witness_object& wit : wits )
|
||||
vc.add( wit.witness_account, _vote_tally_buffer[wit.vote_id] );
|
||||
vc.add( wit.witness_account, std::max(_vote_tally_buffer[wit.vote_id], UINT64_C(1)) );
|
||||
vc.finish( a.active );
|
||||
}
|
||||
} );
|
||||
|
|
@ -308,7 +310,7 @@ void database::update_active_committee_members()
|
|||
{
|
||||
vote_counter vc;
|
||||
for( const committee_member_object& cm : committee_members )
|
||||
vc.add( cm.committee_member_account, _vote_tally_buffer[cm.vote_id] );
|
||||
vc.add( cm.committee_member_account, std::max(_vote_tally_buffer[cm.vote_id], UINT64_C(1)) );
|
||||
vc.finish( a.active );
|
||||
}
|
||||
} );
|
||||
|
|
@ -735,7 +737,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
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)
|
||||
{
|
||||
{ try {
|
||||
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 =
|
||||
|
|
@ -759,6 +761,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
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));
|
||||
|
|
@ -771,6 +774,20 @@ void schedule_pending_dividend_balances(database& db,
|
|||
// ("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;
|
||||
|
|
@ -1042,10 +1059,10 @@ void schedule_pending_dividend_balances(database& db,
|
|||
dividend_data_obj.last_distribution_time = current_head_block_time;
|
||||
});
|
||||
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void process_dividend_assets(database& db)
|
||||
{
|
||||
{ try {
|
||||
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>();
|
||||
|
|
@ -1066,10 +1083,11 @@ void process_dividend_assets(database& db)
|
|||
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)
|
||||
{
|
||||
try
|
||||
{
|
||||
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>();
|
||||
|
|
@ -1089,7 +1107,7 @@ void process_dividend_assets(database& db)
|
|||
// 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;
|
||||
vector<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
|
||||
|
|
@ -1132,7 +1150,7 @@ void process_dividend_assets(database& db)
|
|||
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,
|
||||
payouts_for_this_holder.push_back(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;
|
||||
|
|
@ -1199,11 +1217,13 @@ void process_dividend_assets(database& db)
|
|||
(dividend_data_obj.options.next_payout_time));
|
||||
});
|
||||
}
|
||||
FC_RETHROW_EXCEPTIONS(error, "Error while paying out dividends for holder asset ${holder_asset}", ("holder_asset", dividend_holder_asset_obj.symbol))
|
||||
}
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)
|
||||
{
|
||||
{ try {
|
||||
const auto& gpo = get_global_properties();
|
||||
|
||||
distribute_fba_balances(*this);
|
||||
|
|
@ -1225,6 +1245,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
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 =
|
||||
|
|
@ -1236,6 +1257,19 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
// ("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) {
|
||||
|
|
@ -1244,10 +1278,15 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
// There may be a difference between the account whose stake is voting and the one specifying opinions.
|
||||
// Usually they're the same, but if the stake account has specified a voting_account, that account is the one
|
||||
// specifying the opinions.
|
||||
const account_object& opinion_account =
|
||||
const account_object* opinion_account_ptr =
|
||||
(stake_account.options.voting_account ==
|
||||
GRAPHENE_PROXY_TO_SELF_ACCOUNT)? stake_account
|
||||
: d.get(stake_account.options.voting_account);
|
||||
GRAPHENE_PROXY_TO_SELF_ACCOUNT)? &stake_account
|
||||
: d.find(stake_account.options.voting_account);
|
||||
|
||||
if( !opinion_account_ptr ) // skip non-exist account
|
||||
return;
|
||||
|
||||
const account_object& opinion_account = *opinion_account_ptr;
|
||||
|
||||
const auto& stats = stake_account.statistics(d);
|
||||
uint64_t voting_stake = stats.total_core_in_orders.value
|
||||
|
|
@ -1257,7 +1296,6 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
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();
|
||||
|
|
@ -1333,6 +1371,16 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
|
||||
if( p.pending_parameters )
|
||||
{
|
||||
if( !p.pending_parameters->extensions.value.min_bet_multiplier.valid() )
|
||||
p.pending_parameters->extensions.value.min_bet_multiplier = p.parameters.extensions.value.min_bet_multiplier;
|
||||
if( !p.pending_parameters->extensions.value.max_bet_multiplier.valid() )
|
||||
p.pending_parameters->extensions.value.max_bet_multiplier = p.parameters.extensions.value.max_bet_multiplier;
|
||||
if( !p.pending_parameters->extensions.value.betting_rake_fee_percentage.valid() )
|
||||
p.pending_parameters->extensions.value.betting_rake_fee_percentage = p.parameters.extensions.value.betting_rake_fee_percentage;
|
||||
if( !p.pending_parameters->extensions.value.permitted_betting_odds_increments.valid() )
|
||||
p.pending_parameters->extensions.value.permitted_betting_odds_increments = p.parameters.extensions.value.permitted_betting_odds_increments;
|
||||
if( !p.pending_parameters->extensions.value.live_betting_delay_time.valid() )
|
||||
p.pending_parameters->extensions.value.live_betting_delay_time = p.parameters.extensions.value.live_betting_delay_time;
|
||||
p.parameters = std::move(*p.pending_parameters);
|
||||
p.pending_parameters.reset();
|
||||
}
|
||||
|
|
@ -1379,12 +1427,13 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
});
|
||||
|
||||
// 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
|
||||
process_budget();
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -64,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..." );
|
||||
// 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() )
|
||||
{
|
||||
|
|
@ -88,6 +94,15 @@ 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;
|
||||
}
|
||||
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 |
|
||||
|
|
@ -95,6 +110,7 @@ void database::reindex(fc::path data_dir, const genesis_state_type& initial_allo
|
|||
skip_witness_schedule_check |
|
||||
skip_authority_check);
|
||||
}
|
||||
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 ) );
|
||||
|
|
@ -164,8 +180,9 @@ void database::close(bool rewind)
|
|||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
catch ( const fc::exception& e )
|
||||
{
|
||||
wlog( "Database close unexpected exception: ${e}", ("e", e) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,6 +200,11 @@ void database::close(bool rewind)
|
|||
_fork_db.reset();
|
||||
}
|
||||
|
||||
void database::force_slow_replays()
|
||||
{
|
||||
ilog("enabling slow replays");
|
||||
_slow_replays = true;
|
||||
}
|
||||
|
||||
void database::check_ending_lotteries()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
475
libraries/chain/db_notify.cpp
Normal file
475
libraries/chain/db_notify.cpp
Normal file
|
|
@ -0,0 +1,475 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <fc/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 sport_delete_operation&){}
|
||||
void operator()(const event_group_create_operation&){}
|
||||
void operator()(const event_group_update_operation& op ) {}
|
||||
void operator()(const event_group_delete_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 operator()( const affiliate_payout_operation& op )
|
||||
{
|
||||
_impacted.insert( op.affiliate );
|
||||
}
|
||||
void operator()( const affiliate_referral_payout_operation& op ) { }
|
||||
};
|
||||
|
||||
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) ) }
|
||||
|
||||
} }
|
||||
|
|
@ -35,6 +35,7 @@
|
|||
#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>
|
||||
|
||||
|
|
@ -184,8 +185,54 @@ void database::clear_expired_transactions()
|
|||
//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()
|
||||
|
|
@ -608,4 +655,29 @@ void database::update_tournaments()
|
|||
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);
|
||||
remove_completed_events();
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
}
|
||||
|
||||
|
||||
} }
|
||||
|
|
|
|||
140
libraries/chain/event_evaluator.cpp
Normal file
140
libraries/chain/event_evaluator.cpp
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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(db().head_block_time() >= HARDFORK_1000_TIME);
|
||||
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(db().head_block_time() >= HARDFORK_1000_TIME);
|
||||
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();
|
||||
FC_ASSERT(d.head_block_time() >= HARDFORK_1000_TIME);
|
||||
//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
|
||||
121
libraries/chain/event_group_evaluator.cpp
Normal file
121
libraries/chain/event_group_evaluator.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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/event_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(db().head_block_time() >= HARDFORK_1000_TIME);
|
||||
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(db().head_block_time() >= HARDFORK_1000_TIME);
|
||||
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) ) }
|
||||
|
||||
|
||||
void_result event_group_delete_evaluator::do_evaluate(const event_group_delete_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_1001_TIME);
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
|
||||
//check for event group existence
|
||||
_event_group = &op.event_group_id(db());
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result event_group_delete_evaluator::do_apply(const event_group_delete_operation& op)
|
||||
{ try {
|
||||
database& _db = db();
|
||||
|
||||
_event_group->cancel_events(_db);
|
||||
_db.remove(*_event_group);
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // graphene::chain
|
||||
22
libraries/chain/event_group_object.cpp
Normal file
22
libraries/chain/event_group_object.cpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#define DEFAULT_LOGGER "betting"
|
||||
|
||||
#include <graphene/chain/event_group_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void event_group_object::cancel_events(database& db) const
|
||||
{
|
||||
const auto& events_for_group = db.get_index_type<event_object_index>().indices().get<by_event_group_id>();
|
||||
auto event_it = events_for_group.lower_bound(id);
|
||||
auto event_end_it = events_for_group.upper_bound(id);
|
||||
for (; event_it != event_end_it; ++event_it)
|
||||
{
|
||||
db.modify( *event_it, [&](event_object& event_obj) {
|
||||
event_obj.dispatch_new_status(db, event_status::canceled);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
584
libraries/chain/event_object.cpp
Normal file
584
libraries/chain/event_object.cpp
Normal file
|
|
@ -0,0 +1,584 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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"
|
||||
#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");
|
||||
}
|
||||
|
||||
template <class Fsm>
|
||||
void no_transition(canceled_event const& e, Fsm&, int state)
|
||||
{
|
||||
//ignore transitions from settled to canceled state
|
||||
//and from canceled to canceled state
|
||||
}
|
||||
|
||||
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 (!strstr(filled_state_names[i], fc_reflected_value_name))
|
||||
{
|
||||
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));
|
||||
++error_count;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (error_count == 0)
|
||||
dlog("Event status constants are correct");
|
||||
else
|
||||
wlog("There were ${count} errors in the event status constants", ("count", error_count));
|
||||
|
||||
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
|
||||
|
||||
|
||||
3
libraries/chain/hardfork.d/1000.hf
Normal file
3
libraries/chain/hardfork.d/1000.hf
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#ifndef HARDFORK_1000_TIME
|
||||
#define HARDFORK_1000_TIME (fc::time_point_sec( 1540000000 ))
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/1001.hf
Normal file
4
libraries/chain/hardfork.d/1001.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// added delete sport and delete event group operations
|
||||
#ifndef HARDFORK_1001_TIME
|
||||
#define HARDFORK_1001_TIME (fc::time_point_sec( 1540000000 ))
|
||||
#endif
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// #599 Unpacking of extension is incorrect
|
||||
#ifndef HARDFORK_599_TIME
|
||||
#define HARDFORK_599_TIME (fc::time_point_sec( 1458061200 ))
|
||||
#define HARDFORK_599_TIME (fc::time_point_sec( 1459789200 ))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// #607 Disable negative voting on workers
|
||||
#ifndef HARDFORK_607_TIME
|
||||
#define HARDFORK_607_TIME (fc::time_point_sec( 1458061200 ))
|
||||
#define HARDFORK_607_TIME (fc::time_point_sec( 1458752400 ))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// #613 Deprecate annual membership
|
||||
#ifndef HARDFORK_613_TIME
|
||||
#define HARDFORK_613_TIME (fc::time_point_sec( 1458061200 ))
|
||||
#define HARDFORK_613_TIME (fc::time_point_sec( 1458752400 ))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// #615 Fix price feed expiration check, so websocket server will never spam too much data
|
||||
#ifndef HARDFORK_615_TIME
|
||||
#define HARDFORK_615_TIME (fc::time_point_sec( 1457550000 ))
|
||||
#define HARDFORK_615_TIME (fc::time_point_sec( 1458752400 ))
|
||||
#endif
|
||||
|
|
|
|||
4
libraries/chain/hardfork.d/999.hf
Normal file
4
libraries/chain/hardfork.d/999.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// Placeholder HF for affiliate reward system
|
||||
#ifndef HARDFORK_999_TIME
|
||||
#define HARDFORK_999_TIME (fc::time_point_sec( 1540000000 ))
|
||||
#endif
|
||||
|
|
@ -50,7 +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
|
||||
|
|
@ -224,6 +227,8 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
optional< flat_set<asset_id_type> > allowed_assets;
|
||||
|
||||
optional< affiliate_reward_distributions > affiliate_distributions;
|
||||
|
||||
bool has_special_authority()const
|
||||
{
|
||||
return (owner_special_authority.which() != special_authority::tag< no_special_authority >::value)
|
||||
|
|
@ -443,7 +448,7 @@ FC_REFLECT_DERIVED( graphene::chain::account_object,
|
|||
(cashback_vb)
|
||||
(owner_special_authority)(active_special_authority)
|
||||
(top_n_control_flags)
|
||||
(allowed_assets)
|
||||
(allowed_assets)(affiliate_distributions)
|
||||
)
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::account_balance_object,
|
||||
|
|
@ -454,7 +459,7 @@ FC_REFLECT_DERIVED( graphene::chain::account_statistics_object,
|
|||
(graphene::chain::object),
|
||||
(owner)
|
||||
(most_recent_op)
|
||||
(total_ops)
|
||||
(total_ops)(removed_ops)
|
||||
(total_core_in_orders)
|
||||
(lifetime_fees_paid)
|
||||
(pending_fees)(pending_vested_fees)
|
||||
|
|
|
|||
90
libraries/chain/include/graphene/chain/affiliate_payout.hpp
Normal file
90
libraries/chain/include/graphene/chain/affiliate_payout.hpp
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/asset.hpp>
|
||||
#include <graphene/chain/protocol/affiliate.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class database;
|
||||
|
||||
namespace impl {
|
||||
class game_type_visitor {
|
||||
public:
|
||||
typedef app_tag result_type;
|
||||
|
||||
inline app_tag operator()( const rock_paper_scissors_game_options& o )const { return rps; }
|
||||
};
|
||||
}
|
||||
|
||||
template<typename GAME>
|
||||
app_tag get_tag_for_game( const GAME& game );
|
||||
|
||||
template<>
|
||||
inline app_tag get_tag_for_game( const betting_market_group_object& game )
|
||||
{
|
||||
return bookie;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline app_tag get_tag_for_game( const tournament_object& game )
|
||||
{
|
||||
return game.options.game_options.visit( impl::game_type_visitor() );
|
||||
}
|
||||
|
||||
template<typename GAME>
|
||||
asset_id_type get_asset_for_game( const GAME& game );
|
||||
|
||||
template<>
|
||||
inline asset_id_type get_asset_for_game( const betting_market_group_object& game )
|
||||
{
|
||||
return game.asset_id;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline asset_id_type get_asset_for_game( const tournament_object& game )
|
||||
{
|
||||
return game.options.buy_in.asset_id;
|
||||
}
|
||||
|
||||
class affiliate_payout_helper {
|
||||
public:
|
||||
template<typename GAME>
|
||||
affiliate_payout_helper( database& db, const GAME& game )
|
||||
: _db(db), tag( get_tag_for_game( game ) ), payout_asset( get_asset_for_game( game ) ) {}
|
||||
|
||||
share_type payout( account_id_type player, share_type amount );
|
||||
share_type payout( const account_object& player, share_type amount );
|
||||
void commit();
|
||||
|
||||
private:
|
||||
database& _db;
|
||||
app_tag tag;
|
||||
asset_id_type payout_asset;
|
||||
std::map<account_id_type, share_type> accumulator;
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -36,7 +36,6 @@ namespace graphene { namespace chain {
|
|||
void_result do_evaluate( const asset_create_operation& o );
|
||||
object_id_type do_apply( const asset_create_operation& o );
|
||||
|
||||
// copied from bitshares. (https://github.com/bitshares/bitshares-core/issues/429)
|
||||
/** override the default behavior defined by generic_evalautor which is to
|
||||
* post the fee to fee_paying_account_stats.pending_fees
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -249,7 +249,8 @@ 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;
|
||||
|
||||
// used to sort active_lotteries index
|
||||
struct lottery_asset_comparer
|
||||
|
|
@ -266,6 +267,7 @@ namespace graphene { namespace chain {
|
|||
|
||||
struct by_symbol;
|
||||
struct by_type;
|
||||
struct by_issuer;
|
||||
struct active_lotteries;
|
||||
struct by_lottery;
|
||||
struct by_lottery_owner;
|
||||
|
|
@ -274,6 +276,7 @@ namespace graphene { namespace chain {
|
|||
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_non_unique< tag<active_lotteries>,
|
||||
identity< asset_object >,
|
||||
lottery_asset_comparer
|
||||
|
|
|
|||
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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
|
||||
724
libraries/chain/include/graphene/chain/betting_market_object.hpp
Normal file
724
libraries/chain/include/graphene/chain/betting_market_object.hpp
Normal file
|
|
@ -0,0 +1,724 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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;
|
||||
void cancel_all_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_unique< tag<by_event_id>, composite_key<betting_market_group_object,
|
||||
member<betting_market_group_object, event_id_type, &betting_market_group_object::event_id>,
|
||||
member<object, object_id_type, &object::id> > >,
|
||||
ordered_unique< tag<by_settling_time>, composite_key<betting_market_group_object,
|
||||
member<betting_market_group_object, fc::optional<fc::time_point_sec>, &betting_market_group_object::settling_time>,
|
||||
member<object, object_id_type, &object::id> > >
|
||||
> > 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_unique< tag<by_betting_market_group_id>, composite_key<betting_market_object,
|
||||
member<betting_market_object, betting_market_group_id_type, &betting_market_object::group_id>,
|
||||
member<object, object_id_type, &object::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_betting_market_id {};
|
||||
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_betting_market_id>, composite_key<bet_object,
|
||||
member<bet_object, betting_market_id_type, &bet_object::betting_market_id>,
|
||||
member<object, object_id_type, &object::id> > >,
|
||||
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) )
|
||||
|
|
@ -50,6 +50,8 @@ namespace graphene { namespace chain {
|
|||
public:
|
||||
typedef committee_member_update_global_parameters_operation operation_type;
|
||||
|
||||
const global_property_object* dgpo;
|
||||
|
||||
void_result do_evaluate( const committee_member_update_global_parameters_operation& o );
|
||||
void_result do_apply( const committee_member_update_global_parameters_operation& o );
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#define GRAPHENE_SYMBOL "PPY"
|
||||
#define GRAPHENE_ADDRESS_PREFIX "PPY"
|
||||
#define GRAPHENE_SYMBOL "TEST"
|
||||
#define GRAPHENE_ADDRESS_PREFIX "TEST"
|
||||
|
||||
#define GRAPHENE_MIN_ACCOUNT_NAME_LENGTH 1
|
||||
#define GRAPHENE_MAX_ACCOUNT_NAME_LENGTH 63
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
#define GRAPHENE_MIN_BLOCK_INTERVAL 1 /* seconds */
|
||||
#define GRAPHENE_MAX_BLOCK_INTERVAL 30 /* seconds */
|
||||
|
||||
#define GRAPHENE_DEFAULT_BLOCK_INTERVAL 5 /* seconds */
|
||||
#define GRAPHENE_DEFAULT_BLOCK_INTERVAL 3 /* seconds */
|
||||
#define GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE 2048
|
||||
#define GRAPHENE_DEFAULT_MAX_BLOCK_SIZE (GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE*GRAPHENE_DEFAULT_BLOCK_INTERVAL*200000)
|
||||
#define GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION (60*60*24) // seconds, aka: 1 day
|
||||
|
|
@ -151,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 "PPY2.1"
|
||||
|
||||
#define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT)
|
||||
|
||||
|
|
@ -172,13 +172,45 @@
|
|||
/// Represents the canonical account for specifying you will vote directly (as opposed to a proxy)
|
||||
#define GRAPHENE_PROXY_TO_SELF_ACCOUNT (graphene::chain::account_id_type(5))
|
||||
///
|
||||
#define TOURNAMENT_RAKE_FEE_ACCOUNT_ID (graphene::chain::account_id_type(6))
|
||||
#define GRAPHENE_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
|
||||
|
|
|
|||
|
|
@ -137,6 +137,8 @@ namespace graphene { namespace chain {
|
|||
const flat_map<uint32_t,block_id_type> get_checkpoints()const { return _checkpoints; }
|
||||
bool before_last_checkpoint()const;
|
||||
|
||||
void check_tansaction_for_duplicated_operations(const signed_transaction& trx);
|
||||
|
||||
bool push_block( const signed_block& b, uint32_t skip = skip_nothing );
|
||||
processed_transaction push_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing );
|
||||
bool _push_block( const signed_block& b );
|
||||
|
|
@ -171,8 +173,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;
|
||||
|
||||
/**
|
||||
|
|
@ -195,12 +207,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 ////////////////////
|
||||
|
||||
|
|
@ -386,6 +404,29 @@ 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);
|
||||
void remove_completed_events();
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
|
|
@ -452,12 +493,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
|
||||
|
|
@ -514,6 +557,7 @@ namespace graphene { namespace chain {
|
|||
|
||||
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) 2018 Peerplays Blockchain Standards Association, 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,69 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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_object;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
class event_group_delete_evaluator : public evaluator<event_group_delete_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef event_group_delete_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const event_group_delete_operation& o );
|
||||
void_result do_apply( const event_group_delete_operation& o );
|
||||
|
||||
private:
|
||||
const event_group_object* _event_group = nullptr;
|
||||
};
|
||||
} } // graphene::chain
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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 <boost/multi_index/composite_key.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;
|
||||
|
||||
void cancel_events(database& db) const;
|
||||
};
|
||||
|
||||
typedef multi_index_container<
|
||||
event_group_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag<by_sport_id>, composite_key<event_group_object,
|
||||
member< event_group_object, sport_id_type, &event_group_object::sport_id >,
|
||||
member<object, object_id_type, &object::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) )
|
||||
161
libraries/chain/include/graphene/chain/event_object.hpp
Normal file
161
libraries/chain/include/graphene/chain/event_object.hpp
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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>
|
||||
|
||||
#include <boost/multi_index/composite_key.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;
|
||||
struct by_event_status;
|
||||
typedef multi_index_container<
|
||||
event_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag<by_event_group_id>, composite_key<event_object,
|
||||
member< event_object, event_group_id_type, &event_object::event_group_id >,
|
||||
member<object, object_id_type, &object::id> > >,
|
||||
ordered_unique< tag<by_event_status>, composite_key<event_object,
|
||||
const_mem_fun< event_object, event_status, &event_object::get_status >,
|
||||
member<object, object_id_type, &object::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))
|
||||
|
||||
|
|
@ -1,3 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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/match_object.hpp>
|
||||
#include <graphene/chain/rock_paper_scissors.hpp>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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) )
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ namespace graphene { namespace chain {
|
|||
struct by_id;
|
||||
struct by_seq;
|
||||
struct by_op;
|
||||
struct by_opid;
|
||||
typedef multi_index_container<
|
||||
account_transaction_history_object,
|
||||
indexed_by<
|
||||
|
|
@ -117,6 +118,9 @@ typedef multi_index_container<
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
|
|
|||
|
|
@ -60,6 +60,21 @@ namespace graphene { namespace chain {
|
|||
void validate()const;
|
||||
};
|
||||
|
||||
enum app_tag {
|
||||
bookie = 0,
|
||||
rps = 1
|
||||
};
|
||||
struct affiliate_reward_distribution
|
||||
{
|
||||
fc::flat_map<account_id_type,uint16_t> _dist;
|
||||
void validate()const;
|
||||
};
|
||||
struct affiliate_reward_distributions
|
||||
{
|
||||
fc::flat_map<app_tag,affiliate_reward_distribution> _dists;
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup operations
|
||||
*/
|
||||
|
|
@ -71,6 +86,7 @@ namespace graphene { namespace chain {
|
|||
optional< special_authority > owner_special_authority;
|
||||
optional< special_authority > active_special_authority;
|
||||
optional< buyback_account_options > buyback_options;
|
||||
optional< affiliate_reward_distributions > affiliate_distributions;
|
||||
};
|
||||
|
||||
struct fee_parameters_type
|
||||
|
|
@ -264,10 +280,14 @@ 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))
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::app_tag, (bookie)(rps) )
|
||||
FC_REFLECT( graphene::chain::affiliate_reward_distribution, (_dist) );
|
||||
FC_REFLECT( graphene::chain::affiliate_reward_distributions, (_dists) );
|
||||
|
||||
FC_REFLECT(graphene::chain::account_create_operation::ext, (null_ext)(owner_special_authority)(active_special_authority)(buyback_options) )
|
||||
FC_REFLECT( graphene::chain::account_create_operation,
|
||||
(fee)(registrar)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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/account.hpp>
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/protocol/memo.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
/**
|
||||
* Virtual op generated when an affiliate receives payout.
|
||||
*/
|
||||
|
||||
struct affiliate_payout_operation : public base_operation
|
||||
{
|
||||
affiliate_payout_operation(){}
|
||||
affiliate_payout_operation( account_id_type a, app_tag t, const asset& amount )
|
||||
: affiliate(a), tag(t), payout(amount) {}
|
||||
|
||||
struct fee_parameters_type { };
|
||||
asset fee;
|
||||
|
||||
// Account of the receiving affiliate
|
||||
account_id_type affiliate;
|
||||
// App-tag for which the payout was generated
|
||||
app_tag tag;
|
||||
// Payout amount
|
||||
asset payout;
|
||||
|
||||
account_id_type fee_payer()const { return affiliate; }
|
||||
void validate()const {
|
||||
FC_ASSERT( false, "Virtual operation" );
|
||||
}
|
||||
|
||||
share_type calculate_fee(const fee_parameters_type& params)const
|
||||
{ return 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Virtual op generated when a player generates an affiliate payout
|
||||
*/
|
||||
struct affiliate_referral_payout_operation : public base_operation
|
||||
{
|
||||
affiliate_referral_payout_operation(){}
|
||||
affiliate_referral_payout_operation( account_id_type p, const asset& amount )
|
||||
: player(p), payout(amount) {}
|
||||
|
||||
struct fee_parameters_type { };
|
||||
asset fee;
|
||||
|
||||
// Account of the winning player
|
||||
account_id_type player;
|
||||
// Payout amount
|
||||
asset payout;
|
||||
|
||||
account_id_type fee_payer()const { return player; }
|
||||
void validate()const {
|
||||
FC_ASSERT( false, "virtual operation" );
|
||||
}
|
||||
|
||||
share_type calculate_fee(const fee_parameters_type& params)const
|
||||
{ return 0; }
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::affiliate_payout_operation::fee_parameters_type, )
|
||||
FC_REFLECT( graphene::chain::affiliate_referral_payout_operation::fee_parameters_type, )
|
||||
|
||||
FC_REFLECT( graphene::chain::affiliate_payout_operation, (fee)(affiliate)(tag)(payout) )
|
||||
FC_REFLECT( graphene::chain::affiliate_referral_payout_operation, (fee)(player)(payout) )
|
||||
|
|
@ -319,7 +319,7 @@ namespace graphene { namespace chain {
|
|||
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) :
|
||||
const vector<asset>& amounts) :
|
||||
dividend_asset_id(dividend_asset_id),
|
||||
account_id(account_id),
|
||||
amounts(amounts)
|
||||
|
|
@ -351,7 +351,7 @@ namespace graphene { namespace chain {
|
|||
account_id_type account_id;
|
||||
|
||||
/// The amounts received
|
||||
flat_set<asset> amounts;
|
||||
vector<asset> amounts;
|
||||
|
||||
extensions_type 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) 2018 Peerplays Blockchain Standards Association, 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) )
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/protocol/ext.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <fc/smart_ref_fwd.hpp>
|
||||
|
||||
|
|
@ -35,12 +36,23 @@ namespace fc {
|
|||
*/
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
struct bet_parameter_extension
|
||||
{
|
||||
optional< bet_multiplier_type > min_bet_multiplier;
|
||||
optional< bet_multiplier_type > max_bet_multiplier;
|
||||
optional< uint16_t > betting_rake_fee_percentage;
|
||||
optional< flat_map<bet_multiplier_type, bet_multiplier_type> > permitted_betting_odds_increments;
|
||||
optional< uint16_t > live_betting_delay_time;
|
||||
};
|
||||
|
||||
struct sweeps_parameters_extension {
|
||||
uint16_t sweeps_distribution_percentage = SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE;
|
||||
asset_id_type sweeps_distribution_asset = SWEEPS_DEFAULT_DISTRIBUTION_ASSET;
|
||||
account_id_type sweeps_vesting_accumulator_account = SWEEPS_ACCUMULATOR_ACCOUNT;
|
||||
};
|
||||
typedef static_variant<void_t,sweeps_parameters_extension> parameter_extension;
|
||||
|
||||
typedef static_variant<void_t,sweeps_parameters_extension,bet_parameter_extension> parameter_extension;
|
||||
|
||||
struct chain_parameters
|
||||
{
|
||||
/** using a smart ref breaks the circular dependency created between operations and the fee schedule */
|
||||
|
|
@ -73,7 +85,6 @@ 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;
|
||||
//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
|
||||
|
|
@ -94,10 +105,34 @@ namespace graphene { namespace chain {
|
|||
|
||||
/** defined in fee_schedule.cpp */
|
||||
void validate()const;
|
||||
inline bet_multiplier_type min_bet_multiplier()const {
|
||||
return extensions.value.min_bet_multiplier.valid() ? *extensions.value.min_bet_multiplier : GRAPHENE_DEFAULT_MIN_BET_MULTIPLIER;
|
||||
}
|
||||
inline bet_multiplier_type max_bet_multiplier()const {
|
||||
return extensions.value.max_bet_multiplier.valid() ? *extensions.value.max_bet_multiplier : GRAPHENE_DEFAULT_MAX_BET_MULTIPLIER;
|
||||
}
|
||||
inline uint16_t betting_rake_fee_percentage()const {
|
||||
return extensions.value.betting_rake_fee_percentage.valid() ? *extensions.value.betting_rake_fee_percentage : GRAPHENE_DEFAULT_RAKE_FEE_PERCENTAGE;
|
||||
}
|
||||
inline const flat_map<bet_multiplier_type, bet_multiplier_type>& permitted_betting_odds_increments()const {
|
||||
static const flat_map<bet_multiplier_type, bet_multiplier_type> _default = GRAPHENE_DEFAULT_PERMITTED_BETTING_ODDS_INCREMENTS;
|
||||
return extensions.value.permitted_betting_odds_increments.valid() ? *extensions.value.permitted_betting_odds_increments : _default;
|
||||
}
|
||||
inline uint16_t live_betting_delay_time()const {
|
||||
return extensions.value.live_betting_delay_time.valid() ? *extensions.value.live_betting_delay_time : GRAPHENE_DEFAULT_LIVE_BETTING_DELAY_TIME;
|
||||
}
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::parameter_extension,
|
||||
(min_bet_multiplier)
|
||||
(max_bet_multiplier)
|
||||
(betting_rake_fee_percentage)
|
||||
(permitted_betting_odds_increments)
|
||||
(live_betting_delay_time)
|
||||
)
|
||||
|
||||
FC_REFLECT( graphene::chain::sweeps_parameters_extension,
|
||||
(sweeps_distribution_percentage)
|
||||
(sweeps_distribution_asset) )
|
||||
|
|
|
|||
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) 2018 Peerplays Blockchain Standards Association, 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,99 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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;
|
||||
};
|
||||
|
||||
struct event_group_delete_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
event_group_id_type event_group_id;
|
||||
|
||||
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) )
|
||||
|
||||
FC_REFLECT( graphene::chain::event_group_delete_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::event_group_delete_operation,
|
||||
(fee)(event_group_id)(extensions) )
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/protocol/account.hpp>
|
||||
#include <graphene/chain/protocol/affiliate.hpp>
|
||||
#include <graphene/chain/protocol/assert.hpp>
|
||||
#include <graphene/chain/protocol/asset_ops.hpp>
|
||||
#include <graphene/chain/protocol/lottery_ops.hpp>
|
||||
|
|
@ -39,6 +40,10 @@
|
|||
#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 {
|
||||
|
|
@ -101,6 +106,31 @@ namespace graphene { namespace chain {
|
|||
asset_dividend_distribution_operation, // VIRTUAL
|
||||
tournament_payout_operation, // VIRTUAL
|
||||
tournament_leave_operation,
|
||||
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
|
||||
betting_market_group_update_operation,
|
||||
betting_market_update_operation,
|
||||
event_update_status_operation,
|
||||
sport_delete_operation,
|
||||
event_group_delete_operation,
|
||||
affiliate_payout_operation, // VIRTUAL
|
||||
affiliate_referral_payout_operation // VIRTUAL
|
||||
ticket_purchase_operation,
|
||||
lottery_reward_operation,
|
||||
lottery_end_operation,
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ namespace graphene { namespace chain {
|
|||
|
||||
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_TYPENAME( graphene::chain::rock_paper_scissors_gesture)
|
||||
FC_REFLECT_ENUM( graphene::chain::rock_paper_scissors_gesture,
|
||||
(rock)
|
||||
(paper)
|
||||
|
|
|
|||
87
libraries/chain/include/graphene/chain/protocol/sport.hpp
Normal file
87
libraries/chain/include/graphene/chain/protocol/sport.hpp
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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;
|
||||
};
|
||||
|
||||
struct sport_delete_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
sport_id_type sport_id;
|
||||
|
||||
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) )
|
||||
|
||||
FC_REFLECT( graphene::chain::sport_delete_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::sport_delete_operation,
|
||||
(fee)(sport_id)(extensions) )
|
||||
|
|
@ -138,6 +138,13 @@ namespace graphene { namespace chain {
|
|||
tournament_details_object_type,
|
||||
match_object_type,
|
||||
game_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,
|
||||
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||
};
|
||||
|
||||
|
|
@ -163,6 +170,8 @@ namespace graphene { namespace chain {
|
|||
impl_asset_dividend_data_type,
|
||||
impl_pending_dividend_payout_balance_for_holder_object_type,
|
||||
impl_distributed_dividend_balance_data_type,
|
||||
impl_betting_market_position_object_type,
|
||||
impl_global_betting_statistics_object_type
|
||||
impl_lottery_balance_object_type,
|
||||
impl_sweeps_vesting_balance_object_type
|
||||
};
|
||||
|
|
@ -188,6 +197,13 @@ namespace graphene { namespace chain {
|
|||
class tournament_details_object;
|
||||
class match_object;
|
||||
class game_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;
|
||||
|
||||
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;
|
||||
|
|
@ -207,6 +223,13 @@ namespace graphene { namespace chain {
|
|||
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;
|
||||
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;
|
||||
|
||||
// implementation types
|
||||
class global_property_object;
|
||||
|
|
@ -224,9 +247,10 @@ namespace graphene { namespace chain {
|
|||
class special_authority_object;
|
||||
class buyback_object;
|
||||
class fba_accumulator_object;
|
||||
class tournament_details_object;
|
||||
class asset_dividend_data_object;
|
||||
class pending_dividend_payout_balance_for_holder_object;
|
||||
class betting_market_position_object;
|
||||
class global_betting_statistics_object;
|
||||
class lottery_balance_object;
|
||||
class sweeps_vesting_balance_object;
|
||||
|
||||
|
|
@ -253,6 +277,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 object_id< implementation_ids, impl_lottery_balance_object_type, lottery_balance_object > lottery_balance_id_type;
|
||||
typedef object_id< implementation_ids, impl_sweeps_vesting_balance_object_type, sweeps_vesting_balance_object> sweeps_vesting_balance_id_type;
|
||||
|
||||
|
|
@ -333,6 +359,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
|
||||
|
|
@ -373,6 +403,13 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
|
|||
(tournament_details_object_type)
|
||||
(match_object_type)
|
||||
(game_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)
|
||||
(OBJECT_TYPE_COUNT)
|
||||
)
|
||||
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
||||
|
|
@ -396,6 +433,8 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
|||
(impl_asset_dividend_data_type)
|
||||
(impl_pending_dividend_payout_balance_for_holder_object_type)
|
||||
(impl_distributed_dividend_balance_data_type)
|
||||
(impl_betting_market_position_object_type)
|
||||
(impl_global_betting_statistics_object_type)
|
||||
(impl_lottery_balance_object_type)
|
||||
(impl_sweeps_vesting_balance_object_type)
|
||||
)
|
||||
|
|
@ -416,6 +455,13 @@ 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 )
|
||||
|
|
@ -430,6 +476,8 @@ 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) )
|
||||
|
|
|
|||
64
libraries/chain/include/graphene/chain/sport_evaluator.hpp
Normal file
64
libraries/chain/include/graphene/chain/sport_evaluator.hpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, 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_object;
|
||||
|
||||
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 );
|
||||
};
|
||||
|
||||
class sport_delete_evaluator : public evaluator<sport_delete_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sport_delete_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const sport_delete_operation& o );
|
||||
void_result do_apply( const sport_delete_operation& o );
|
||||
|
||||
private:
|
||||
const sport_object* _sport = nullptr;
|
||||
};
|
||||
|
||||
} } // 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) 2018 Peerplays Blockchain Standards Association, 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) )
|
||||
|
|
@ -133,14 +133,16 @@ namespace graphene { namespace chain {
|
|||
tournament_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag<by_registration_deadline>,
|
||||
ordered_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>,
|
||||
const_mem_fun<tournament_object, time_point_sec, &tournament_object::get_registration_deadline>,
|
||||
member< object, object_id_type, &object::id > > >,
|
||||
ordered_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> > >
|
||||
member<tournament_object, optional<time_point_sec>, &tournament_object::start_time>,
|
||||
member< object, object_id_type, &object::id > > >
|
||||
>
|
||||
> tournament_object_multi_index_type;
|
||||
typedef generic_index<tournament_object, tournament_object_multi_index_type> tournament_index;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include <graphene/chain/protocol/market.hpp>
|
||||
|
||||
#include <fc/uint128.hpp>
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
void_result limit_order_create_evaluator::do_evaluate(const limit_order_create_operation& op)
|
||||
|
|
|
|||
|
|
@ -21,19 +21,134 @@
|
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/proposal_evaluator.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/protocol/account.hpp>
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct proposal_operation_hardfork_visitor
|
||||
{
|
||||
typedef void result_type;
|
||||
const fc::time_point_sec block_time;
|
||||
|
||||
proposal_operation_hardfork_visitor( const fc::time_point_sec bt ) : block_time(bt) {}
|
||||
|
||||
template<typename T>
|
||||
void operator()(const T &v) const {}
|
||||
|
||||
void operator()(const committee_member_update_global_parameters_operation &op) const {
|
||||
if( block_time < HARDFORK_1000_TIME ) // TODO: remove after hf
|
||||
FC_ASSERT( !op.new_parameters.extensions.value.min_bet_multiplier.valid()
|
||||
&& !op.new_parameters.extensions.value.max_bet_multiplier.valid()
|
||||
&& !op.new_parameters.extensions.value.betting_rake_fee_percentage.valid()
|
||||
&& !op.new_parameters.extensions.value.permitted_betting_odds_increments.valid()
|
||||
&& !op.new_parameters.extensions.value.live_betting_delay_time.valid(),
|
||||
"Parameter extensions are not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const graphene::chain::tournament_payout_operation &o) const {
|
||||
// TODO: move check into tournament_payout_operation::validate after HARDFORK_999_TIME
|
||||
FC_ASSERT( block_time < HARDFORK_999_TIME, "Not allowed!" );
|
||||
}
|
||||
|
||||
void operator()(const graphene::chain::asset_settle_cancel_operation &o) const {
|
||||
// TODO: move check into asset_settle_cancel_operation::validate after HARDFORK_999_TIME
|
||||
FC_ASSERT( block_time < HARDFORK_999_TIME, "Not allowed!" );
|
||||
}
|
||||
|
||||
void operator()(const graphene::chain::account_create_operation &aco) const {
|
||||
// TODO: remove after HARDFORK_999_TIME
|
||||
if (block_time < HARDFORK_999_TIME)
|
||||
FC_ASSERT( !aco.extensions.value.affiliate_distributions.valid(), "Affiliate reward distributions not allowed yet" );
|
||||
}
|
||||
|
||||
void operator()(const sport_update_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "sport_update_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const event_group_create_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_group_create_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const event_group_update_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_group_update_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const event_create_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_create_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const event_update_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_update_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const betting_market_rules_create_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_rules_create_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const betting_market_rules_update_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_rules_update_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const betting_market_group_create_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_group_create_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const betting_market_create_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_create_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const bet_place_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "bet_place_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const betting_market_group_resolve_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_group_resolve_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const betting_market_group_cancel_unmatched_bets_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_group_cancel_unmatched_bets_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const bet_cancel_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_group_resolve_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const betting_market_group_update_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_group_update_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const betting_market_update_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_update_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const event_update_status_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_update_status_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
// loop and self visit in proposals
|
||||
void operator()(const proposal_create_operation &v) const {
|
||||
for (const op_wrapper &op : v.proposed_ops)
|
||||
op.op.visit(*this);
|
||||
}
|
||||
};
|
||||
|
||||
void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o)
|
||||
{ try {
|
||||
const database& d = db();
|
||||
|
||||
proposal_operation_hardfork_visitor vtor( d.head_block_time() );
|
||||
vtor( o );
|
||||
|
||||
const auto& global_parameters = d.get_global_properties().parameters;
|
||||
|
||||
FC_ASSERT( o.expiration_time > d.head_block_time(), "Proposal has already expired on creation." );
|
||||
|
|
@ -85,6 +200,7 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati
|
|||
const proposal_object& proposal = d.create<proposal_object>([&](proposal_object& proposal) {
|
||||
_proposed_trx.expiration = o.expiration_time;
|
||||
proposal.proposed_transaction = _proposed_trx;
|
||||
proposal.proposer = o.fee_paying_account;
|
||||
proposal.expiration_time = o.expiration_time;
|
||||
if( o.review_period_seconds )
|
||||
proposal.review_period_time = o.expiration_time - *o.review_period_seconds;
|
||||
|
|
|
|||
|
|
@ -182,6 +182,27 @@ void account_options::validate() const
|
|||
"May not specify fewer witnesses or committee members than the number voted for.");
|
||||
}
|
||||
|
||||
void affiliate_reward_distribution::validate() const
|
||||
{
|
||||
// sum of weights must equal 100%
|
||||
uint32_t sum = 0;
|
||||
for( const auto& share : _dist )
|
||||
{
|
||||
FC_ASSERT( share.second > 0, "Must leave out affilates who receive 0%!" );
|
||||
FC_ASSERT( share.second <= GRAPHENE_100_PERCENT, "Can't pay out more than 100% per affiliate!" );
|
||||
sum += share.second;
|
||||
FC_ASSERT( sum <= GRAPHENE_100_PERCENT, "Can't pay out more than 100% total!" );
|
||||
}
|
||||
FC_ASSERT( sum == GRAPHENE_100_PERCENT, "Total affiliate distributions must cover 100%!" );
|
||||
}
|
||||
|
||||
void affiliate_reward_distributions::validate() const
|
||||
{
|
||||
FC_ASSERT( !_dists.empty(), "Empty affiliate reward distributions not allowed!" );
|
||||
for( const auto& dist: _dists )
|
||||
dist.second.validate();
|
||||
}
|
||||
|
||||
share_type account_create_operation::calculate_fee( const fee_parameters_type& k )const
|
||||
{
|
||||
auto core_fee_required = k.basic_fee;
|
||||
|
|
@ -226,6 +247,8 @@ void account_create_operation::validate()const
|
|||
FC_ASSERT( m != extensions.value.buyback_options->asset_to_buy );
|
||||
}
|
||||
}
|
||||
if( extensions.value.affiliate_distributions.valid() )
|
||||
extensions.value.affiliate_distributions->validate();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
81
libraries/chain/protocol/betting_market.cpp
Normal file
81
libraries/chain/protocol/betting_market.cpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/protocol/betting_market.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void betting_market_rules_create_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void betting_market_rules_update_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void betting_market_group_create_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void betting_market_group_update_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void betting_market_create_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void betting_market_update_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void betting_market_group_resolve_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void betting_market_group_cancel_unmatched_bets_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void bet_place_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void bet_cancel_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
|
|
@ -21,10 +21,15 @@
|
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/p2p/peer_connection.hpp>
|
||||
#include <graphene/chain/protocol/competitor.hpp>
|
||||
|
||||
namespace graphene { namespace p2p {
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
} } //graphene::p2p
|
||||
void competitor_create_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
44
libraries/chain/protocol/event.cpp
Normal file
44
libraries/chain/protocol/event.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/protocol/event.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void event_create_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void event_update_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void event_update_status_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
44
libraries/chain/protocol/event_group.cpp
Normal file
44
libraries/chain/protocol/event_group.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/protocol/event_group.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void event_group_create_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void event_group_update_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void event_group_delete_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
|
|
@ -189,11 +189,22 @@ namespace graphene { namespace chain {
|
|||
FC_ASSERT( maximum_proposal_lifetime - committee_proposal_review_period > block_interval,
|
||||
"Committee proposal review period must be less than the maximum proposal lifetime" );
|
||||
|
||||
FC_ASSERT( rake_fee_percentage >= TOURNAMENT_MINIMAL_RAKE_FEE_PERCENTAGE,
|
||||
"Rake fee percentage must not be less than ${min}", ("min",TOURNAMENT_MINIMAL_RAKE_FEE_PERCENTAGE));
|
||||
FC_ASSERT( rake_fee_percentage <= TOURNAMENT_MAXIMAL_RAKE_FEE_PERCENTAGE,
|
||||
"Rake fee percentage must not be greater than ${max}", ("max", TOURNAMENT_MAXIMAL_RAKE_FEE_PERCENTAGE));
|
||||
if( extensions.value.min_bet_multiplier.valid() )
|
||||
FC_ASSERT( *extensions.value.min_bet_multiplier >= GRAPHENE_BETTING_MIN_MULTIPLIER &&
|
||||
*extensions.value.min_bet_multiplier <= GRAPHENE_BETTING_MAX_MULTIPLIER );
|
||||
if( extensions.value.max_bet_multiplier.valid() )
|
||||
FC_ASSERT( *extensions.value.max_bet_multiplier >= GRAPHENE_BETTING_MIN_MULTIPLIER &&
|
||||
*extensions.value.max_bet_multiplier <= GRAPHENE_BETTING_MAX_MULTIPLIER );
|
||||
if( extensions.value.min_bet_multiplier.valid() && extensions.value.max_bet_multiplier.valid() )
|
||||
FC_ASSERT( *extensions.value.min_bet_multiplier < *extensions.value.max_bet_multiplier );
|
||||
|
||||
if( extensions.value.betting_rake_fee_percentage.valid() )
|
||||
{
|
||||
FC_ASSERT( *extensions.value.betting_rake_fee_percentage >= TOURNAMENT_MINIMAL_RAKE_FEE_PERCENTAGE,
|
||||
"Rake fee percentage must not be less than ${min}", ("min",TOURNAMENT_MINIMAL_RAKE_FEE_PERCENTAGE));
|
||||
FC_ASSERT( *extensions.value.betting_rake_fee_percentage <= TOURNAMENT_MAXIMAL_RAKE_FEE_PERCENTAGE,
|
||||
"Rake fee percentage must not be greater than ${max}", ("max", TOURNAMENT_MAXIMAL_RAKE_FEE_PERCENTAGE));
|
||||
}
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
|
|||
44
libraries/chain/protocol/sport.cpp
Normal file
44
libraries/chain/protocol/sport.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/protocol/sport.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void sport_create_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void sport_update_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
void sport_delete_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ struct sign_state
|
|||
if( approved_by.find(a.first) == approved_by.end() )
|
||||
{
|
||||
if( depth == max_recursion )
|
||||
return false;
|
||||
continue;
|
||||
if( check_authority( get_active( a.first ), depth+1 ) )
|
||||
{
|
||||
approved_by.insert( a.first );
|
||||
|
|
|
|||
111
libraries/chain/sport_evaluator.cpp
Normal file
111
libraries/chain/sport_evaluator.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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/sport_evaluator.hpp>
|
||||
#include <graphene/chain/sport_object.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 sport_create_evaluator::do_evaluate(const sport_create_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_1000_TIME);
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type sport_create_evaluator::do_apply(const sport_create_operation& op)
|
||||
{ try {
|
||||
const sport_object& new_sport =
|
||||
db().create<sport_object>( [&]( sport_object& sport_obj ) {
|
||||
sport_obj.name = op.name;
|
||||
});
|
||||
return new_sport.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
||||
void_result sport_update_evaluator::do_evaluate(const sport_update_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_1000_TIME);
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
FC_ASSERT(op.new_name.valid());
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result sport_update_evaluator::do_apply(const sport_update_operation& op)
|
||||
{ try {
|
||||
database& _db = db();
|
||||
_db.modify(
|
||||
_db.get(op.sport_id),
|
||||
[&]( sport_object& spo )
|
||||
{
|
||||
if( op.new_name.valid() )
|
||||
spo.name = *op.new_name;
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
||||
void_result sport_delete_evaluator::do_evaluate( const sport_delete_operation& op )
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_1001_TIME);
|
||||
FC_ASSERT(trx_state->_is_proposed_trx);
|
||||
|
||||
//check for sport existence
|
||||
_sport = &op.sport_id(db());
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result sport_delete_evaluator::do_apply( const sport_delete_operation& op )
|
||||
{ try {
|
||||
database& _db = db();
|
||||
|
||||
std::vector<const event_group_object*> event_groups_to_remove;
|
||||
|
||||
const auto& event_group_by_sport_id = _db.get_index_type<event_group_object_index>().indices().get<by_sport_id>();
|
||||
auto event_group_it = event_group_by_sport_id.lower_bound(op.sport_id);
|
||||
auto event_group_end_it = event_group_by_sport_id.upper_bound(op.sport_id);
|
||||
for (; event_group_it != event_group_end_it; ++event_group_it)
|
||||
{
|
||||
event_group_it->cancel_events(_db);
|
||||
event_groups_to_remove.push_back(&*event_group_it);
|
||||
}
|
||||
|
||||
for (auto event_group: event_groups_to_remove)
|
||||
{
|
||||
_db.remove(*event_group);
|
||||
}
|
||||
|
||||
_db.remove(*_sport);
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/affiliate_payout.hpp>
|
||||
|
||||
#include <boost/msm/back/state_machine.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
|
|
@ -304,23 +305,19 @@ namespace graphene { namespace chain {
|
|||
const tournament_details_object& details = tournament_obj.tournament_details_id(event.db);
|
||||
share_type total_prize = 0;
|
||||
for (const auto& payer_pair : details.payers)
|
||||
{
|
||||
total_prize += payer_pair.second;
|
||||
}
|
||||
assert(total_prize == tournament_obj.prize_pool);
|
||||
#endif
|
||||
assert(event.match.match_winners.size() == 1);
|
||||
const account_id_type& winner = *event.match.match_winners.begin();
|
||||
uint16_t rake_fee_percentage = event.db.get_global_properties().parameters.rake_fee_percentage;
|
||||
share_type rake_amount = 0;
|
||||
|
||||
const asset_object & asset_obj = tournament_obj.options.buy_in.asset_id(event.db);
|
||||
const asset_object & asset_obj = asset_id_type(0)(event.db);
|
||||
optional<asset_dividend_data_id_type> dividend_id = asset_obj.dividend_data_id;
|
||||
|
||||
if (dividend_id.valid())
|
||||
{
|
||||
share_type rake_amount = 0;
|
||||
if (dividend_id)
|
||||
rake_amount = (fc::uint128_t(tournament_obj.prize_pool.value) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100).to_uint64();
|
||||
}
|
||||
asset won_prize(tournament_obj.prize_pool - rake_amount, tournament_obj.options.buy_in.asset_id);
|
||||
tournament_payout_operation op;
|
||||
|
||||
|
|
@ -337,7 +334,15 @@ namespace graphene { namespace chain {
|
|||
event.db.push_applied_operation(op);
|
||||
}
|
||||
|
||||
if (dividend_id.valid() && rake_amount.value)
|
||||
if (rake_amount.value)
|
||||
{
|
||||
affiliate_payout_helper payout_helper( event.db, tournament_obj );
|
||||
rake_amount -= payout_helper.payout( winner, rake_amount );
|
||||
payout_helper.commit();
|
||||
FC_ASSERT( rake_amount.value >= 0 );
|
||||
}
|
||||
|
||||
if (rake_amount.value)
|
||||
{
|
||||
// Adjusting balance of dividend_distribution_account
|
||||
const asset_dividend_data_id_type& asset_dividend_data_id_= *dividend_id;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ asset linear_vesting_policy::get_allowed_withdraw( const vesting_policy_context&
|
|||
}
|
||||
}
|
||||
|
||||
return asset( allowed_withdraw, ctx.amount.asset_id );
|
||||
return asset( allowed_withdraw, ctx.balance.asset_id );
|
||||
}
|
||||
|
||||
void linear_vesting_policy::on_deposit(const vesting_policy_context& ctx)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue