Merged beatrice into 5050

This commit is contained in:
Prabhjot Singh 2019-07-30 11:43:31 -04:00
parent dfffabd004
commit aa3128fe89
192 changed files with 244632 additions and 218620 deletions

2
.gitignore vendored
View file

@ -41,3 +41,5 @@ object_database/*
*.pyc
*.pyo
.vscode
.DS_Store

2
.gitmodules vendored
View file

@ -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

View file

@ -32,13 +32,16 @@ if (USE_PCH)
include (cotire)
endif(USE_PCH)
list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libraries/fc/CMakeModules" )
IF( NOT WIN32 )
list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libraries/fc/CMakeModules" )
ENDIF( NOT WIN32 )
list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libraries/fc/GitVersionGen" )
include( GetGitRevisionDescription )
get_git_head_revision( GIT_REFSPEC GIT_SHA2 )
SET(BOOST_COMPONENTS)
LIST(APPEND BOOST_COMPONENTS thread
iostreams
date_time
system
filesystem
@ -129,6 +132,10 @@ else( WIN32 ) # Apple AND Linux
if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp" )
elseif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
if( CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.0.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0.0 )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization" )
endif()
endif()
if( "${CMAKE_GENERATOR}" STREQUAL "Ninja" )
@ -143,8 +150,6 @@ else( WIN32 ) # Apple AND Linux
endif( WIN32 )
find_package( BerkeleyDB )
set(ENABLE_COVERAGE_TESTING FALSE CACHE BOOL "Build BitShares for code coverage analysis")
if(ENABLE_COVERAGE_TESTING)

View file

@ -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
View file

@ -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.
*/

View file

@ -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.

View file

@ -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
View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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
View 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

File diff suppressed because it is too large Load diff

216158
genesis/alice-genesis.json Normal file

File diff suppressed because it is too large Load diff

181
genesis/genesis.py Normal file
View 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))

View file

@ -1 +1 @@
2.0.160208
0.0.1

View file

@ -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" )

View file

@ -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

View file

@ -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,80 +378,68 @@ 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
{
_chain_db->open(_data_dir / "blockchain", initial_state);
_chain_db->open( _data_dir / "blockchain", initial_state );
}
catch( const fc::exception& e )
{
ilog( "caught exception ${e} in open()", ("e", e.to_detail_string()) );
need_reindex = true;
reindex_reason = "exception in open()";
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());
_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")

View file

@ -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,24 +324,24 @@ fc::variants database_api_impl::get_objects(const vector<object_id_type>& ids)co
// //
//////////////////////////////////////////////////////////////////////
void database_api::set_subscribe_callback( std::function<void(const variant&)> cb, bool clear_filter )
void database_api::set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create )
{
my->set_subscribe_callback( cb, clear_filter );
my->set_subscribe_callback( cb, notify_remove_create );
}
void database_api_impl::set_subscribe_callback( std::function<void(const variant&)> cb, bool clear_filter )
void database_api_impl::set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create )
{
edump((clear_filter));
//edump((clear_filter));
_subscribe_callback = cb;
if( clear_filter || !cb )
{
_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,45 +2136,22 @@ 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)
{
/// 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 );
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() )
if( queue.size() )
{
auto capture_this = shared_from_this();
fc::async([capture_this,this,broadcast_queue](){
for( const auto& item : broadcast_queue )
fc::async([capture_this, this, queue](){
for( const auto& item : queue )
{
auto sub = _market_subscriptions.find(item.first);
if( sub != _market_subscriptions.end() )
@ -2018,63 +2159,90 @@ void database_api_impl::on_objects_removed( const vector<const object*>& objs )
}
});
}
}
}
void database_api_impl::on_objects_changed(const vector<object_id_type>& ids)
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)
{
if( _subscribe_callback )
{
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 )
if( force_notify || is_subscribed_to_item(id) || is_impacted_account(impacted_accounts) )
{
obj = _db.find_object( id );
if( full_object )
{
auto obj = find_object(id);
if( obj )
{
updates.emplace_back( obj->to_variant() );
}
}
else
{
updates.emplace_back(id); // send just the id to indicate removal
updates.emplace_back( id );
}
}
}
broadcast_updates(updates);
}
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

View file

@ -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 );

View file

@ -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)
)

View file

@ -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)

View file

@ -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)
)

View file

@ -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" )

View file

@ -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 )

View 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

View 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

View 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

View 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

View file

@ -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;
});

View file

@ -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 )

View file

@ -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
View 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;
}
} }

View file

@ -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 );

View file

@ -50,7 +50,7 @@ const chain_property_object& database::get_chain_properties()const
return get( chain_property_id_type() );
}
const dynamic_global_property_object&database::get_dynamic_global_properties() const
const dynamic_global_property_object& database::get_dynamic_global_properties() const
{
return get( dynamic_global_property_id_type() );
}

View file

@ -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 =
@ -396,7 +463,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
a.options.core_exchange_rate.quote.asset_id = asset_id_type(0);
a.dynamic_asset_data_id = dyn_asset.id;
a.dividend_data_id = div_asset.id;
});
});
assert( asset_id_type(core_asset.id) == asset().asset_id );
assert( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) );
@ -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" );

View file

@ -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() }
} }

View file

@ -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()
{

View file

@ -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;
}

View 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) ) }
} }

View file

@ -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();
}
} }

View file

@ -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>();
}
} }

View 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

View 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

View 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);
});
}
}
}}

View 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

View file

@ -0,0 +1,3 @@
#ifndef HARDFORK_1000_TIME
#define HARDFORK_1000_TIME (fc::time_point_sec( 1540000000 ))
#endif

View 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View 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

View file

@ -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)

View 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

View file

@ -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
*/

View file

@ -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

View file

@ -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

View 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) )

View file

@ -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 );
};

View file

@ -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

View file

@ -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

View 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

View file

@ -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

View file

@ -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) )

View 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))

View file

@ -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>

View file

@ -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) )

View file

@ -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;
}
};
/**

View file

@ -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;

View file

@ -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) )

View file

@ -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)

View file

@ -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) )

View file

@ -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;

View file

@ -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) )

View file

@ -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) )

View file

@ -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) )

View 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) )

View file

@ -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) )

View file

@ -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,

View file

@ -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)

View 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) )

View file

@ -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, )

View file

@ -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) )

View 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

View 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) )

View file

@ -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;

View file

@ -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)

View file

@ -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;

View file

@ -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();
}

View 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

View file

@ -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

View 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

View 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

View file

@ -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

View 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

View file

@ -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 );

View 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

View file

@ -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;

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