Merge remote-tracking branch 'origin/baxter'
This commit is contained in:
commit
0a2af8afb2
153 changed files with 15441 additions and 2521 deletions
8
.gitmodules
vendored
8
.gitmodules
vendored
|
|
@ -1,8 +1,8 @@
|
|||
[submodule "docs"]
|
||||
path = docs
|
||||
url = https://github.com/cryptonomex/graphene.wiki.git
|
||||
path = docs
|
||||
url = https://github.com/bitshares/bitshares-core.wiki.git
|
||||
ignore = dirty
|
||||
[submodule "libraries/fc"]
|
||||
path = libraries/fc
|
||||
url = https://github.com/PBSA/peerplays-0.1-fc.git
|
||||
path = libraries/fc
|
||||
url = https://github.com/PBSA/peerplays-0.1-fc.git
|
||||
ignore = dirty
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
0
CONTRIBUTORS.txt
Normal file
0
CONTRIBUTORS.txt
Normal file
74
Dockerfile
74
Dockerfile
|
|
@ -1,22 +1,64 @@
|
|||
# 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 \
|
||||
libcurl4-openssl-dev \
|
||||
&& \
|
||||
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 update --init --recursive && \
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
. && \
|
||||
make witness_node && \
|
||||
make install && \
|
||||
#
|
||||
# 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 2001
|
||||
|
||||
# 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
|
||||
|
||||
# default execute entry
|
||||
CMD /usr/local/bin/peerplaysentry.sh
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
Copyright (c) 2015-2016 Cryptonomex Inc. <contact@cryptonomex.com>
|
||||
Copyright (c) 2015-2017 contributors <CONTRIBUTORS.txt>
|
||||
|
||||
The MIT License
|
||||
|
||||
|
|
@ -19,4 +20,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
|
@ -210,6 +210,3 @@ Running specific tests
|
|||
----------------------
|
||||
|
||||
- `tests/chain_tests -t block_tests/name_of_test`
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
161
betting2-nhl
Normal file
161
betting2-nhl
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
|
||||
|
||||
### PRE...
|
||||
|
||||
import_key init0 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init1 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init2 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init3 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init4 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init5 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init6 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init7 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init8 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init9 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
import_key init10 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
||||
|
||||
#upgrade_account nathan 1
|
||||
import_balance nathan [5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3] 1
|
||||
|
||||
|
||||
|
||||
transfer nathan init0 125 PPY "" true
|
||||
transfer nathan init1 125 PPY "" true
|
||||
#transfer nathan init2 125 PPY "" true
|
||||
#transfer nathan init3 125 PPY "" true
|
||||
#transfer nathan init4 125 PPY "" true
|
||||
#transfer nathan init5 125 PPY "" true
|
||||
#transfer nathan init6 125 PPY "" true
|
||||
#transfer nathan init7 125 PPY "" true
|
||||
#transfer nathan init8 125 PPY "" true
|
||||
#transfer nathan init9 125 PPY "" true
|
||||
#transfer nathan init10 125 PPY "" true
|
||||
|
||||
transfer nathan 1.2.0 1250 PPY "" true
|
||||
transfer nathan 1.2.1 1250 PPY "" true
|
||||
|
||||
transfer nathan 1.2.5 1250 PPY "" true
|
||||
|
||||
vote_for_witness nathan init0 1 1
|
||||
vote_for_witness nathan init1 1 1
|
||||
|
||||
whitelist_account init0 1.2.1 1 1
|
||||
whitelist_account init1 1.2.1 1 1
|
||||
#whitelist_account init2 1.2.1 1 1
|
||||
#whitelist_account init2 1.2.1 1 1
|
||||
#whitelist_account init3 1.2.1 1 1
|
||||
#whitelist_account init4 1.2.1 1 1
|
||||
#whitelist_account init5 1.2.1 1 1
|
||||
#whitelist_account init6 1.2.1 1 1
|
||||
#whitelist_account init7 1.2.1 1 1
|
||||
#whitelist_account init8 1.2.1 1 1
|
||||
#whitelist_account init9 1.2.1 1 1
|
||||
#whitelist_account init10 1.2.1 1 1
|
||||
|
||||
|
||||
# ALICE & BOB
|
||||
|
||||
register_account alice PPY4zSJHx7D84T1j6HQ7keXWdtabBBWJxvfJw72XmEyqmgdoo1njF PPY4zSJHx7D84T1j6HQ7keXWdtabBBWJxvfJw72XmEyqmgdoo1njF nathan nathan 0 true
|
||||
register_account bob PPY4zSJHx7D84T1j6HQ7keXWdtabBBWJxvfJw72XmEyqmgdoo1njF PPY4zSJHx7D84T1j6HQ7keXWdtabBBWJxvfJw72XmEyqmgdoo1njF nathan nathan 0 true
|
||||
|
||||
transfer nathan alice 1250 PPY "" true
|
||||
transfer nathan bob 1250 PPY "" true
|
||||
|
||||
import_key alice 5HuCDiMeESd86xrRvTbexLjkVg2BEoKrb7BAA5RLgXizkgV3shs
|
||||
import_key bob 5HuCDiMeESd86xrRvTbexLjkVg2BEoKrb7BAA5RLgXizkgV3shs
|
||||
|
||||
|
||||
### SPORT
|
||||
|
||||
propose_create_sport nathan "2017-05-16-T07:46:03" [["en","Ice Hockey"],["zh_Hans","冰球"],["ja","アイスホッケー"]] 1
|
||||
propose_create_sport nathan "2017-05-16-T07:47:01" [["en","spce balls"], ["pl","gra w kulki"]] 1
|
||||
|
||||
// proposal
|
||||
#get_object 1.10.0
|
||||
#get_object 1.10.1
|
||||
|
||||
#approve_proposal nathan 1.10.0 { "active_approvals_to_add" : [ "init0", "init1","init2", "init3", "init4", "init5", "init6", "init7", "init8", "init9", "init10"] } 1
|
||||
approve_proposal nathan 1.10.0 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
approve_proposal nathan 1.10.12 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
#get_object 1.16.0
|
||||
#get_object 1.16.1
|
||||
|
||||
|
||||
### EVENT GROUOP
|
||||
|
||||
propose_create_event_group nathan "2017-05-16-T08:45:01" [ ["en", "NHL"], ["zh_Hans", "國家冰球聯盟"], ["ja", "ナショナルホッケーリーグ"] ] "1.16.0" 1
|
||||
approve_proposal nathan 1.10.2 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.17.0
|
||||
|
||||
|
||||
### EVENT
|
||||
|
||||
propose_create_event nathan "2017-08-07-T11:25:01" [["en", "Washington Capitals/Chicago Blackhawks"], ["zh_Hans", "華盛頓首都隊/芝加哥黑鷹"], ["ja", "ワシントン・キャピタルズ/シカゴ・ブラックホークス"]] [["en","2016-17"]] "2018-09-16-T07:47:01" "1.17.0" 1
|
||||
|
||||
propose_create_event nathan "2017-08-07-T11:25:01" [["en", "Washington Capitals/Chicago Blackhawks"], ["zh_Hans", "華盛頓首都隊/芝加哥黑鷹"], ["ja", "ワシントン・キャピタルズ/シカゴ・ブラックホークス"]] [["en","2017-18"]] "2018-09-16-T07:47:01" "1.17.0" 1
|
||||
|
||||
//propose_update_event nathan "2017-08-07-T11:25:01" "1.18.0" "1.17.0" [["en", "Washington Capitals/Chicago Blackhawks"], ["zh_Hans", "華盛頓首都隊/芝加哥黑鷹"], ["ja", "ワシントン・キャピタルズ/シカゴ・ブラックホークス"]] [["en","2016-17"]] "2018-09-16-T07:47:01" 1
|
||||
|
||||
approve_proposal nathan 1.10.3 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.18.0
|
||||
|
||||
|
||||
# RULES
|
||||
|
||||
propose_create_betting_market_rules nathan "2017-08-31-T10:35:01" [["en","NHL Rules v1.0"]] [["en", "The winner will be the team with the most points at the end of the game. The team with fewer points will not be the winner."]] 1
|
||||
|
||||
approve_proposal nathan 1.10.8 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.19.0
|
||||
|
||||
|
||||
### BETTING_MARKET GROUP
|
||||
|
||||
propose_create_betting_market_group nathan "2017-08-31-T10:39:01" [["en", "Moneyline"]] "1.18.0" "1.19.0" "1.3.0" 1
|
||||
propose_create_betting_market_group nathan "2017-08-25-T11:10:01" [["en", "Moneyline NHL"]] "1.18.1" "1.19.1" "1.3.0" 1
|
||||
|
||||
approve_proposal nathan 1.10.9 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.20.0
|
||||
|
||||
|
||||
### BETTING_MARKET
|
||||
|
||||
propose_create_betting_market nathan "2017-08-31-T10:42:01" "1.20.0" [["en","Washington Capitals win"]] [] 1
|
||||
propose_create_betting_market nathan "2017-08-31-T10:42:01" "1.20.0" [["en","Chicago Blackhawks win"]] [] 1
|
||||
|
||||
approve_proposal nathan 1.10.10 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
approve_proposal nathan 1.10.11 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.21.0
|
||||
//get_object 1.21.1
|
||||
|
||||
//list_betting_markets 1.20.0
|
||||
|
||||
|
||||
# PLACE_BET
|
||||
|
||||
//place_bet bob "1.21.0" 1 { "amount" : 100 } 20000 20000 true
|
||||
//place_bet alice "1.21.0" 0 { "amount" : 100 } 20000 20000 true
|
||||
|
||||
place_bet alice "1.21.0" 1 "123" "PPY" 200 true
|
||||
//place_bet bob "1.21.0" 0 "123" "PPY" 200 true
|
||||
|
||||
|
||||
place_bet carol "1.21.1" 0 "123" "PPY" 200 true
|
||||
cancel_bet carol 1.22.2 1
|
||||
|
||||
get_unmatched_bets_for_bettor 1.21.1 1.2.22
|
||||
|
||||
|
||||
# RESOLVE
|
||||
|
||||
propose_resolve_betting_market_group nathan "2017-08-16-T17:50:01" "1.20.0" [ [ "1.21.0" , 0] [ "1.21.1" , 1] ] 1
|
||||
|
||||
approve_proposal nathan 1.10.9 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
|
||||
|
||||
165
betting2-wimbledon
Normal file
165
betting2-wimbledon
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
|
||||
|
||||
|
||||
http://api.coindesk.com/charts/data?data=close&startdate=2017-01-01&enddate=2017-08-28&exchanges=bpi,bitstamp,coinbase,itbit,okcoin&dev=1&index=USD
|
||||
|
||||
|
||||
|
||||
select date, b.coin_type as bought_coin_type, bought_quantity, s.coin_type as sold_coin_type, sold_quantity from external_trades as t
|
||||
where processed = false
|
||||
left join coin_types as b ON t.bought_coin_type_id = b.coin_type_id
|
||||
left join coin_types as s ON t.sold_coin_type_id = s.coin_type_id
|
||||
|
||||
|
||||
### PRE...
|
||||
|
||||
see betting-nhl
|
||||
|
||||
|
||||
### SPORT
|
||||
|
||||
propose_create_sport nathan "2017-08-16-T08:20:01" [["en","Tennis"], ["pl","Tenis"]] 1
|
||||
|
||||
// proposal
|
||||
#get_object 1.10.10
|
||||
|
||||
approve_proposal nathan 1.10.10 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
#get_object 1.16.2
|
||||
|
||||
|
||||
|
||||
|
||||
### EVENT GROUOP
|
||||
|
||||
propose_create_event_group nathan "2017-08-16-T08:25:01" [ ["en", "Wimbledon"] ] "1.16.2" 1
|
||||
|
||||
approve_proposal nathan 1.10.12 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
// proposal
|
||||
#get_object 1.10.11
|
||||
|
||||
|
||||
//get_object 1.17.1
|
||||
|
||||
propose_update_event_group nathan "2017-08-16-T09:20:01" "1.17.2" "1.16.2" [ ["en", "US Open"] ] 1
|
||||
approve_proposal nathan 1.10.12 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
approve_proposal nathan 1.10.13 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
|
||||
//get_object 1.17.2
|
||||
|
||||
|
||||
### EVENT
|
||||
|
||||
propose_create_event nathan "2017-08-16-T16:15:01" [["en", "R. Federer/T. Berdych"]] [["en","2017"]] "2017-09-16-T07:47:01" "1.17.1" 1
|
||||
propose_create_event nathan "2017-08-16-T16:15:01" [["en", "M. Cilic/S. Querrye"]] [["en","2017"]] "2017-09-16-T07:47:01" "1.17.1" 1
|
||||
|
||||
approve_proposal nathan 1.10.14 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
approve_proposal nathan 1.10.15 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.18.1
|
||||
//get_object 1.18.2
|
||||
|
||||
|
||||
# RULES
|
||||
|
||||
propose_create_betting_market_rules nathan "2017-08-16-T16:20:01" [["en","Tennis Rules v1.0"]] [["en", "The winner is the player who wins the last ball in the match.."]] 1
|
||||
|
||||
approve_proposal nathan 1.10.16 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
0.14
|
||||
//get_object 1.19.?
|
||||
|
||||
|
||||
### BETTING_MARKET GROUP
|
||||
|
||||
propose_create_betting_market_group nathan "2017-08-16-T16:25:01" [["en", "Moneyline 1st sf men"]] "1.18.1" "1.19.1" "1.3.0" 1
|
||||
propose_create_betting_market_group nathan "2017-08-16-T16:25:01" [["en", "Moneyline 2nd sf men"]] "1.18.2" "1.19.1" "1.3.0" 1
|
||||
|
||||
approve_proposal nathan 1.10.17 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
approve_proposal nathan 1.10.18 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.20.0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### FINAL
|
||||
|
||||
propose_create_event nathan "2017-08-16-T16:30:01" [["en", "R. Federer/M. Cilic"]] [["en","2017"]] "2017-09-16-T07:47:01" "1.17.1" 1
|
||||
approve_proposal nathan 1.10.19 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
|
||||
propose_create_betting_market_group nathan "2017-08-16-T16:35:01" [["en", "Moneyline final men"]] "1.18.3" "1.19.1" "1.3.0" 1
|
||||
approve_proposal nathan 1.10.20 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### BETTING_MARKET
|
||||
|
||||
propose_create_betting_market nathan "2017-08-16-T17:05:01" "1.20.1" [["en", "T. Berdych defeats R. Federer"}]] [] 1
|
||||
propose_create_betting_market nathan "2017-08-16-T17:05:02" "1.20.1" [["en","R. Federer defeats T. Berdych"]] [] 1
|
||||
|
||||
approve_proposal nathan 1.10.21 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
approve_proposal nathan 1.10.22 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.21.2
|
||||
//get_object 1.21.3
|
||||
|
||||
//list_betting_markets 1.20.1
|
||||
|
||||
|
||||
|
||||
propose_create_betting_market nathan "2017-08-16-T17:10:01" "1.20.2" [["en", "M. Cilic defeats S. Querrey"]] [] 1
|
||||
propose_create_betting_market nathan "2017-08-16-T17:10:02" "1.20.2" [["en","S. Querrey defeats M. Cilic"]] [] 1
|
||||
|
||||
approve_proposal nathan 1.10.23 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
approve_proposal nathan 1.10.24 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.21.4
|
||||
//get_object 1.21.5
|
||||
|
||||
//list_betting_markets 1.20.2
|
||||
|
||||
|
||||
|
||||
|
||||
propose_create_betting_market nathan "2017-08-16-T17:15:01" "1.20.3" [["en", "R. Federer defeats M. Cilic"]] [] 1
|
||||
propose_create_betting_market nathan "2017-08-16-T17:15:02" "1.20.3" [["en","M. Cilic defeats R. Federer"]] [] 1
|
||||
|
||||
approve_proposal nathan 1.10.25 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
approve_proposal nathan 1.10.26 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
//get_object 1.21.6
|
||||
//get_object 1.21.7
|
||||
|
||||
//
|
||||
|
||||
|
||||
|
||||
# PLACE_BET
|
||||
|
||||
#place_bet bob "1.21.2" 1 { "amount" : 100 } 20000 20000 true
|
||||
#place_bet alice "1.21.2" 0 { "amount" : 100 } 20000 20000 true
|
||||
|
||||
|
||||
place_bet alice "1.21.1" 1 "123" "PPY" 200 true
|
||||
place_bet bob "1.21.1" 0 "123" "PPY" 200 true
|
||||
|
||||
|
||||
|
||||
# RESOLVE
|
||||
|
||||
propose_resolve_betting_market_group nathan "2017-08-16-T17:50:01" "1.20.2" [ [ "1.21.4" , 0] [ "1.21.5" , 1] ] 1
|
||||
|
||||
approve_proposal nathan 1.10.27 { "active_approvals_to_add" : [ "init0", "init1"] } 1
|
||||
|
||||
|
||||
|
||||
1113
betting_simulator.html
Normal file
1113
betting_simulator.html
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -47,28 +47,15 @@ required-participation = false
|
|||
# track-account =
|
||||
|
||||
# Track market history by grouping orders into buckets of equal size measured in seconds specified as a JSON array of numbers
|
||||
bucket-size = [15,60,300,3600,86400]
|
||||
# bucket-size = [15,60,300,3600,86400]
|
||||
bucket-size = [60,300,900,1800,3600,14400,86400]
|
||||
# for 1 min, 5 mins, 30 mins, 1h, 4 hs and 1 day. i think this should be the default.
|
||||
|
||||
# How far back in time to track history for each bucket size, measured in the number of buckets (default: 1000)
|
||||
history-per-size = 1000
|
||||
|
||||
# declare an appender named "stderr" that writes messages to the console
|
||||
[log.console_appender.stderr]
|
||||
stream=std_error
|
||||
|
||||
# declare an appender named "p2p" that writes messages to p2p.log
|
||||
[log.file_appender.p2p]
|
||||
filename=logs/p2p/p2p.log
|
||||
# filename can be absolute or relative to this config file
|
||||
|
||||
# route any messages logged to the default logger to the "stderr" logger we
|
||||
# declared above, if they are info level are higher
|
||||
[logger.default]
|
||||
level=info
|
||||
appenders=stderr
|
||||
|
||||
# route messages sent to the "p2p" logger to the p2p appender declared above
|
||||
[logger.p2p]
|
||||
level=debug
|
||||
appenders=p2p
|
||||
# Max amount of operations to store in the database, per account (drastically reduces RAM requirements)
|
||||
max-ops-per-account = 1000
|
||||
|
||||
# Remove old operation history # objects from RAM
|
||||
partial-operations = true
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
[ -e /data_dir/config.ini ] || cp /default_config.ini /data_dir/config.ini
|
||||
|
||||
[ -e /data_dir/pre_exec ] && bash /data_dir/pre_exec
|
||||
if [ -e /data_dir/extra_args ]; then
|
||||
/bitshares-2/programs/witness_node/witness_node --data-dir /data_dir `cat /data_dir/extra_args`
|
||||
else
|
||||
/bitshares-2/programs/witness_node/witness_node --data-dir /data_dir
|
||||
fi
|
||||
[ -e /data_dir/post_exec ] && bash /data_dir/post_exec
|
||||
82
docker/peerplaysentry.sh
Normal file
82
docker/peerplaysentry.sh
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#!/bin/bash
|
||||
PEERPLAYSD="/usr/local/bin/witness_node"
|
||||
|
||||
# For blockchain download
|
||||
VERSION=`cat /etc/peerplays/version`
|
||||
|
||||
## Supported Environmental Variables
|
||||
#
|
||||
# * $PEERPLAYSD_SEED_NODES
|
||||
# * $PEERPLAYSD_RPC_ENDPOINT
|
||||
# * $PEERPLAYSD_PLUGINS
|
||||
# * $PEERPLAYSD_REPLAY
|
||||
# * $PEERPLAYSD_RESYNC
|
||||
# * $PEERPLAYSD_P2P_ENDPOINT
|
||||
# * $PEERPLAYSD_WITNESS_ID
|
||||
# * $PEERPLAYSD_PRIVATE_KEY
|
||||
# * $PEERPLAYSD_TRACK_ACCOUNTS
|
||||
# * $PEERPLAYSD_PARTIAL_OPERATIONS
|
||||
# * $PEERPLAYSD_MAX_OPS_PER_ACCOUNT
|
||||
# * $PEERPLAYSD_TRUSTED_NODE
|
||||
#
|
||||
|
||||
ARGS=""
|
||||
# Translate environmental variables
|
||||
if [[ ! -z "$PEERPLAYSD_SEED_NODES" ]]; then
|
||||
for NODE in $PEERPLAYSD_SEED_NODES ; do
|
||||
ARGS+=" --seed-node=$NODE"
|
||||
done
|
||||
fi
|
||||
if [[ ! -z "$PEERPLAYSD_RPC_ENDPOINT" ]]; then
|
||||
ARGS+=" --rpc-endpoint=${PEERPLAYSD_RPC_ENDPOINT}"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_REPLAY" ]]; then
|
||||
ARGS+=" --replay-blockchain"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_RESYNC" ]]; then
|
||||
ARGS+=" --resync-blockchain"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_P2P_ENDPOINT" ]]; then
|
||||
ARGS+=" --p2p-endpoint=${PEERPLAYSD_P2P_ENDPOINT}"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_WITNESS_ID" ]]; then
|
||||
ARGS+=" --witness-id=$PEERPLAYSD_WITNESS_ID"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_PRIVATE_KEY" ]]; then
|
||||
ARGS+=" --private-key=$PEERPLAYSD_PRIVATE_KEY"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_TRACK_ACCOUNTS" ]]; then
|
||||
for ACCOUNT in $PEERPLAYSD_TRACK_ACCOUNTS ; do
|
||||
ARGS+=" --track-account=$ACCOUNT"
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_PARTIAL_OPERATIONS" ]]; then
|
||||
ARGS+=" --partial-operations=${PEERPLAYSD_PARTIAL_OPERATIONS}"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_MAX_OPS_PER_ACCOUNT" ]]; then
|
||||
ARGS+=" --max-ops-per-account=${PEERPLAYSD_MAX_OPS_PER_ACCOUNT}"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$PEERPLAYSD_TRUSTED_NODE" ]]; then
|
||||
ARGS+=" --trusted-node=${PEERPLAYSD_TRUSTED_NODE}"
|
||||
fi
|
||||
|
||||
## Link the peerplays config file into home
|
||||
## This link has been created in Dockerfile, already
|
||||
ln -f -s /etc/peerplays/config.ini /var/lib/peerplays
|
||||
|
||||
# Plugins need to be provided in a space-separated list, which
|
||||
# makes it necessary to write it like this
|
||||
if [[ ! -z "$PEERPLAYSD_PLUGINS" ]]; then
|
||||
$PEERPLAYSD --data-dir ${HOME} ${ARGS} ${PEERPLAYSD_ARGS} --plugins "${PEERPLAYSD_PLUGINS}"
|
||||
else
|
||||
$PEERPLAYSD --data-dir ${HOME} ${ARGS} ${PEERPLAYSD_ARGS}
|
||||
fi
|
||||
|
|
@ -1 +1 @@
|
|||
2.0.160208
|
||||
2.0.170522
|
||||
|
|
|
|||
|
|
@ -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_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie )
|
||||
target_include_directories( graphene_app
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" )
|
||||
|
|
@ -28,3 +29,4 @@ INSTALL( TARGETS
|
|||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/app" )
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <fc/thread/future.hpp>
|
||||
|
||||
namespace graphene { namespace app {
|
||||
|
||||
|
|
@ -80,6 +81,10 @@ namespace graphene { namespace app {
|
|||
{
|
||||
_database_api = std::make_shared< database_api >( std::ref( *_app.chain_database() ) );
|
||||
}
|
||||
else if( api_name == "block_api" )
|
||||
{
|
||||
_block_api = std::make_shared< block_api >( std::ref( *_app.chain_database() ) );
|
||||
}
|
||||
else if( api_name == "network_broadcast_api" )
|
||||
{
|
||||
_network_broadcast_api = std::make_shared< network_broadcast_api >( std::ref( _app ) );
|
||||
|
|
@ -96,15 +101,39 @@ namespace graphene { namespace app {
|
|||
{
|
||||
_crypto_api = std::make_shared< crypto_api >();
|
||||
}
|
||||
else if( api_name == "asset_api" )
|
||||
{
|
||||
_asset_api = std::make_shared< asset_api >( std::ref( *_app.chain_database() ) );
|
||||
}
|
||||
else if( api_name == "debug_api" )
|
||||
{
|
||||
// can only enable this API if the plugin was loaded
|
||||
if( _app.get_plugin( "debug_witness" ) )
|
||||
_debug_api = std::make_shared< graphene::debug_witness::debug_api >( std::ref(_app) );
|
||||
}
|
||||
else if( api_name == "bookie_api" )
|
||||
{
|
||||
// can only enable this API if the plugin was loaded
|
||||
if( _app.get_plugin( "bookie" ) )
|
||||
_bookie_api = std::make_shared<graphene::bookie::bookie_api>(std::ref(_app));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// block_api
|
||||
block_api::block_api(graphene::chain::database& db) : _db(db) { }
|
||||
block_api::~block_api() { }
|
||||
|
||||
vector<optional<signed_block>> block_api::get_blocks(uint32_t block_num_from, uint32_t block_num_to)const
|
||||
{
|
||||
FC_ASSERT( block_num_to >= block_num_from );
|
||||
vector<optional<signed_block>> res;
|
||||
for(uint32_t block_num=block_num_from; block_num<=block_num_to; block_num++) {
|
||||
res.push_back(_db.fetch_block_by_number(block_num));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
network_broadcast_api::network_broadcast_api(application& a):_app(a)
|
||||
{
|
||||
_applied_block_connection = _app.chain_database()->applied_block.connect([this](const signed_block& b){ on_applied_block(b); });
|
||||
|
|
@ -138,6 +167,16 @@ namespace graphene { namespace app {
|
|||
_app.p2p_node()->broadcast_transaction(trx);
|
||||
}
|
||||
|
||||
fc::variant network_broadcast_api::broadcast_transaction_synchronous(const signed_transaction& trx)
|
||||
{
|
||||
fc::promise<fc::variant>::ptr prom( new fc::promise<fc::variant>() );
|
||||
broadcast_transaction_with_callback( [=]( const fc::variant& v ){
|
||||
prom->set_value(v);
|
||||
}, trx );
|
||||
|
||||
return fc::future<fc::variant>(prom).wait();
|
||||
}
|
||||
|
||||
void network_broadcast_api::broadcast_block( const signed_block& b )
|
||||
{
|
||||
_app.chain_database()->push_block(b);
|
||||
|
|
@ -194,6 +233,12 @@ namespace graphene { namespace app {
|
|||
return *_network_broadcast_api;
|
||||
}
|
||||
|
||||
fc::api<block_api> login_api::block()const
|
||||
{
|
||||
FC_ASSERT(_block_api);
|
||||
return *_block_api;
|
||||
}
|
||||
|
||||
fc::api<network_node_api> login_api::network_node()const
|
||||
{
|
||||
FC_ASSERT(_network_node_api);
|
||||
|
|
@ -218,12 +263,24 @@ namespace graphene { namespace app {
|
|||
return *_crypto_api;
|
||||
}
|
||||
|
||||
fc::api<asset_api> login_api::asset() const
|
||||
{
|
||||
FC_ASSERT(_asset_api);
|
||||
return *_asset_api;
|
||||
}
|
||||
|
||||
fc::api<graphene::debug_witness::debug_api> login_api::debug() const
|
||||
{
|
||||
FC_ASSERT(_debug_api);
|
||||
return *_debug_api;
|
||||
}
|
||||
|
||||
fc::api<graphene::bookie::bookie_api> login_api::bookie() const
|
||||
{
|
||||
FC_ASSERT(_bookie_api);
|
||||
return *_bookie_api;
|
||||
}
|
||||
|
||||
#if 0
|
||||
vector<account_id_type> get_relevant_accounts( const object* obj )
|
||||
{
|
||||
|
|
@ -306,6 +363,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);
|
||||
|
|
@ -372,6 +441,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;
|
||||
|
|
@ -403,13 +476,13 @@ namespace graphene { namespace app {
|
|||
return result;
|
||||
}
|
||||
|
||||
vector<operation_history_object> history_api::get_account_history( account_id_type account,
|
||||
operation_history_id_type stop,
|
||||
unsigned limit,
|
||||
vector<operation_history_object> history_api::get_account_history( account_id_type account,
|
||||
operation_history_id_type stop,
|
||||
unsigned limit,
|
||||
operation_history_id_type start ) const
|
||||
{
|
||||
FC_ASSERT( _app.chain_database() );
|
||||
const auto& db = *_app.chain_database();
|
||||
const auto& db = *_app.chain_database();
|
||||
FC_ASSERT( limit <= 100 );
|
||||
vector<operation_history_object> result;
|
||||
const auto& stats = account(db).statistics(db);
|
||||
|
|
@ -417,7 +490,7 @@ namespace graphene { namespace app {
|
|||
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 )
|
||||
|
|
@ -426,38 +499,82 @@ namespace graphene { namespace app {
|
|||
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,
|
||||
|
||||
vector<operation_history_object> history_api::get_account_history_operations( account_id_type account,
|
||||
int operation_id,
|
||||
operation_history_id_type start,
|
||||
operation_history_id_type stop,
|
||||
unsigned limit) const
|
||||
{
|
||||
FC_ASSERT( _app.chain_database() );
|
||||
const auto& db = *_app.chain_database();
|
||||
FC_ASSERT( limit <= 100 );
|
||||
vector<operation_history_object> result;
|
||||
const auto& stats = account(db).statistics(db);
|
||||
if( stats.most_recent_op == account_transaction_history_id_type() ) return result;
|
||||
const account_transaction_history_object* node = &stats.most_recent_op(db);
|
||||
if( start == operation_history_id_type() )
|
||||
start = node->operation_id;
|
||||
|
||||
while(node && node->operation_id.instance.value > stop.instance.value && result.size() < limit)
|
||||
{
|
||||
if( node->operation_id.instance.value <= start.instance.value ) {
|
||||
|
||||
if(node->operation_id(db).op.which() == operation_id)
|
||||
result.push_back( node->operation_id(db) );
|
||||
}
|
||||
if( node->next == account_transaction_history_id_type() )
|
||||
node = nullptr;
|
||||
else node = &node->next(db);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
vector<operation_history_object> history_api::get_relative_account_history( account_id_type account,
|
||||
uint32_t stop,
|
||||
unsigned limit,
|
||||
uint32_t start) const
|
||||
{
|
||||
FC_ASSERT( _app.chain_database() );
|
||||
const auto& db = *_app.chain_database();
|
||||
FC_ASSERT(limit <= 100);
|
||||
vector<operation_history_object> result;
|
||||
const auto& stats = account(db).statistics(db);
|
||||
if( start == 0 )
|
||||
start = account(db).statistics(db).total_ops;
|
||||
else start = min( account(db).statistics(db).total_ops, start );
|
||||
const auto& hist_idx = db.get_index_type<account_transaction_history_index>();
|
||||
const auto& by_seq_idx = hist_idx.indices().get<by_seq>();
|
||||
|
||||
auto itr = by_seq_idx.upper_bound( boost::make_tuple( account, start ) );
|
||||
auto itr_stop = by_seq_idx.lower_bound( boost::make_tuple( account, stop ) );
|
||||
--itr;
|
||||
|
||||
while ( itr != itr_stop && result.size() < limit )
|
||||
start = stats.total_ops;
|
||||
else
|
||||
start = min( stats.total_ops, start );
|
||||
|
||||
|
||||
if( start >= stop && start > stats.removed_ops && limit > 0 )
|
||||
{
|
||||
result.push_back( itr->operation_id(db) );
|
||||
--itr;
|
||||
const auto& hist_idx = db.get_index_type<account_transaction_history_index>();
|
||||
const auto& by_seq_idx = hist_idx.indices().get<by_seq>();
|
||||
|
||||
auto itr = by_seq_idx.upper_bound( boost::make_tuple( account, start ) );
|
||||
auto itr_stop = by_seq_idx.lower_bound( boost::make_tuple( account, stop ) );
|
||||
|
||||
do
|
||||
{
|
||||
--itr;
|
||||
result.push_back( itr->operation_id(db) );
|
||||
}
|
||||
while ( itr != itr_stop && result.size() < limit );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<account_balance_object> history_api::list_core_accounts()const
|
||||
{
|
||||
auto list = _app.get_plugin<accounts_list_plugin>( "accounts_list" );
|
||||
FC_ASSERT( list );
|
||||
return list->list_accounts();
|
||||
}
|
||||
|
||||
flat_set<uint32_t> history_api::get_market_history_buckets()const
|
||||
{
|
||||
auto hist = _app.get_plugin<market_history_plugin>( "market_history" );
|
||||
|
|
@ -490,14 +607,14 @@ namespace graphene { namespace app {
|
|||
}
|
||||
return result;
|
||||
} FC_CAPTURE_AND_RETHROW( (a)(b)(bucket_seconds)(start)(end) ) }
|
||||
|
||||
|
||||
crypto_api::crypto_api(){};
|
||||
|
||||
|
||||
blind_signature crypto_api::blind_sign( const extended_private_key_type& key, const blinded_hash& hash, int i )
|
||||
{
|
||||
return fc::ecc::extended_private_key( key ).blind_sign( hash, i );
|
||||
}
|
||||
|
||||
|
||||
signature_type crypto_api::unblind_signature( const extended_private_key_type& key,
|
||||
const extended_public_key_type& bob,
|
||||
const blind_signature& sig,
|
||||
|
|
@ -506,32 +623,32 @@ namespace graphene { namespace app {
|
|||
{
|
||||
return fc::ecc::extended_private_key( key ).unblind_signature( extended_public_key( bob ), sig, hash, i );
|
||||
}
|
||||
|
||||
|
||||
commitment_type crypto_api::blind( const blind_factor_type& blind, uint64_t value )
|
||||
{
|
||||
return fc::ecc::blind( blind, value );
|
||||
}
|
||||
|
||||
|
||||
blind_factor_type crypto_api::blind_sum( const std::vector<blind_factor_type>& blinds_in, uint32_t non_neg )
|
||||
{
|
||||
return fc::ecc::blind_sum( blinds_in, non_neg );
|
||||
}
|
||||
|
||||
|
||||
bool crypto_api::verify_sum( const std::vector<commitment_type>& commits_in, const std::vector<commitment_type>& neg_commits_in, int64_t excess )
|
||||
{
|
||||
return fc::ecc::verify_sum( commits_in, neg_commits_in, excess );
|
||||
}
|
||||
|
||||
|
||||
verify_range_result crypto_api::verify_range( const commitment_type& commit, const std::vector<char>& proof )
|
||||
{
|
||||
verify_range_result result;
|
||||
result.success = fc::ecc::verify_range( result.min_val, result.max_val, commit, proof );
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<char> crypto_api::range_proof_sign( uint64_t min_value,
|
||||
const commitment_type& commit,
|
||||
const blind_factor_type& commit_blind,
|
||||
|
||||
std::vector<char> crypto_api::range_proof_sign( uint64_t min_value,
|
||||
const commitment_type& commit,
|
||||
const blind_factor_type& commit_blind,
|
||||
const blind_factor_type& nonce,
|
||||
int8_t base10_exp,
|
||||
uint8_t min_bits,
|
||||
|
|
@ -539,26 +656,100 @@ namespace graphene { namespace app {
|
|||
{
|
||||
return fc::ecc::range_proof_sign( min_value, commit, commit_blind, nonce, base10_exp, min_bits, actual_value );
|
||||
}
|
||||
|
||||
|
||||
verify_range_proof_rewind_result crypto_api::verify_range_proof_rewind( const blind_factor_type& nonce,
|
||||
const commitment_type& commit,
|
||||
const commitment_type& commit,
|
||||
const std::vector<char>& proof )
|
||||
{
|
||||
verify_range_proof_rewind_result result;
|
||||
result.success = fc::ecc::verify_range_proof_rewind( result.blind_out,
|
||||
result.value_out,
|
||||
result.message_out,
|
||||
nonce,
|
||||
result.min_val,
|
||||
result.max_val,
|
||||
const_cast< commitment_type& >( commit ),
|
||||
result.success = fc::ecc::verify_range_proof_rewind( result.blind_out,
|
||||
result.value_out,
|
||||
result.message_out,
|
||||
nonce,
|
||||
result.min_val,
|
||||
result.max_val,
|
||||
const_cast< commitment_type& >( commit ),
|
||||
proof );
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
range_proof_info crypto_api::range_get_info( const std::vector<char>& proof )
|
||||
{
|
||||
return fc::ecc::range_get_info( proof );
|
||||
}
|
||||
|
||||
// asset_api
|
||||
asset_api::asset_api(graphene::chain::database& db) : _db(db) { }
|
||||
asset_api::~asset_api() { }
|
||||
|
||||
vector<account_asset_balance> asset_api::get_asset_holders( asset_id_type asset_id, uint32_t start, uint32_t limit ) const {
|
||||
FC_ASSERT(limit <= 100);
|
||||
|
||||
const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >();
|
||||
auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) );
|
||||
|
||||
vector<account_asset_balance> result;
|
||||
|
||||
uint32_t index = 0;
|
||||
for( const account_balance_object& bal : boost::make_iterator_range( range.first, range.second ) )
|
||||
{
|
||||
if( result.size() >= limit )
|
||||
break;
|
||||
|
||||
if( bal.balance.value == 0 )
|
||||
continue;
|
||||
|
||||
if( index++ < start )
|
||||
continue;
|
||||
|
||||
const auto account = _db.find(bal.owner);
|
||||
|
||||
account_asset_balance aab;
|
||||
aab.name = account->name;
|
||||
aab.account_id = account->id;
|
||||
aab.amount = bal.balance.value;
|
||||
|
||||
result.push_back(aab);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
// get number of asset holders.
|
||||
int asset_api::get_asset_holders_count( asset_id_type asset_id ) const {
|
||||
|
||||
const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >();
|
||||
auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) );
|
||||
|
||||
int count = boost::distance(range) - 1;
|
||||
|
||||
return count;
|
||||
}
|
||||
// function to get vector of system assets with holders count.
|
||||
vector<asset_holders> asset_api::get_all_asset_holders() const {
|
||||
|
||||
vector<asset_holders> result;
|
||||
|
||||
vector<asset_id_type> total_assets;
|
||||
for( const asset_object& asset_obj : _db.get_index_type<asset_index>().indices() )
|
||||
{
|
||||
const auto& dasset_obj = asset_obj.dynamic_asset_data_id(_db);
|
||||
|
||||
asset_id_type asset_id;
|
||||
asset_id = dasset_obj.id;
|
||||
|
||||
const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >();
|
||||
auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) );
|
||||
|
||||
int count = boost::distance(range) - 1;
|
||||
|
||||
asset_holders ah;
|
||||
ah.asset_id = asset_id;
|
||||
ah.count = count;
|
||||
|
||||
result.push_back(ah);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} } // graphene::app
|
||||
|
|
|
|||
|
|
@ -28,15 +28,12 @@
|
|||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/time/time.hpp>
|
||||
|
||||
#include <graphene/egenesis/egenesis.hpp>
|
||||
|
||||
#include <graphene/net/core_messages.hpp>
|
||||
#include <graphene/net/exceptions.hpp>
|
||||
|
||||
#include <graphene/time/time.hpp>
|
||||
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
#include <graphene/chain/worker_evaluator.hpp>
|
||||
|
||||
|
|
@ -46,10 +43,12 @@
|
|||
#include <fc/rpc/api_connection.hpp>
|
||||
#include <fc/rpc/websocket_api.hpp>
|
||||
#include <fc/network/resolve.hpp>
|
||||
#include <fc/crypto/base64.hpp>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/signals2.hpp>
|
||||
#include <boost/range/algorithm/reverse.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
|
@ -198,6 +197,7 @@ namespace detail {
|
|||
"54.37.235.164:7777", // melea-trust
|
||||
"peerplays-seed.lukestokes.info:7777" // lukestokes-witness
|
||||
};
|
||||
|
||||
for( const string& endpoint_string : seeds )
|
||||
{
|
||||
try {
|
||||
|
|
@ -254,23 +254,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();
|
||||
|
|
@ -288,17 +314,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();
|
||||
|
|
@ -339,7 +357,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";
|
||||
|
|
@ -390,79 +408,67 @@ namespace detail {
|
|||
}
|
||||
_chain_db->add_checkpoints( loaded_checkpoints );
|
||||
|
||||
if( _options->count("replay-blockchain") )
|
||||
bool replay = false;
|
||||
std::string replay_reason = "reason not provided";
|
||||
|
||||
// never replay if data dir is empty
|
||||
if( fc::exists( _data_dir ) && fc::directory_iterator( _data_dir ) != fc::directory_iterator() )
|
||||
{
|
||||
ilog("Replaying blockchain on user request.");
|
||||
_chain_db->reindex(_data_dir/"blockchain", initial_state());
|
||||
} else if( clean ) {
|
||||
|
||||
auto is_new = [&]() -> bool
|
||||
if( _options->count("replay-blockchain") )
|
||||
{
|
||||
// directory doesn't exist
|
||||
if( !fc::exists( _data_dir ) )
|
||||
return true;
|
||||
// if directory exists but is empty, return true; else false.
|
||||
return ( fc::directory_iterator( _data_dir ) == fc::directory_iterator() );
|
||||
};
|
||||
|
||||
auto is_outdated = [&]() -> bool
|
||||
replay = true;
|
||||
replay_reason = "replay-blockchain argument specified";
|
||||
}
|
||||
else if( !clean )
|
||||
{
|
||||
if( !fc::exists( _data_dir / "db_version" ) )
|
||||
return true;
|
||||
std::string version_str;
|
||||
fc::read_file_contents( _data_dir / "db_version", version_str );
|
||||
return (version_str != GRAPHENE_CURRENT_DB_VERSION);
|
||||
};
|
||||
|
||||
bool need_reindex = (!is_new() && is_outdated());
|
||||
std::string reindex_reason = "version upgrade";
|
||||
|
||||
if( !need_reindex )
|
||||
replay = true;
|
||||
replay_reason = "unclean shutdown detected";
|
||||
}
|
||||
else if( !fc::exists( _data_dir / "db_version" ) )
|
||||
{
|
||||
try
|
||||
replay = true;
|
||||
replay_reason = "db_version file not found";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string version_string;
|
||||
fc::read_file_contents( _data_dir / "db_version", version_string );
|
||||
|
||||
if( version_string != GRAPHENE_CURRENT_DB_VERSION )
|
||||
{
|
||||
_chain_db->open(_data_dir / "blockchain", initial_state);
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
ilog( "caught exception ${e} in open()", ("e", e.to_detail_string()) );
|
||||
need_reindex = true;
|
||||
reindex_reason = "exception in open()";
|
||||
replay = true;
|
||||
replay_reason = "db_version file content mismatch";
|
||||
}
|
||||
}
|
||||
|
||||
if( need_reindex )
|
||||
{
|
||||
ilog("Replaying blockchain due to ${reason}", ("reason", reindex_reason) );
|
||||
|
||||
fc::remove_all( _data_dir / "db_version" );
|
||||
_chain_db->reindex(_data_dir / "blockchain", initial_state());
|
||||
|
||||
// doing this down here helps ensure that DB will be wiped
|
||||
// if any of the above steps were interrupted on a previous run
|
||||
if( !fc::exists( _data_dir / "db_version" ) )
|
||||
{
|
||||
std::ofstream db_version(
|
||||
(_data_dir / "db_version").generic_string().c_str(),
|
||||
std::ios::out | std::ios::binary | std::ios::trunc );
|
||||
std::string version_string = GRAPHENE_CURRENT_DB_VERSION;
|
||||
db_version.write( version_string.c_str(), version_string.size() );
|
||||
db_version.close();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
wlog("Detected unclean shutdown. Replaying blockchain...");
|
||||
_chain_db->reindex(_data_dir / "blockchain", initial_state());
|
||||
}
|
||||
|
||||
if (!_options->count("genesis-json") &&
|
||||
_chain_db->get_chain_id() != graphene::egenesis::get_egenesis_chain_id()) {
|
||||
elog("Detected old database. Nuking and starting over.");
|
||||
_chain_db->wipe(_data_dir / "blockchain", true);
|
||||
_chain_db.reset();
|
||||
_chain_db = std::make_shared<chain::database>();
|
||||
_chain_db->add_checkpoints(loaded_checkpoints);
|
||||
_chain_db->open(_data_dir / "blockchain", initial_state);
|
||||
if( !replay )
|
||||
{
|
||||
try
|
||||
{
|
||||
_chain_db->open( _data_dir / "blockchain", initial_state );
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
ilog( "Caught exception ${e} in open()", ("e", e.to_detail_string()) );
|
||||
|
||||
replay = true;
|
||||
replay_reason = "exception in open()";
|
||||
}
|
||||
}
|
||||
|
||||
if( replay )
|
||||
{
|
||||
ilog( "Replaying blockchain due to: ${reason}", ("reason", replay_reason) );
|
||||
|
||||
fc::remove_all( _data_dir / "db_version" );
|
||||
_chain_db->reindex( _data_dir / "blockchain", initial_state() );
|
||||
|
||||
const auto mode = std::ios::out | std::ios::binary | std::ios::trunc;
|
||||
std::ofstream db_version( (_data_dir / "db_version").generic_string().c_str(), mode );
|
||||
std::string version_string = GRAPHENE_CURRENT_DB_VERSION;
|
||||
db_version.write( version_string.c_str(), version_string.size() );
|
||||
db_version.close();
|
||||
}
|
||||
|
||||
if( _options->count("force-validate") )
|
||||
|
|
@ -471,8 +477,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>();
|
||||
|
|
@ -488,6 +492,7 @@ namespace detail {
|
|||
wild_access.allowed_apis.push_back( "network_broadcast_api" );
|
||||
wild_access.allowed_apis.push_back( "history_api" );
|
||||
wild_access.allowed_apis.push_back( "crypto_api" );
|
||||
wild_access.allowed_apis.push_back( "bookie_api" );
|
||||
_apiaccess.permission_map["*"] = wild_access;
|
||||
}
|
||||
|
||||
|
|
@ -496,6 +501,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;
|
||||
|
|
@ -540,21 +546,21 @@ 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);
|
||||
const auto& witness_account = witness.witness_account(*_chain_db);
|
||||
auto last_irr = _chain_db->get_dynamic_global_properties().last_irreversible_block_num;
|
||||
ilog("Got block: #${n} time: ${t} latency: ${l} ms from: ${w} irreversible: ${i} (-${d})",
|
||||
ilog("Got block: #${n} time: ${t} latency: ${l} ms from: ${w} irreversible: ${i} (-${d})",
|
||||
("t",blk_msg.block.timestamp)
|
||||
("n", blk_msg.block.block_num())
|
||||
("l", (latency.count()/1000))
|
||||
("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,
|
||||
|
|
@ -643,7 +649,7 @@ namespace detail {
|
|||
|
||||
result.reserve(limit);
|
||||
block_id_type last_known_block_id;
|
||||
|
||||
|
||||
if (blockchain_synopsis.empty() ||
|
||||
(blockchain_synopsis.size() == 1 && blockchain_synopsis[0] == block_id_type()))
|
||||
{
|
||||
|
|
@ -704,13 +710,13 @@ namespace detail {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a synopsis of the blockchain used for syncing. This consists of a list of
|
||||
* Returns a synopsis of the blockchain used for syncing. This consists of a list of
|
||||
* block hashes at intervals exponentially increasing towards the genesis block.
|
||||
* When syncing to a peer, the peer uses this data to determine if we're on the same
|
||||
* fork as they are, and if not, what blocks they need to send us to get us on their
|
||||
* fork.
|
||||
*
|
||||
* In the over-simplified case, this is a straighforward synopsis of our current
|
||||
* In the over-simplified case, this is a straighforward synopsis of our current
|
||||
* preferred blockchain; when we first connect up to a peer, this is what we will be sending.
|
||||
* It looks like this:
|
||||
* If the blockchain is empty, it will return the empty list.
|
||||
|
|
@ -726,7 +732,7 @@ namespace detail {
|
|||
* the last item in the list will be the hash of the most recent block on our preferred chain
|
||||
* so if the blockchain had 26 blocks labeled a - z, the synopsis would be:
|
||||
* a n u x z
|
||||
* the idea being that by sending a small (<30) number of block ids, we can summarize a huge
|
||||
* the idea being that by sending a small (<30) number of block ids, we can summarize a huge
|
||||
* blockchain. The block ids are more dense near the end of the chain where because we are
|
||||
* more likely to be almost in sync when we first connect, and forks are likely to be short.
|
||||
* If the peer we're syncing with in our example is on a fork that started at block 'v',
|
||||
|
|
@ -741,27 +747,27 @@ namespace detail {
|
|||
* no reason to fetch the blocks.
|
||||
*
|
||||
* Second, when a peer replies to our initial synopsis and gives us a list of the blocks they think
|
||||
* we are missing, they only send a chunk of a few thousand blocks at once. After we get those
|
||||
* we are missing, they only send a chunk of a few thousand blocks at once. After we get those
|
||||
* block ids, we need to request more blocks by sending another synopsis (we can't just say "send me
|
||||
* the next 2000 ids" because they may have switched forks themselves and they don't track what
|
||||
* they've sent us). For faster performance, we want to get a fairly long list of block ids first,
|
||||
* then start downloading the blocks.
|
||||
* The peer doesn't handle these follow-up block id requests any different from the initial request;
|
||||
* it treats the synopsis we send as our blockchain and bases its response entirely off that. So to
|
||||
* get the response we want (the next chunk of block ids following the last one they sent us, or,
|
||||
* get the response we want (the next chunk of block ids following the last one they sent us, or,
|
||||
* failing that, the shortest fork off of the last list of block ids they sent), we need to construct
|
||||
* a synopsis as if our blockchain was made up of:
|
||||
* 1. the blocks in our block chain up to the fork point (if there is a fork) or the head block (if no fork)
|
||||
* 2. the blocks we've already pushed from their fork (if there's a fork)
|
||||
* 3. the block ids they've previously sent us
|
||||
* Segment 3 is handled in the p2p code, it just tells us the number of blocks it has (in
|
||||
* Segment 3 is handled in the p2p code, it just tells us the number of blocks it has (in
|
||||
* number_of_blocks_after_reference_point) so we can leave space in the synopsis for them.
|
||||
* We're responsible for constructing the synopsis of Segments 1 and 2 from our active blockchain and
|
||||
* fork database. The reference_point parameter is the last block from that peer that has been
|
||||
* successfully pushed to the blockchain, so that tells us whether the peer is on a fork or on
|
||||
* the main chain.
|
||||
*/
|
||||
virtual std::vector<item_hash_t> get_blockchain_synopsis(const item_hash_t& reference_point,
|
||||
virtual std::vector<item_hash_t> get_blockchain_synopsis(const item_hash_t& reference_point,
|
||||
uint32_t number_of_blocks_after_reference_point) override
|
||||
{ try {
|
||||
std::vector<item_hash_t> synopsis;
|
||||
|
|
@ -786,13 +792,13 @@ namespace detail {
|
|||
|
||||
if (reference_point_block_num < low_block_num)
|
||||
{
|
||||
// we're on the same fork (at least as far as reference_point) but we've passed
|
||||
// reference point and could no longer undo that far if we diverged after that
|
||||
// we're on the same fork (at least as far as reference_point) but we've passed
|
||||
// reference point and could no longer undo that far if we diverged after that
|
||||
// block. This should probably only happen due to a race condition where
|
||||
// the network thread calls this function, and then immediately pushes a bunch of blocks,
|
||||
// the network thread calls this function, and then immediately pushes a bunch of blocks,
|
||||
// then the main thread finally processes this function.
|
||||
// with the current framework, there's not much we can do to tell the network
|
||||
// thread what our current head block is, so we'll just pretend that
|
||||
// thread what our current head block is, so we'll just pretend that
|
||||
// our head is actually the reference point.
|
||||
// this *may* enable us to fetch blocks that we're unable to push, but that should
|
||||
// be a rare case (and correctly handled)
|
||||
|
|
@ -836,7 +842,7 @@ namespace detail {
|
|||
if (non_fork_high_block_num < low_block_num)
|
||||
{
|
||||
wlog("Unable to generate a usable synopsis because the peer we're generating it for forked too long ago "
|
||||
"(our chains diverge after block #${non_fork_high_block_num} but only undoable to block #${low_block_num})",
|
||||
"(our chains diverge after block #${non_fork_high_block_num} but only undoable to block #${low_block_num})",
|
||||
("low_block_num", low_block_num)
|
||||
("non_fork_high_block_num", non_fork_high_block_num));
|
||||
FC_THROW_EXCEPTION(graphene::net::block_older_than_undo_history, "Peer is are on a fork I'm unable to switch to");
|
||||
|
|
@ -856,11 +862,11 @@ namespace detail {
|
|||
low_block_num = 1;
|
||||
|
||||
// at this point:
|
||||
// low_block_num is the block before the first block we can undo,
|
||||
// low_block_num is the block before the first block we can undo,
|
||||
// non_fork_high_block_num is the block before the fork (if the peer is on a fork, or otherwise it is the same as high_block_num)
|
||||
// high_block_num is the block number of the reference block, or the end of the chain if no reference provided
|
||||
|
||||
// true_high_block_num is the ending block number after the network code appends any item ids it
|
||||
// true_high_block_num is the ending block number after the network code appends any item ids it
|
||||
// knows about that we don't
|
||||
uint32_t true_high_block_num = high_block_num + number_of_blocks_after_reference_point;
|
||||
do
|
||||
|
|
@ -916,12 +922,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();
|
||||
|
|
@ -987,8 +987,6 @@ void application::set_program_options(boost::program_options::options_descriptio
|
|||
("checkpoint,c", bpo::value<vector<string>>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.")
|
||||
("rpc-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8090"), "Endpoint for websocket RPC to listen on")
|
||||
("rpc-tls-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8089"), "Endpoint for TLS websocket RPC to listen on")
|
||||
("enable-permessage-deflate", "Enable support for per-message deflate compression in the websocket servers "
|
||||
"(--rpc-endpoint and --rpc-tls-endpoint), disabled by default")
|
||||
("server-pem,p", bpo::value<string>()->implicit_value("server.pem"), "The TLS certificate file for this server")
|
||||
("server-pem-password,P", bpo::value<string>()->implicit_value(""), "Password for this certificate")
|
||||
("genesis-json", bpo::value<boost::filesystem::path>(), "File to read Genesis State from")
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@
|
|||
|
||||
#define GET_REQUIRED_FEES_MAX_RECURSION 4
|
||||
|
||||
typedef std::map< std::pair<graphene::chain::asset_id_type, graphene::chain::asset_id_type>, std::vector<fc::variant> > market_queue_type;
|
||||
|
||||
namespace graphene { namespace app {
|
||||
|
||||
class database_api_impl;
|
||||
|
|
@ -58,13 +60,14 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
fc::variants get_objects(const vector<object_id_type>& ids)const;
|
||||
|
||||
// Subscriptions
|
||||
void set_subscribe_callback( std::function<void(const variant&)> cb, bool clear_filter );
|
||||
void set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create );
|
||||
void set_pending_transaction_callback( std::function<void(const variant&)> cb );
|
||||
void set_block_applied_callback( std::function<void(const variant& block_id)> cb );
|
||||
void cancel_all_subscriptions();
|
||||
|
||||
// Blocks and transactions
|
||||
optional<block_header> get_block_header(uint32_t block_num)const;
|
||||
map<uint32_t, optional<block_header>> get_block_header_batch(const vector<uint32_t> block_nums)const;
|
||||
optional<signed_block> get_block(uint32_t block_num)const;
|
||||
processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const;
|
||||
|
||||
|
|
@ -74,9 +77,11 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
fc::variant_object get_config()const;
|
||||
chain_id_type get_chain_id()const;
|
||||
dynamic_global_property_object get_dynamic_global_properties()const;
|
||||
global_betting_statistics_object get_global_betting_statistics() const;
|
||||
|
||||
// Keys
|
||||
vector<vector<account_id_type>> get_key_references( vector<public_key_type> key )const;
|
||||
bool is_public_key_registered(string public_key) const;
|
||||
|
||||
// Accounts
|
||||
vector<optional<account_object>> get_accounts(const vector<account_id_type>& account_ids)const;
|
||||
|
|
@ -99,6 +104,15 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
vector<asset_object> list_assets(const string& lower_bound_symbol, uint32_t limit)const;
|
||||
vector<optional<asset_object>> lookup_asset_symbols(const vector<string>& symbols_or_ids)const;
|
||||
|
||||
// Peerplays
|
||||
vector<sport_object> list_sports() const;
|
||||
vector<event_group_object> list_event_groups(sport_id_type sport_id) const;
|
||||
vector<event_object> list_events_in_group(event_group_id_type event_group_id) const;
|
||||
vector<betting_market_group_object> list_betting_market_groups(event_id_type) const;
|
||||
vector<betting_market_object> list_betting_markets(betting_market_group_id_type) const;
|
||||
vector<bet_object> get_unmatched_bets_for_bettor(betting_market_id_type, account_id_type) const;
|
||||
vector<bet_object> get_all_unmatched_bets_for_bettor(account_id_type) const;
|
||||
|
||||
// Markets / feeds
|
||||
vector<limit_order_object> get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const;
|
||||
vector<call_order_object> get_call_orders(asset_id_type a, uint32_t limit)const;
|
||||
|
|
@ -168,22 +182,52 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
{
|
||||
if( !_subscribe_callback )
|
||||
return false;
|
||||
return true;
|
||||
|
||||
return _subscribe_filter.contains( i );
|
||||
}
|
||||
|
||||
bool is_impacted_account( const flat_set<account_id_type>& accounts)
|
||||
{
|
||||
if( !_subscribed_accounts.size() || !accounts.size() )
|
||||
return false;
|
||||
|
||||
return std::any_of(accounts.begin(), accounts.end(), [this](const account_id_type& account) {
|
||||
return _subscribed_accounts.find(account) != _subscribed_accounts.end();
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void enqueue_if_subscribed_to_market(const object* obj, market_queue_type& queue, bool full_object=true)
|
||||
{
|
||||
const T* order = dynamic_cast<const T*>(obj);
|
||||
FC_ASSERT( order != nullptr);
|
||||
|
||||
auto market = order->get_market();
|
||||
|
||||
auto sub = _market_subscriptions.find( market );
|
||||
if( sub != _market_subscriptions.end() ) {
|
||||
queue[market].emplace_back( full_object ? obj->to_variant() : fc::variant(obj->id) );
|
||||
}
|
||||
}
|
||||
|
||||
void broadcast_updates( const vector<variant>& updates );
|
||||
void broadcast_market_updates( const market_queue_type& queue);
|
||||
void handle_object_changed(bool force_notify, bool full_object, const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts, std::function<const object*(object_id_type id)> find_object);
|
||||
|
||||
/** called every time a block is applied to report the objects that were changed */
|
||||
void on_objects_changed(const vector<object_id_type>& ids);
|
||||
void on_objects_removed(const vector<const object*>& objs);
|
||||
void on_objects_new(const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts);
|
||||
void on_objects_changed(const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts);
|
||||
void on_objects_removed(const vector<object_id_type>& ids, const vector<const object*>& objs, const flat_set<account_id_type>& impacted_accounts);
|
||||
void on_applied_block();
|
||||
|
||||
mutable fc::bloom_filter _subscribe_filter;
|
||||
bool _notify_remove_create = false;
|
||||
mutable fc::bloom_filter _subscribe_filter;
|
||||
std::set<account_id_type> _subscribed_accounts;
|
||||
std::function<void(const fc::variant&)> _subscribe_callback;
|
||||
std::function<void(const fc::variant&)> _pending_trx_callback;
|
||||
std::function<void(const fc::variant&)> _block_applied_callback;
|
||||
|
||||
boost::signals2::scoped_connection _new_connection;
|
||||
boost::signals2::scoped_connection _change_connection;
|
||||
boost::signals2::scoped_connection _removed_connection;
|
||||
boost::signals2::scoped_connection _applied_block_connection;
|
||||
|
|
@ -206,11 +250,14 @@ database_api::~database_api() {}
|
|||
database_api_impl::database_api_impl( graphene::chain::database& db ):_db(db)
|
||||
{
|
||||
wlog("creating database api ${x}", ("x",int64_t(this)) );
|
||||
_change_connection = _db.changed_objects.connect([this](const vector<object_id_type>& ids) {
|
||||
on_objects_changed(ids);
|
||||
_new_connection = _db.new_objects.connect([this](const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts) {
|
||||
on_objects_new(ids, impacted_accounts);
|
||||
});
|
||||
_removed_connection = _db.removed_objects.connect([this](const vector<const object*>& objs) {
|
||||
on_objects_removed(objs);
|
||||
_change_connection = _db.changed_objects.connect([this](const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts) {
|
||||
on_objects_changed(ids, impacted_accounts);
|
||||
});
|
||||
_removed_connection = _db.removed_objects.connect([this](const vector<object_id_type>& ids, const vector<const object*>& objs, const flat_set<account_id_type>& impacted_accounts) {
|
||||
on_objects_removed(ids, objs, impacted_accounts);
|
||||
});
|
||||
_applied_block_connection = _db.applied_block.connect([this](const signed_block&){ on_applied_block(); });
|
||||
|
||||
|
|
@ -246,10 +293,6 @@ fc::variants database_api_impl::get_objects(const vector<object_id_type>& ids)co
|
|||
this->subscribe_to_item( id );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
elog( "getObjects without subscribe callback??" );
|
||||
}
|
||||
|
||||
fc::variants result;
|
||||
result.reserve(ids.size());
|
||||
|
|
@ -270,24 +313,24 @@ fc::variants database_api_impl::get_objects(const vector<object_id_type>& ids)co
|
|||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
void database_api::set_subscribe_callback( std::function<void(const variant&)> cb, bool clear_filter )
|
||||
void database_api::set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create )
|
||||
{
|
||||
my->set_subscribe_callback( cb, clear_filter );
|
||||
my->set_subscribe_callback( cb, notify_remove_create );
|
||||
}
|
||||
|
||||
void database_api_impl::set_subscribe_callback( std::function<void(const variant&)> cb, bool clear_filter )
|
||||
void database_api_impl::set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create )
|
||||
{
|
||||
edump((clear_filter));
|
||||
//edump((clear_filter));
|
||||
_subscribe_callback = cb;
|
||||
if( clear_filter || !cb )
|
||||
{
|
||||
static fc::bloom_parameters param;
|
||||
param.projected_element_count = 10000;
|
||||
param.false_positive_probability = 1.0/10000;
|
||||
param.maximum_size = 1024*8*8*2;
|
||||
param.compute_optimal_parameters();
|
||||
_subscribe_filter = fc::bloom_filter(param);
|
||||
}
|
||||
_notify_remove_create = notify_remove_create;
|
||||
_subscribed_accounts.clear();
|
||||
|
||||
static fc::bloom_parameters param;
|
||||
param.projected_element_count = 10000;
|
||||
param.false_positive_probability = 1.0/100;
|
||||
param.maximum_size = 1024*8*8*2;
|
||||
param.compute_optimal_parameters();
|
||||
_subscribe_filter = fc::bloom_filter(param);
|
||||
}
|
||||
|
||||
void database_api::set_pending_transaction_callback( std::function<void(const variant&)> cb )
|
||||
|
|
@ -339,6 +382,20 @@ optional<block_header> database_api_impl::get_block_header(uint32_t block_num) c
|
|||
return *result;
|
||||
return {};
|
||||
}
|
||||
map<uint32_t, optional<block_header>> database_api::get_block_header_batch(const vector<uint32_t> block_nums)const
|
||||
{
|
||||
return my->get_block_header_batch( block_nums );
|
||||
}
|
||||
|
||||
map<uint32_t, optional<block_header>> database_api_impl::get_block_header_batch(const vector<uint32_t> block_nums) const
|
||||
{
|
||||
map<uint32_t, optional<block_header>> results;
|
||||
for (const uint32_t block_num : block_nums)
|
||||
{
|
||||
results[block_num] = get_block_header(block_num);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
optional<signed_block> database_api::get_block(uint32_t block_num)const
|
||||
{
|
||||
|
|
@ -428,6 +485,17 @@ dynamic_global_property_object database_api_impl::get_dynamic_global_properties(
|
|||
return _db.get(dynamic_global_property_id_type());
|
||||
}
|
||||
|
||||
global_betting_statistics_object database_api::get_global_betting_statistics() const
|
||||
{
|
||||
return my->get_global_betting_statistics();
|
||||
}
|
||||
|
||||
global_betting_statistics_object database_api_impl::get_global_betting_statistics() const
|
||||
{
|
||||
return _db.get(global_betting_statistics_id_type());
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Keys //
|
||||
|
|
@ -498,6 +566,35 @@ vector<vector<account_id_type>> database_api_impl::get_key_references( vector<pu
|
|||
return final_result;
|
||||
}
|
||||
|
||||
bool database_api::is_public_key_registered(string public_key) const
|
||||
{
|
||||
return my->is_public_key_registered(public_key);
|
||||
}
|
||||
|
||||
bool database_api_impl::is_public_key_registered(string public_key) const
|
||||
{
|
||||
// Short-circuit
|
||||
if (public_key.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search among all keys using an existing map of *current* account keys
|
||||
public_key_type key;
|
||||
try {
|
||||
key = public_key_type(public_key);
|
||||
} catch ( ... ) {
|
||||
// An invalid public key was detected
|
||||
return false;
|
||||
}
|
||||
const auto& idx = _db.get_index_type<account_index>();
|
||||
const auto& aidx = dynamic_cast<const primary_index<account_index>&>(idx);
|
||||
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
|
||||
auto itr = refs.account_to_key_memberships.find(key);
|
||||
bool is_known = itr != refs.account_to_key_memberships.end();
|
||||
|
||||
return is_known;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Accounts //
|
||||
|
|
@ -551,7 +648,8 @@ std::map<std::string, full_account> database_api_impl::get_full_accounts( const
|
|||
|
||||
if( subscribe )
|
||||
{
|
||||
ilog( "subscribe to ${id}", ("id",account->name) );
|
||||
FC_ASSERT( std::distance(_subscribed_accounts.begin(), _subscribed_accounts.end()) <= 100 );
|
||||
_subscribed_accounts.insert( account->get_id() );
|
||||
subscribe_to_item( account->id );
|
||||
}
|
||||
|
||||
|
|
@ -613,6 +711,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));
|
||||
|
|
@ -895,6 +1012,86 @@ vector<optional<asset_object>> database_api_impl::lookup_asset_symbols(const vec
|
|||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Peerplays //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
vector<sport_object> database_api::list_sports() const
|
||||
{
|
||||
return my->list_sports();
|
||||
}
|
||||
|
||||
vector<sport_object> database_api_impl::list_sports() const
|
||||
{
|
||||
const auto& sport_object_idx = _db.get_index_type<sport_object_index>().indices().get<by_id>();
|
||||
return boost::copy_range<vector<sport_object> >(sport_object_idx);
|
||||
}
|
||||
|
||||
vector<event_group_object> database_api::list_event_groups(sport_id_type sport_id) const
|
||||
{
|
||||
return my->list_event_groups(sport_id);
|
||||
}
|
||||
|
||||
vector<event_group_object> database_api_impl::list_event_groups(sport_id_type sport_id) const
|
||||
{
|
||||
const auto& event_group_idx = _db.get_index_type<event_group_object_index>().indices().get<by_sport_id>();
|
||||
return boost::copy_range<vector<event_group_object> >(event_group_idx.equal_range(sport_id));
|
||||
}
|
||||
|
||||
vector<event_object> database_api::list_events_in_group(event_group_id_type event_group_id) const
|
||||
{
|
||||
return my->list_events_in_group(event_group_id);
|
||||
}
|
||||
|
||||
vector<event_object> database_api_impl::list_events_in_group(event_group_id_type event_group_id) const
|
||||
{
|
||||
const auto& event_idx = _db.get_index_type<event_object_index>().indices().get<by_event_group_id>();
|
||||
return boost::copy_range<vector<event_object> >(event_idx.equal_range(event_group_id));
|
||||
}
|
||||
|
||||
vector<betting_market_group_object> database_api::list_betting_market_groups(event_id_type event_id) const
|
||||
{
|
||||
return my->list_betting_market_groups(event_id);
|
||||
}
|
||||
|
||||
vector<betting_market_group_object> database_api_impl::list_betting_market_groups(event_id_type event_id) const
|
||||
{
|
||||
const auto& betting_market_group_idx = _db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
return boost::copy_range<vector<betting_market_group_object> >(betting_market_group_idx.equal_range(event_id));
|
||||
}
|
||||
|
||||
vector<betting_market_object> database_api::list_betting_markets(betting_market_group_id_type betting_market_group_id) const
|
||||
{
|
||||
return my->list_betting_markets(betting_market_group_id);
|
||||
}
|
||||
|
||||
vector<betting_market_object> database_api_impl::list_betting_markets(betting_market_group_id_type betting_market_group_id) const
|
||||
{
|
||||
const auto& betting_market_idx = _db.get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
||||
return boost::copy_range<vector<betting_market_object> >(betting_market_idx.equal_range(betting_market_group_id));
|
||||
}
|
||||
|
||||
vector<bet_object> database_api::get_unmatched_bets_for_bettor(betting_market_id_type betting_market_id, account_id_type bettor_id) const
|
||||
{
|
||||
return my->get_unmatched_bets_for_bettor(betting_market_id, bettor_id);
|
||||
}
|
||||
|
||||
vector<bet_object> database_api_impl::get_unmatched_bets_for_bettor(betting_market_id_type betting_market_id, account_id_type bettor_id) const
|
||||
{
|
||||
const auto& bet_idx = _db.get_index_type<bet_object_index>().indices().get<by_bettor_and_odds>();
|
||||
return boost::copy_range<vector<bet_object> >(bet_idx.equal_range(std::make_tuple(bettor_id, betting_market_id)));
|
||||
}
|
||||
|
||||
vector<bet_object> database_api::get_all_unmatched_bets_for_bettor(account_id_type bettor_id) const
|
||||
{
|
||||
return my->get_all_unmatched_bets_for_bettor(bettor_id);
|
||||
}
|
||||
|
||||
vector<bet_object> database_api_impl::get_all_unmatched_bets_for_bettor(account_id_type bettor_id) const
|
||||
{
|
||||
const auto& bet_idx = _db.get_index_type<bet_object_index>().indices().get<by_bettor_and_odds>();
|
||||
return boost::copy_range<vector<bet_object> >(bet_idx.equal_range(std::make_tuple(bettor_id)));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Markets / feeds //
|
||||
|
|
@ -1015,117 +1212,84 @@ void database_api_impl::unsubscribe_from_market(asset_id_type a, asset_id_type b
|
|||
|
||||
market_ticker database_api::get_ticker( const string& base, const string& quote )const
|
||||
{
|
||||
return my->get_ticker( base, quote );
|
||||
return my->get_ticker( base, quote );
|
||||
}
|
||||
|
||||
market_ticker database_api_impl::get_ticker( const string& base, const string& quote )const
|
||||
{
|
||||
auto assets = lookup_asset_symbols( {base, quote} );
|
||||
FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
|
||||
FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
|
||||
const auto assets = lookup_asset_symbols( {base, quote} );
|
||||
FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
|
||||
FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
|
||||
|
||||
auto base_id = assets[0]->id;
|
||||
auto quote_id = assets[1]->id;
|
||||
market_ticker result;
|
||||
result.base = base;
|
||||
result.quote = quote;
|
||||
result.latest = 0;
|
||||
result.lowest_ask = 0;
|
||||
result.highest_bid = 0;
|
||||
result.percent_change = 0;
|
||||
result.base_volume = 0;
|
||||
result.quote_volume = 0;
|
||||
|
||||
market_ticker result;
|
||||
try {
|
||||
const fc::time_point_sec now = fc::time_point::now();
|
||||
const fc::time_point_sec yesterday = fc::time_point_sec( now.sec_since_epoch() - 86400 );
|
||||
const auto batch_size = 100;
|
||||
|
||||
result.base = base;
|
||||
result.quote = quote;
|
||||
result.base_volume = 0;
|
||||
result.quote_volume = 0;
|
||||
result.percent_change = 0;
|
||||
result.lowest_ask = 0;
|
||||
result.highest_bid = 0;
|
||||
vector<market_trade> trades = get_trade_history( base, quote, now, yesterday, batch_size );
|
||||
if( !trades.empty() )
|
||||
{
|
||||
result.latest = trades[0].price;
|
||||
|
||||
auto price_to_real = [&]( const share_type a, int p ) { return double( a.value ) / pow( 10, p ); };
|
||||
while( !trades.empty() )
|
||||
{
|
||||
for( const market_trade& t: trades )
|
||||
{
|
||||
result.base_volume += t.value;
|
||||
result.quote_volume += t.amount;
|
||||
}
|
||||
|
||||
try {
|
||||
if( base_id > quote_id ) std::swap(base_id, quote_id);
|
||||
trades = get_trade_history( base, quote, trades.back().date, yesterday, batch_size );
|
||||
}
|
||||
|
||||
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 auto last_trade_yesterday = get_trade_history( base, quote, yesterday, fc::time_point_sec(), 1 );
|
||||
if( !last_trade_yesterday.empty() )
|
||||
{
|
||||
const auto price_yesterday = last_trade_yesterday[0].price;
|
||||
result.percent_change = ( (result.latest / price_yesterday) - 1 ) * 100;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto last_trade = get_trade_history( base, quote, now, fc::time_point_sec(), 1 );
|
||||
if( !last_trade.empty() )
|
||||
result.latest = last_trade[0].price;
|
||||
}
|
||||
|
||||
result.latest = trades[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) )
|
||||
|
||||
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() - 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, 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)
|
||||
{
|
||||
result.lowest_ask = orders.asks[0].price;
|
||||
result.highest_bid = orders.bids[0].price;
|
||||
}
|
||||
|
||||
return result;
|
||||
} FC_CAPTURE_AND_RETHROW( (base)(quote) )
|
||||
return result;
|
||||
}
|
||||
|
||||
market_volume database_api::get_24_volume( const string& base, const string& quote )const
|
||||
{
|
||||
return my->get_24_volume( base, quote );
|
||||
return my->get_24_volume( base, quote );
|
||||
}
|
||||
|
||||
market_volume database_api_impl::get_24_volume( const string& base, const string& quote )const
|
||||
{
|
||||
auto assets = lookup_asset_symbols( {base, quote} );
|
||||
FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
|
||||
FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
|
||||
const auto ticker = get_ticker( base, quote );
|
||||
|
||||
auto base_id = assets[0]->id;
|
||||
auto quote_id = assets[1]->id;
|
||||
market_volume result;
|
||||
result.base = ticker.base;
|
||||
result.quote = ticker.quote;
|
||||
result.base_volume = ticker.base_volume;
|
||||
result.quote_volume = ticker.quote_volume;
|
||||
|
||||
market_volume result;
|
||||
result.base = base;
|
||||
result.quote = quote;
|
||||
result.base_volume = 0;
|
||||
result.quote_volume = 0;
|
||||
|
||||
try {
|
||||
if( base_id > quote_id ) std::swap(base_id, quote_id);
|
||||
|
||||
uint32_t bucket_size = 86400;
|
||||
auto now = fc::time_point_sec( fc::time_point::now() );
|
||||
|
||||
auto trades = get_trade_history( base, quote, now, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 );
|
||||
|
||||
for ( market_trade t: trades )
|
||||
{
|
||||
result.base_volume += t.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;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
} FC_CAPTURE_AND_RETHROW( (base)(quote) )
|
||||
return result;
|
||||
}
|
||||
|
||||
order_book database_api::get_order_book( const string& base, const string& quote, unsigned limit )const
|
||||
|
|
@ -1175,7 +1339,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 );
|
||||
}
|
||||
}
|
||||
|
|
@ -1865,109 +2029,113 @@ vector<tournament_id_type> database_api_impl::get_registered_tournaments(account
|
|||
|
||||
void database_api_impl::broadcast_updates( const vector<variant>& updates )
|
||||
{
|
||||
if( updates.size() ) {
|
||||
if( updates.size() && _subscribe_callback ) {
|
||||
auto capture_this = shared_from_this();
|
||||
fc::async([capture_this,updates](){
|
||||
capture_this->_subscribe_callback( fc::variant(updates) );
|
||||
if(capture_this->_subscribe_callback)
|
||||
capture_this->_subscribe_callback( fc::variant(updates) );
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database_api_impl::on_objects_removed( const vector<const object*>& objs )
|
||||
void database_api_impl::broadcast_market_updates( const market_queue_type& queue)
|
||||
{
|
||||
if( queue.size() )
|
||||
{
|
||||
auto capture_this = shared_from_this();
|
||||
fc::async([capture_this, this, queue](){
|
||||
for( const auto& item : queue )
|
||||
{
|
||||
auto sub = _market_subscriptions.find(item.first);
|
||||
if( sub != _market_subscriptions.end() )
|
||||
sub->second( fc::variant(item.second ) );
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database_api_impl::on_objects_removed( const vector<object_id_type>& ids, const vector<const object*>& objs, const flat_set<account_id_type>& impacted_accounts)
|
||||
{
|
||||
handle_object_changed(_notify_remove_create, false, ids, impacted_accounts,
|
||||
[objs](object_id_type id) -> const object* {
|
||||
auto it = std::find_if(
|
||||
objs.begin(), objs.end(),
|
||||
[id](const object* o) {return o != nullptr && o->id == id;});
|
||||
|
||||
if (it != objs.end())
|
||||
return *it;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void database_api_impl::on_objects_new(const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts)
|
||||
{
|
||||
handle_object_changed(_notify_remove_create, true, ids, impacted_accounts,
|
||||
std::bind(&object_database::find_object, &_db, std::placeholders::_1)
|
||||
);
|
||||
}
|
||||
|
||||
void database_api_impl::on_objects_changed(const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts)
|
||||
{
|
||||
handle_object_changed(false, true, ids, impacted_accounts,
|
||||
std::bind(&object_database::find_object, &_db, std::placeholders::_1)
|
||||
);
|
||||
}
|
||||
|
||||
void database_api_impl::handle_object_changed(bool force_notify, bool full_object, const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts, std::function<const object*(object_id_type id)> find_object)
|
||||
{
|
||||
/// we need to ensure the database_api is not deleted for the life of the async operation
|
||||
if( _subscribe_callback )
|
||||
{
|
||||
vector<variant> updates;
|
||||
updates.reserve(objs.size());
|
||||
vector<variant> updates;
|
||||
|
||||
for( auto obj : objs )
|
||||
updates.emplace_back( obj->id );
|
||||
broadcast_updates( updates );
|
||||
for(auto id : ids)
|
||||
{
|
||||
if( force_notify || is_subscribed_to_item(id) || is_impacted_account(impacted_accounts) )
|
||||
{
|
||||
if( full_object )
|
||||
{
|
||||
auto obj = find_object(id);
|
||||
if( obj )
|
||||
{
|
||||
updates.emplace_back( obj->to_variant() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
updates.emplace_back( id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
broadcast_updates(updates);
|
||||
}
|
||||
|
||||
if( _market_subscriptions.size() )
|
||||
{
|
||||
map< pair<asset_id_type, asset_id_type>, vector<variant> > broadcast_queue;
|
||||
for( const auto& obj : objs )
|
||||
{
|
||||
const limit_order_object* order = dynamic_cast<const limit_order_object*>(obj);
|
||||
if( order )
|
||||
{
|
||||
auto sub = _market_subscriptions.find( order->get_market() );
|
||||
if( sub != _market_subscriptions.end() )
|
||||
broadcast_queue[order->get_market()].emplace_back( order->id );
|
||||
}
|
||||
}
|
||||
if( broadcast_queue.size() )
|
||||
{
|
||||
auto capture_this = shared_from_this();
|
||||
fc::async([capture_this,this,broadcast_queue](){
|
||||
for( const auto& item : broadcast_queue )
|
||||
{
|
||||
auto sub = _market_subscriptions.find(item.first);
|
||||
if( sub != _market_subscriptions.end() )
|
||||
sub->second( fc::variant(item.second ) );
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void database_api_impl::on_objects_changed(const vector<object_id_type>& ids)
|
||||
{
|
||||
vector<variant> updates;
|
||||
map< pair<asset_id_type, asset_id_type>, vector<variant> > market_broadcast_queue;
|
||||
|
||||
for(auto id : ids)
|
||||
{
|
||||
const object* obj = nullptr;
|
||||
if( _subscribe_callback )
|
||||
{
|
||||
obj = _db.find_object( id );
|
||||
if( obj )
|
||||
{
|
||||
updates.emplace_back( obj->to_variant() );
|
||||
}
|
||||
else
|
||||
{
|
||||
updates.emplace_back(id); // send just the id to indicate removal
|
||||
}
|
||||
}
|
||||
|
||||
if( _market_subscriptions.size() )
|
||||
{
|
||||
if( !_subscribe_callback )
|
||||
obj = _db.find_object( id );
|
||||
if( obj )
|
||||
{
|
||||
const limit_order_object* order = dynamic_cast<const limit_order_object*>(obj);
|
||||
if( order )
|
||||
{
|
||||
auto sub = _market_subscriptions.find( order->get_market() );
|
||||
if( sub != _market_subscriptions.end() )
|
||||
market_broadcast_queue[order->get_market()].emplace_back( order->id );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto capture_this = shared_from_this();
|
||||
|
||||
market_queue_type broadcast_queue;
|
||||
/// pushing the future back / popping the prior future if it is complete.
|
||||
/// if a connection hangs then this could get backed up and result in
|
||||
/// a failure to exit cleanly.
|
||||
fc::async([capture_this,this,updates,market_broadcast_queue](){
|
||||
if( _subscribe_callback )
|
||||
_subscribe_callback( updates );
|
||||
//fc::async([capture_this,this,updates,market_broadcast_queue](){
|
||||
//if( _subscribe_callback )
|
||||
// _subscribe_callback( updates );
|
||||
|
||||
for( const auto& item : market_broadcast_queue )
|
||||
for(auto id : ids)
|
||||
{
|
||||
auto sub = _market_subscriptions.find(item.first);
|
||||
if( sub != _market_subscriptions.end() )
|
||||
sub->second( fc::variant(item.second ) );
|
||||
if( id.is<call_order_object>() )
|
||||
{
|
||||
enqueue_if_subscribed_to_market<call_order_object>( find_object(id), broadcast_queue, full_object );
|
||||
}
|
||||
else if( id.is<limit_order_object>() )
|
||||
{
|
||||
enqueue_if_subscribed_to_market<limit_order_object>( find_object(id), broadcast_queue, full_object );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
broadcast_market_updates(broadcast_queue);
|
||||
}
|
||||
}
|
||||
|
||||
/** note: this method cannot yield because it is called in the middle of
|
||||
|
|
|
|||
|
|
@ -208,6 +208,48 @@ struct get_impacted_account_visitor
|
|||
{
|
||||
_impacted.insert( op.account_id );
|
||||
}
|
||||
|
||||
void operator()( const sport_create_operation& op ) {}
|
||||
void operator()( const sport_update_operation& op ) {}
|
||||
void operator()( const event_group_create_operation& op ) {}
|
||||
void operator()( const event_group_update_operation& op ) {}
|
||||
void operator()( const event_create_operation& op ) {}
|
||||
void operator()( const event_update_operation& op ) {}
|
||||
void operator()( const event_update_status_operation& op ) {}
|
||||
void operator()( const betting_market_rules_create_operation& op ) {}
|
||||
void operator()( const betting_market_rules_update_operation& op ) {}
|
||||
void operator()( const betting_market_group_create_operation& op ) {}
|
||||
void operator()( const betting_market_group_update_operation& op ) {}
|
||||
void operator()( const betting_market_create_operation& op ) {}
|
||||
void operator()( const betting_market_update_operation& op ) {}
|
||||
void operator()( const betting_market_group_resolve_operation& op ) {}
|
||||
void operator()( const betting_market_group_cancel_unmatched_bets_operation& op ) {}
|
||||
|
||||
void operator()( const bet_place_operation& op )
|
||||
{
|
||||
_impacted.insert( op.bettor_id );
|
||||
}
|
||||
void operator()( const bet_cancel_operation& op )
|
||||
{
|
||||
_impacted.insert( op.bettor_id );
|
||||
}
|
||||
void operator()( const bet_canceled_operation& op )
|
||||
{
|
||||
_impacted.insert( op.bettor_id );
|
||||
}
|
||||
void operator()( const bet_adjusted_operation& op )
|
||||
{
|
||||
_impacted.insert( op.bettor_id );
|
||||
}
|
||||
void operator()( const bet_matched_operation& op )
|
||||
{
|
||||
_impacted.insert( op.bettor_id );
|
||||
}
|
||||
void operator()( const betting_market_group_resolved_operation& op )
|
||||
{
|
||||
_impacted.insert( op.bettor_id );
|
||||
}
|
||||
|
||||
void operator()( const tournament_create_operation& op )
|
||||
{
|
||||
_impacted.insert( op.creator );
|
||||
|
|
|
|||
|
|
@ -29,8 +29,10 @@
|
|||
#include <graphene/chain/protocol/confidential.hpp>
|
||||
|
||||
#include <graphene/market_history/market_history_plugin.hpp>
|
||||
#include <graphene/accounts_list/accounts_list_plugin.hpp>
|
||||
|
||||
#include <graphene/debug_witness/debug_api.hpp>
|
||||
#include <graphene/bookie/bookie_api.hpp>
|
||||
|
||||
#include <graphene/net/node.hpp>
|
||||
|
||||
|
|
@ -49,6 +51,7 @@
|
|||
namespace graphene { namespace app {
|
||||
using namespace graphene::chain;
|
||||
using namespace graphene::market_history;
|
||||
using namespace graphene::accounts_list;
|
||||
using namespace fc::ecc;
|
||||
using namespace std;
|
||||
|
||||
|
|
@ -71,6 +74,18 @@ namespace graphene { namespace app {
|
|||
string message_out;
|
||||
};
|
||||
|
||||
struct account_asset_balance
|
||||
{
|
||||
string name;
|
||||
account_id_type account_id;
|
||||
share_type amount;
|
||||
};
|
||||
struct asset_holders
|
||||
{
|
||||
asset_id_type asset_id;
|
||||
int count;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The history_api class implements the RPC API for account history
|
||||
*
|
||||
|
|
@ -93,6 +108,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 +144,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 +199,12 @@ namespace graphene { namespace app {
|
|||
*/
|
||||
void broadcast_transaction_with_callback( confirmation_callback cb, const signed_transaction& trx);
|
||||
|
||||
/** this version of broadcast transaction registers a callback method that will be called when the transaction is
|
||||
* included into a block. The callback method includes the transaction id, block number, and transaction number in the
|
||||
* block.
|
||||
*/
|
||||
fc::variant broadcast_transaction_synchronous(const signed_transaction& trx);
|
||||
|
||||
void broadcast_block( const signed_block& block );
|
||||
|
||||
/**
|
||||
|
|
@ -252,6 +306,23 @@ namespace graphene { namespace app {
|
|||
range_proof_info range_get_info( const std::vector<char>& proof );
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*/
|
||||
class asset_api
|
||||
{
|
||||
public:
|
||||
asset_api(graphene::chain::database& db);
|
||||
~asset_api();
|
||||
|
||||
vector<account_asset_balance> get_asset_holders( asset_id_type asset_id, uint32_t start, uint32_t limit )const;
|
||||
int get_asset_holders_count( asset_id_type asset_id )const;
|
||||
vector<asset_holders> get_all_asset_holders() const;
|
||||
|
||||
private:
|
||||
graphene::chain::database& _db;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The login_api class implements the bottom layer of the RPC API
|
||||
*
|
||||
|
|
@ -273,6 +344,8 @@ namespace graphene { namespace app {
|
|||
* has sucessfully authenticated.
|
||||
*/
|
||||
bool login(const string& user, const string& password);
|
||||
/// @brief Retrieve the network block API
|
||||
fc::api<block_api> block()const;
|
||||
/// @brief Retrieve the network broadcast API
|
||||
fc::api<network_broadcast_api> network_broadcast()const;
|
||||
/// @brief Retrieve the database API
|
||||
|
|
@ -283,20 +356,27 @@ namespace graphene { namespace app {
|
|||
fc::api<network_node_api> network_node()const;
|
||||
/// @brief Retrieve the cryptography API
|
||||
fc::api<crypto_api> crypto()const;
|
||||
/// @brief Retrieve the asset API
|
||||
fc::api<asset_api> asset()const;
|
||||
/// @brief Retrieve the debug API (if available)
|
||||
fc::api<graphene::debug_witness::debug_api> debug()const;
|
||||
/// @brief Retrieve the bookie API (if available)
|
||||
fc::api<graphene::bookie::bookie_api> bookie()const;
|
||||
|
||||
private:
|
||||
/// @brief Called to enable an API, not reflected.
|
||||
void enable_api( const string& api_name );
|
||||
private:
|
||||
|
||||
application& _app;
|
||||
optional< fc::api<block_api> > _block_api;
|
||||
optional< fc::api<database_api> > _database_api;
|
||||
optional< fc::api<network_broadcast_api> > _network_broadcast_api;
|
||||
optional< fc::api<network_node_api> > _network_node_api;
|
||||
optional< fc::api<history_api> > _history_api;
|
||||
optional< fc::api<crypto_api> > _crypto_api;
|
||||
optional< fc::api<asset_api> > _asset_api;
|
||||
optional< fc::api<graphene::debug_witness::debug_api> > _debug_api;
|
||||
optional< fc::api<graphene::bookie::bookie_api> > _bookie_api;
|
||||
};
|
||||
|
||||
}} // graphene::app
|
||||
|
|
@ -310,16 +390,25 @@ FC_REFLECT( graphene::app::verify_range_proof_rewind_result,
|
|||
//FC_REFLECT_TYPENAME( fc::ecc::compact_signature );
|
||||
//FC_REFLECT_TYPENAME( fc::ecc::commitment_type );
|
||||
|
||||
FC_REFLECT( graphene::app::account_asset_balance, (name)(account_id)(amount) );
|
||||
FC_REFLECT( graphene::app::asset_holders, (asset_id)(count) );
|
||||
|
||||
FC_API(graphene::app::history_api,
|
||||
(get_account_history)
|
||||
(get_account_history_operations)
|
||||
(get_relative_account_history)
|
||||
(get_fill_order_history)
|
||||
(get_market_history)
|
||||
(get_market_history_buckets)
|
||||
(list_core_accounts)
|
||||
)
|
||||
FC_API(graphene::app::block_api,
|
||||
(get_blocks)
|
||||
)
|
||||
FC_API(graphene::app::network_broadcast_api,
|
||||
(broadcast_transaction)
|
||||
(broadcast_transaction_with_callback)
|
||||
(broadcast_transaction_synchronous)
|
||||
(broadcast_block)
|
||||
)
|
||||
FC_API(graphene::app::network_node_api,
|
||||
|
|
@ -341,12 +430,20 @@ FC_API(graphene::app::crypto_api,
|
|||
(verify_range_proof_rewind)
|
||||
(range_get_info)
|
||||
)
|
||||
FC_API(graphene::app::asset_api,
|
||||
(get_asset_holders)
|
||||
(get_asset_holders_count)
|
||||
(get_all_asset_holders)
|
||||
)
|
||||
FC_API(graphene::app::login_api,
|
||||
(login)
|
||||
(block)
|
||||
(network_broadcast)
|
||||
(database)
|
||||
(history)
|
||||
(network_node)
|
||||
(crypto)
|
||||
(asset)
|
||||
(debug)
|
||||
(bookie)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -38,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 //
|
||||
//////////////
|
||||
|
|
@ -320,6 +343,50 @@ class database_api
|
|||
*/
|
||||
vector<optional<asset_object>> lookup_asset_symbols(const vector<string>& symbols_or_ids)const;
|
||||
|
||||
/////////////////////
|
||||
// Peerplays //
|
||||
/////////////////////
|
||||
|
||||
/**
|
||||
* @brief Get global betting statistics
|
||||
*/
|
||||
global_betting_statistics_object get_global_betting_statistics() const;
|
||||
|
||||
/**
|
||||
* @brief Get a list of all sports
|
||||
*/
|
||||
vector<sport_object> list_sports() const;
|
||||
|
||||
/**
|
||||
* @brief Return a list of all event groups for a sport (e.g. all soccer leagues in soccer)
|
||||
*/
|
||||
vector<event_group_object> list_event_groups(sport_id_type sport_id) const;
|
||||
|
||||
/**
|
||||
* @brief Return a list of all events in an event group
|
||||
*/
|
||||
vector<event_object> list_events_in_group(event_group_id_type event_group_id) const;
|
||||
|
||||
/**
|
||||
* @brief Return a list of all betting market groups for an event
|
||||
*/
|
||||
vector<betting_market_group_object> list_betting_market_groups(event_id_type) const;
|
||||
|
||||
/**
|
||||
* @brief Return a list of all betting markets for a betting market group
|
||||
*/
|
||||
vector<betting_market_object> list_betting_markets(betting_market_group_id_type) const;
|
||||
|
||||
/**
|
||||
* @brief Return a list of all unmatched bets for a given account on a specific betting market
|
||||
*/
|
||||
vector<bet_object> get_unmatched_bets_for_bettor(betting_market_id_type, account_id_type) const;
|
||||
|
||||
/**
|
||||
* @brief Return a list of all unmatched bets for a given account (includes bets on all markets)
|
||||
*/
|
||||
vector<bet_object> get_all_unmatched_bets_for_bettor(account_id_type) const;
|
||||
|
||||
/////////////////////
|
||||
// Markets / feeds //
|
||||
/////////////////////
|
||||
|
|
@ -602,6 +669,7 @@ FC_API(graphene::app::database_api,
|
|||
|
||||
// Blocks and transactions
|
||||
(get_block_header)
|
||||
(get_block_header_batch)
|
||||
(get_block)
|
||||
(get_transaction)
|
||||
(get_recent_transaction_by_id)
|
||||
|
|
@ -615,6 +683,7 @@ FC_API(graphene::app::database_api,
|
|||
|
||||
// Keys
|
||||
(get_key_references)
|
||||
(is_public_key_registered)
|
||||
|
||||
// Accounts
|
||||
(get_accounts)
|
||||
|
|
@ -637,6 +706,16 @@ FC_API(graphene::app::database_api,
|
|||
(list_assets)
|
||||
(lookup_asset_symbols)
|
||||
|
||||
// Peerplays
|
||||
(list_sports)
|
||||
(get_global_betting_statistics)
|
||||
(list_event_groups)
|
||||
(list_events_in_group)
|
||||
(list_betting_market_groups)
|
||||
(list_betting_markets)
|
||||
(get_unmatched_bets_for_bettor)
|
||||
(get_all_unmatched_bets_for_bettor)
|
||||
|
||||
// Markets / feeds
|
||||
(get_order_book)
|
||||
(get_limit_orders)
|
||||
|
|
|
|||
|
|
@ -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,13 +44,17 @@ 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;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::app::full_account,
|
||||
FC_REFLECT( graphene::app::full_account,
|
||||
(account)
|
||||
(statistics)
|
||||
(registrar_name)
|
||||
|
|
@ -61,6 +66,10 @@ FC_REFLECT( graphene::app::full_account,
|
|||
(vesting_balances)
|
||||
(limit_orders)
|
||||
(call_orders)
|
||||
(settle_orders)
|
||||
(proposals)
|
||||
(assets)
|
||||
(withdraws)
|
||||
(proposals)
|
||||
(pending_dividend_payments)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ set_source_files_properties( "${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain
|
|||
add_dependencies( build_hardfork_hpp cat-parts )
|
||||
|
||||
file(GLOB HEADERS "include/graphene/chain/*.hpp")
|
||||
file(GLOB PROTOCOL_HEADERS "include/graphene/chain/protocol/*.hpp")
|
||||
|
||||
if( GRAPHENE_DISABLE_UNITY_BUILD )
|
||||
set( GRAPHENE_DB_FILES
|
||||
db_balance.cpp
|
||||
db_bet.cpp
|
||||
db_block.cpp
|
||||
db_debug.cpp
|
||||
db_getter.cpp
|
||||
|
|
@ -94,7 +96,20 @@ add_library( graphene_chain
|
|||
|
||||
is_authorized_asset.cpp
|
||||
|
||||
protocol/sport.cpp
|
||||
sport_evaluator.cpp
|
||||
protocol/event_group.cpp
|
||||
event_group_evaluator.cpp
|
||||
protocol/event.cpp
|
||||
event_evaluator.cpp
|
||||
event_object.cpp
|
||||
protocol/betting_market.cpp
|
||||
betting_market_evaluator.cpp
|
||||
betting_market_object.cpp
|
||||
betting_market_group_object.cpp
|
||||
|
||||
${HEADERS}
|
||||
${PROTOCOL_HEADERS}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp"
|
||||
)
|
||||
|
||||
|
|
@ -114,3 +129,5 @@ INSTALL( TARGETS
|
|||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/chain" )
|
||||
INSTALL( FILES ${PROTOCOL_HEADERS} DESTINATION "include/graphene/chain/protocol" )
|
||||
|
|
|
|||
393
libraries/chain/betting_market_evaluator.cpp
Normal file
393
libraries/chain/betting_market_evaluator.cpp
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#define DEFAULT_LOGGER "betting"
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
#include <graphene/chain/betting_market_evaluator.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/is_authorized_asset.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result betting_market_rules_create_evaluator::do_evaluate(const betting_market_rules_create_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(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
|
||||
547
libraries/chain/betting_market_group_object.cpp
Normal file
547
libraries/chain/betting_market_group_object.cpp
Normal file
|
|
@ -0,0 +1,547 @@
|
|||
#define DEFAULT_LOGGER "betting"
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <boost/math/common_factor_rt.hpp>
|
||||
|
||||
#include <boost/msm/back/state_machine.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
#include <boost/archive/binary_oarchive.hpp>
|
||||
#include <boost/archive/binary_iarchive.hpp>
|
||||
#include <boost/msm/back/tools.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
enum class betting_market_group_state {
|
||||
upcoming,
|
||||
frozen_upcoming,
|
||||
in_play,
|
||||
frozen_in_play,
|
||||
closed,
|
||||
graded,
|
||||
canceled,
|
||||
settled
|
||||
};
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::betting_market_group_state,
|
||||
(upcoming)
|
||||
(frozen_upcoming)
|
||||
(in_play)
|
||||
(frozen_in_play)
|
||||
(closed)
|
||||
(graded)
|
||||
(canceled)
|
||||
(settled))
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
namespace msm = boost::msm;
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
// betting market object implementation
|
||||
namespace
|
||||
{
|
||||
// Events -- most events happen when the witnesses publish an update operation with a new
|
||||
// status, so if they publish an event with the status set to `frozen`, we'll generate a `frozen_event`
|
||||
struct upcoming_event
|
||||
{
|
||||
database& db;
|
||||
upcoming_event(database& db) : db(db) {}
|
||||
};
|
||||
struct frozen_event
|
||||
{
|
||||
database& db;
|
||||
frozen_event(database& db) : db(db) {}
|
||||
};
|
||||
struct in_play_event
|
||||
{
|
||||
database& db;
|
||||
in_play_event(database& db) : db(db) {}
|
||||
};
|
||||
struct closed_event
|
||||
{
|
||||
database& db;
|
||||
bool closed_by_event;
|
||||
closed_event(database& db, bool closed_by_event) : db(db), closed_by_event(closed_by_event) {}
|
||||
};
|
||||
struct graded_event
|
||||
{
|
||||
database& db;
|
||||
graded_event(database& db) : db(db) {}
|
||||
};
|
||||
struct re_grading_event
|
||||
{
|
||||
database& db;
|
||||
re_grading_event(database& db) : db(db) {}
|
||||
};
|
||||
struct settled_event
|
||||
{
|
||||
database& db;
|
||||
settled_event(database& db) : db(db) {}
|
||||
};
|
||||
struct canceled_event
|
||||
{
|
||||
database& db;
|
||||
|
||||
// true if this was triggered by setting event to canceled state,
|
||||
// false if this was triggered directly on this betting market group
|
||||
bool canceled_by_event;
|
||||
|
||||
canceled_event(database& db, bool canceled_by_event = false) : db(db), canceled_by_event(canceled_by_event) {}
|
||||
};
|
||||
|
||||
// Events
|
||||
struct betting_market_group_state_machine_ : public msm::front::state_machine_def<betting_market_group_state_machine_>
|
||||
{
|
||||
// disable a few state machine features we don't use for performance
|
||||
typedef int no_exception_thrown;
|
||||
typedef int no_message_queue;
|
||||
|
||||
// States
|
||||
struct upcoming : public msm::front::state<>
|
||||
{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> upcoming", ("id", fsm.betting_market_group_obj->id));
|
||||
// when a betting market group goes from frozen -> upcoming, transition the markets from frozen -> unresolved
|
||||
auto& betting_market_index = event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.on_unresolved_event(event.db);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
}
|
||||
}
|
||||
};
|
||||
struct frozen_upcoming : public msm::front::state<>
|
||||
{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> frozen_upcoming", ("id", fsm.betting_market_group_obj->id));
|
||||
|
||||
auto& betting_market_index = event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.on_frozen_event(event.db);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
struct in_play : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> in_play", ("id", fsm.betting_market_group_obj->id));
|
||||
// when an event goes in-play, cancel all unmatched bets in its betting markets
|
||||
auto& betting_market_index = event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.cancel_all_unmatched_bets(event.db);
|
||||
try {
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.on_unresolved_event(event.db);
|
||||
});
|
||||
} catch (const graphene::chain::no_transition&) {
|
||||
// if this wasn't a transition from frozen state, this wasn't necessary
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
struct frozen_in_play : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> frozen_in_play", ("id", fsm.betting_market_group_obj->id));
|
||||
auto& betting_market_index = event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
event.db.modify(betting_market, [&event](betting_market_object& betting_market) {
|
||||
betting_market.on_frozen_event(event.db);
|
||||
});
|
||||
}
|
||||
};
|
||||
struct closed : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& fsm_event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> closed", ("id", fsm.betting_market_group_obj->id));
|
||||
auto& betting_market_index = fsm_event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
fsm_event.db.modify(betting_market, [&fsm_event](betting_market_object& betting_market) {
|
||||
betting_market.cancel_all_unmatched_bets(fsm_event.db);
|
||||
betting_market.on_closed_event(fsm_event.db);
|
||||
});
|
||||
|
||||
// then notify the event that this betting market is now closed so it can change its status accordingly
|
||||
if (!fsm_event.closed_by_event) {
|
||||
const event_object& event = fsm.betting_market_group_obj->event_id(fsm_event.db);
|
||||
fsm_event.db.modify(event, [&fsm_event,&fsm](event_object& event_obj) {
|
||||
event_obj.on_betting_market_group_closed(fsm_event.db, fsm.betting_market_group_obj->id);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
struct graded : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> graded", ("id", fsm.betting_market_group_obj->id));
|
||||
fsm.betting_market_group_obj->settling_time = event.db.head_block_time() + fsm.betting_market_group_obj->delay_before_settling;
|
||||
dlog("grading complete, setting settling time to ${settling_time}", ("settling_time", fsm.betting_market_group_obj->settling_time));
|
||||
}
|
||||
};
|
||||
struct re_grading : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> re_grading", ("id", fsm.betting_market_group_obj->id));
|
||||
}
|
||||
};
|
||||
struct settled : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& fsm_event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> settled", ("id", fsm.betting_market_group_obj->id));
|
||||
// TODO: what triggers this? I guess it will be the blockchain when its settling delay expires. So in that case, it should
|
||||
// trigger the payout in the betting markets
|
||||
auto& betting_market_index = fsm_event.db.template get_index_type<betting_market_object_index>().indices().template get<by_betting_market_group_id>();
|
||||
for (const betting_market_object& betting_market :
|
||||
boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id)))
|
||||
fsm_event.db.modify(betting_market, [&fsm_event](betting_market_object& betting_market) {
|
||||
betting_market.on_settled_event(fsm_event.db);
|
||||
});
|
||||
|
||||
// then notify the event that this betting market is now resolved so it can change its status accordingly
|
||||
const event_object& event = fsm.betting_market_group_obj->event_id(fsm_event.db);
|
||||
fsm_event.db.modify(event, [&fsm_event,&fsm](event_object& event_obj) {
|
||||
event_obj.on_betting_market_group_resolved(fsm_event.db, fsm.betting_market_group_obj->id, false);
|
||||
});
|
||||
}
|
||||
};
|
||||
struct canceled : public msm::front::state<>{
|
||||
void on_entry(const canceled_event& fsm_event, betting_market_group_state_machine_& fsm) {
|
||||
dlog("betting market group ${id} -> canceled", ("id", fsm.betting_market_group_obj->id));
|
||||
auto& betting_market_index = fsm_event.db.get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
||||
auto betting_markets_in_group = boost::make_iterator_range(betting_market_index.equal_range(fsm.betting_market_group_obj->id));
|
||||
|
||||
for (const betting_market_object& betting_market : betting_markets_in_group)
|
||||
fsm_event.db.modify(betting_market, [&fsm_event](betting_market_object& betting_market_obj) {
|
||||
betting_market_obj.on_canceled_event(fsm_event.db);
|
||||
});
|
||||
|
||||
if (!fsm_event.canceled_by_event) {
|
||||
const event_object& event = fsm.betting_market_group_obj->event_id(fsm_event.db);
|
||||
fsm_event.db.modify(event, [&fsm_event,&fsm](event_object& event_obj) {
|
||||
event_obj.on_betting_market_group_resolved(fsm_event.db, fsm.betting_market_group_obj->id, true);
|
||||
});
|
||||
}
|
||||
|
||||
fsm.betting_market_group_obj->settling_time = fsm_event.db.head_block_time();
|
||||
dlog("cancel complete, setting settling time to ${settling_time}", ("settling_time", fsm.betting_market_group_obj->settling_time));
|
||||
}
|
||||
};
|
||||
|
||||
typedef upcoming initial_state;
|
||||
|
||||
// actions
|
||||
void cancel_all_unmatched_bets(const in_play_event& event) {
|
||||
event.db.cancel_all_unmatched_bets_on_betting_market_group(*betting_market_group_obj);
|
||||
}
|
||||
|
||||
// guards
|
||||
bool in_play_is_allowed(const in_play_event& event) {
|
||||
return !betting_market_group_obj->never_in_play;
|
||||
}
|
||||
|
||||
typedef betting_market_group_state_machine_ x; // makes transition table cleaner
|
||||
|
||||
// Transition table for betting market
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < upcoming, frozen_event, frozen_upcoming >,
|
||||
row < upcoming, in_play_event, in_play, &x::cancel_all_unmatched_bets, &x::in_play_is_allowed >,
|
||||
_row < upcoming, closed_event, closed >,
|
||||
_row < upcoming, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < frozen_upcoming, upcoming_event, upcoming >,
|
||||
_row < frozen_upcoming, in_play_event, upcoming >,
|
||||
row < frozen_upcoming, in_play_event, in_play, &x::cancel_all_unmatched_bets, &x::in_play_is_allowed >,
|
||||
_row < frozen_upcoming, closed_event, closed >,
|
||||
_row < frozen_upcoming, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < in_play, frozen_event, frozen_in_play >,
|
||||
_row < in_play, closed_event, closed >,
|
||||
_row < in_play, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < frozen_in_play, in_play_event, in_play >,
|
||||
_row < frozen_in_play, closed_event, closed >,
|
||||
_row < frozen_in_play, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
_row < closed, graded_event, graded >,
|
||||
_row < closed, canceled_event, canceled >,
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
//_row < graded re_grading_event, re_grading >,
|
||||
_row < graded, settled_event, settled >,
|
||||
_row < graded, canceled_event, canceled >
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
//_row < re_grading, graded_event, graded >,
|
||||
//_row < re_grading, canceled_event, canceled >
|
||||
// +-------------------+------------------+---------------------+------------------------------+----------------------+
|
||||
> {};
|
||||
|
||||
template <class Fsm,class Event>
|
||||
void no_transition(Event const& e, Fsm& ,int state)
|
||||
{
|
||||
FC_THROW_EXCEPTION(graphene::chain::no_transition, "No transition");
|
||||
}
|
||||
|
||||
betting_market_group_object* betting_market_group_obj;
|
||||
betting_market_group_state_machine_(betting_market_group_object* betting_market_group_obj) : betting_market_group_obj(betting_market_group_obj) {}
|
||||
};
|
||||
typedef msm::back::state_machine<betting_market_group_state_machine_> betting_market_group_state_machine;
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
class betting_market_group_object::impl {
|
||||
public:
|
||||
betting_market_group_state_machine state_machine;
|
||||
|
||||
impl(betting_market_group_object* self) : state_machine(self) {}
|
||||
};
|
||||
|
||||
betting_market_group_object::betting_market_group_object() :
|
||||
my(new impl(this))
|
||||
{
|
||||
dlog("betting_market_group_object ctor");
|
||||
}
|
||||
|
||||
betting_market_group_object::betting_market_group_object(const betting_market_group_object& rhs) :
|
||||
graphene::db::abstract_object<betting_market_group_object>(rhs),
|
||||
description(rhs.description),
|
||||
event_id(rhs.event_id),
|
||||
rules_id(rhs.rules_id),
|
||||
asset_id(rhs.asset_id),
|
||||
total_matched_bets_amount(rhs.total_matched_bets_amount),
|
||||
never_in_play(rhs.never_in_play),
|
||||
delay_before_settling(rhs.delay_before_settling),
|
||||
settling_time(rhs.settling_time),
|
||||
my(new impl(this))
|
||||
{
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.betting_market_group_obj = this;
|
||||
}
|
||||
|
||||
betting_market_group_object& betting_market_group_object::operator=(const betting_market_group_object& rhs)
|
||||
{
|
||||
//graphene::db::abstract_object<betting_market_group_object>::operator=(rhs);
|
||||
id = rhs.id;
|
||||
description = rhs.description;
|
||||
event_id = rhs.event_id;
|
||||
rules_id = rhs.rules_id;
|
||||
asset_id = rhs.asset_id;
|
||||
total_matched_bets_amount = rhs.total_matched_bets_amount;
|
||||
never_in_play = rhs.never_in_play;
|
||||
delay_before_settling = rhs.delay_before_settling;
|
||||
settling_time = rhs.settling_time;
|
||||
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.betting_market_group_obj = this;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
betting_market_group_object::~betting_market_group_object()
|
||||
{
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool verify_betting_market_group_status_constants()
|
||||
{
|
||||
unsigned error_count = 0;
|
||||
typedef msm::back::generate_state_set<betting_market_group_state_machine::stt>::type all_states;
|
||||
static char const* filled_state_names[mpl::size<all_states>::value];
|
||||
mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> >
|
||||
(msm::back::fill_state_names<betting_market_group_state_machine::stt>(filled_state_names));
|
||||
for (unsigned i = 0; i < mpl::size<all_states>::value; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
// this is an approximate test, the state name provided by typeinfo will be mangled, but should
|
||||
// at least contain the string we're looking for
|
||||
const char* fc_reflected_value_name = fc::reflector<betting_market_group_state>::to_string((betting_market_group_state)i);
|
||||
if (!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
|
||||
|
||||
450
libraries/chain/betting_market_object.cpp
Normal file
450
libraries/chain/betting_market_object.cpp
Normal file
|
|
@ -0,0 +1,450 @@
|
|||
#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;
|
||||
}
|
||||
};
|
||||
|
||||
typedef unresolved initial_state;
|
||||
typedef betting_market_state_machine_ x; // makes transition table cleaner
|
||||
|
||||
|
||||
// Transition table for betting market
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < unresolved, frozen_event, frozen >,
|
||||
_row < unresolved, closed_event, closed >,
|
||||
_row < unresolved, canceled_event, canceled >,
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < frozen, unresolved_event, unresolved >,
|
||||
_row < frozen, closed_event, closed >,
|
||||
_row < frozen, canceled_event, canceled >,
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < closed, graded_event, graded >,
|
||||
_row < closed, canceled_event, canceled >,
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
_row < graded, settled_event, settled >,
|
||||
_row < graded, canceled_event, canceled >
|
||||
// +---------------------------+-----------------------------+----------------------------+---------------------+----------------------+
|
||||
> {};
|
||||
|
||||
template <class Fsm,class Event>
|
||||
void no_transition(Event const& e, Fsm& ,int state)
|
||||
{
|
||||
FC_THROW_EXCEPTION(graphene::chain::no_transition, "No transition");
|
||||
}
|
||||
|
||||
betting_market_object* betting_market_obj;
|
||||
betting_market_state_machine_(betting_market_object* betting_market_obj) : betting_market_obj(betting_market_obj) {}
|
||||
};
|
||||
typedef msm::back::state_machine<betting_market_state_machine_> betting_market_state_machine;
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
class betting_market_object::impl {
|
||||
public:
|
||||
betting_market_state_machine state_machine;
|
||||
|
||||
impl(betting_market_object* self) : state_machine(self) {}
|
||||
};
|
||||
|
||||
betting_market_object::betting_market_object() :
|
||||
my(new impl(this))
|
||||
{
|
||||
}
|
||||
|
||||
betting_market_object::betting_market_object(const betting_market_object& rhs) :
|
||||
graphene::db::abstract_object<betting_market_object>(rhs),
|
||||
group_id(rhs.group_id),
|
||||
description(rhs.description),
|
||||
payout_condition(rhs.payout_condition),
|
||||
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::pack_impl(std::ostream& stream) const
|
||||
{
|
||||
boost::archive::binary_oarchive oa(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
|
||||
oa << my->state_machine;
|
||||
}
|
||||
|
||||
void betting_market_object::unpack_impl(std::istream& stream)
|
||||
{
|
||||
boost::archive::binary_iarchive ia(stream, boost::archive::no_header|boost::archive::no_codecvt|boost::archive::no_xml_tag_checking);
|
||||
ia >> my->state_machine;
|
||||
}
|
||||
|
||||
void betting_market_object::on_unresolved_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(unresolved_event(db));
|
||||
}
|
||||
|
||||
void betting_market_object::on_frozen_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(frozen_event(db));
|
||||
}
|
||||
|
||||
void betting_market_object::on_closed_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(closed_event(db));
|
||||
}
|
||||
|
||||
void betting_market_object::on_graded_event(database& db, betting_market_resolution_type new_grading)
|
||||
{
|
||||
my->state_machine.process_event(graded_event(db, new_grading));
|
||||
}
|
||||
|
||||
void betting_market_object::on_settled_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(settled_event(db));
|
||||
}
|
||||
|
||||
void betting_market_object::on_canceled_event(database& db)
|
||||
{
|
||||
my->state_machine.process_event(canceled_event(db));
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
namespace fc {
|
||||
// Manually reflect betting_market_object to variant to properly reflect "state"
|
||||
void to_variant(const graphene::chain::betting_market_object& event_obj, fc::variant& v)
|
||||
{
|
||||
fc::mutable_variant_object o;
|
||||
o("id", event_obj.id)
|
||||
("group_id", event_obj.group_id)
|
||||
("description", event_obj.description)
|
||||
("payout_condition", event_obj.payout_condition)
|
||||
("resolution", event_obj.resolution)
|
||||
("status", event_obj.get_status());
|
||||
|
||||
v = o;
|
||||
}
|
||||
|
||||
// Manually reflect betting_market_object to variant to properly reflect "state"
|
||||
void from_variant(const fc::variant& v, graphene::chain::betting_market_object& event_obj)
|
||||
{
|
||||
event_obj.id = v["id"].as<graphene::chain::betting_market_id_type>();
|
||||
event_obj.group_id = v["name"].as<graphene::chain::betting_market_group_id_type>();
|
||||
event_obj.description = v["description"].as<graphene::chain::internationalized_string_type>();
|
||||
event_obj.payout_condition = v["payout_condition"].as<graphene::chain::internationalized_string_type>();
|
||||
event_obj.resolution = v["resolution"].as<fc::optional<graphene::chain::betting_market_resolution_type>>();
|
||||
graphene::chain::betting_market_status status = v["status"].as<graphene::chain::betting_market_status>();
|
||||
const_cast<int*>(event_obj.my->state_machine.current_state())[0] = (int)status;
|
||||
}
|
||||
} //end namespace fc
|
||||
|
||||
|
|
@ -29,6 +29,8 @@
|
|||
#include <graphene/chain/fba_accumulator_id.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_operation& o )
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include "db_balance.cpp"
|
||||
#include "db_bet.cpp"
|
||||
#include "db_block.cpp"
|
||||
#include "db_debug.cpp"
|
||||
#include "db_getter.cpp"
|
||||
|
|
@ -32,3 +33,4 @@
|
|||
#include "db_market.cpp"
|
||||
#include "db_update.cpp"
|
||||
#include "db_witness_schedule.cpp"
|
||||
#include "db_notify.cpp"
|
||||
613
libraries/chain/db_bet.cpp
Normal file
613
libraries/chain/db_bet.cpp
Normal file
|
|
@ -0,0 +1,613 @@
|
|||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <boost/range/combine.hpp>
|
||||
#include <boost/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;
|
||||
}
|
||||
|
||||
// collect the resolutions of all markets in the BMG: they were previously published and
|
||||
// stored in the individual betting markets
|
||||
std::map<betting_market_id_type, betting_market_resolution_type> resolutions_by_market_id;
|
||||
|
||||
// collecting bettors and their positions
|
||||
std::map<account_id_type, std::vector<const betting_market_position_object*> > bettor_positions_map;
|
||||
|
||||
auto& betting_market_index = get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
||||
// [ROL] it seems to be my mistake - wrong index used
|
||||
//auto& position_index = get_index_type<betting_market_position_index>().indices().get<by_bettor_betting_market>();
|
||||
auto& position_index = get_index_type<betting_market_position_index>().indices().get<by_betting_market_bettor>();
|
||||
auto betting_market_itr = betting_market_index.lower_bound(betting_market_group.id);
|
||||
while (betting_market_itr != betting_market_index.end() && betting_market_itr->group_id == betting_market_group.id)
|
||||
{
|
||||
const betting_market_object& betting_market = *betting_market_itr;
|
||||
FC_ASSERT(betting_market_itr->resolution, "Unexpected error settling betting market ${market_id}: no published resolution",
|
||||
("market_id", betting_market_itr->id));
|
||||
resolutions_by_market_id.emplace(betting_market.id, *betting_market_itr->resolution);
|
||||
|
||||
++betting_market_itr;
|
||||
cancel_all_unmatched_bets_on_betting_market(betting_market);
|
||||
|
||||
auto position_itr = position_index.lower_bound(betting_market.id);
|
||||
|
||||
while (position_itr != position_index.end() && position_itr->betting_market_id == betting_market.id)
|
||||
{
|
||||
const betting_market_position_object& position = *position_itr;
|
||||
++position_itr;
|
||||
|
||||
bettor_positions_map[position.bettor_id].push_back(&position);
|
||||
}
|
||||
}
|
||||
|
||||
// walking through bettors' positions and collecting winings and fees respecting asset_id
|
||||
for (const auto& bettor_positions_pair: bettor_positions_map)
|
||||
{
|
||||
uint16_t rake_fee_percentage = get_global_properties().parameters.betting_rake_fee_percentage;
|
||||
share_type net_profits;
|
||||
share_type payout_amounts;
|
||||
account_id_type bettor_id = bettor_positions_pair.first;
|
||||
const std::vector<const betting_market_position_object*>& bettor_positions = bettor_positions_pair.second;
|
||||
|
||||
for (const betting_market_position_object* position : bettor_positions)
|
||||
{
|
||||
betting_market_resolution_type resolution;
|
||||
try
|
||||
{
|
||||
resolution = resolutions_by_market_id.at(position->betting_market_id);
|
||||
}
|
||||
catch (std::out_of_range&)
|
||||
{
|
||||
FC_THROW_EXCEPTION(fc::key_not_found_exception, "Unexpected betting market ID, shouldn't happen");
|
||||
}
|
||||
|
||||
///if (cancel)
|
||||
/// resolution = betting_market_resolution_type::cancel;
|
||||
///else
|
||||
///{
|
||||
/// // checked in evaluator, should never happen, see above
|
||||
/// assert(resolutions.count(position->betting_market_id));
|
||||
/// resolution = resolutions.at(position->betting_market_id);
|
||||
///}
|
||||
|
||||
|
||||
switch (resolution)
|
||||
{
|
||||
case betting_market_resolution_type::win:
|
||||
{
|
||||
share_type total_payout = position->pay_if_payout_condition + position->pay_if_not_canceled;
|
||||
payout_amounts += total_payout;
|
||||
net_profits += total_payout - position->pay_if_canceled;
|
||||
break;
|
||||
}
|
||||
case betting_market_resolution_type::not_win:
|
||||
{
|
||||
share_type total_payout = position->pay_if_not_payout_condition + position->pay_if_not_canceled;
|
||||
payout_amounts += total_payout;
|
||||
net_profits += total_payout - position->pay_if_canceled;
|
||||
break;
|
||||
}
|
||||
case betting_market_resolution_type::cancel:
|
||||
payout_amounts += position->pay_if_canceled;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
remove(*position);
|
||||
}
|
||||
|
||||
// pay the fees to the dividend-distribution account if net profit
|
||||
share_type rake_amount;
|
||||
if (net_profits.value > 0 && rake_account_id)
|
||||
{
|
||||
rake_amount = ((fc::uint128_t(net_profits.value) * rake_fee_percentage + GRAPHENE_100_PERCENT - 1) / GRAPHENE_100_PERCENT).to_uint64();
|
||||
if (rake_amount.value)
|
||||
adjust_balance(*rake_account_id, asset(rake_amount, betting_market_group.asset_id));
|
||||
}
|
||||
|
||||
// pay winning - rake
|
||||
adjust_balance(bettor_id, asset(payout_amounts - rake_amount, betting_market_group.asset_id));
|
||||
// [ROL]
|
||||
//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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
@ -29,6 +29,7 @@
|
|||
#include <graphene/chain/block_summary_object.hpp>
|
||||
#include <graphene/chain/global_property_object.hpp>
|
||||
#include <graphene/chain/operation_history_object.hpp>
|
||||
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/transaction_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
|
|
@ -241,7 +242,7 @@ processed_transaction database::_push_transaction( const signed_transaction& trx
|
|||
auto processed_trx = _apply_transaction( trx );
|
||||
_pending_tx.push_back(processed_trx);
|
||||
|
||||
notify_changed_objects();
|
||||
// notify_changed_objects();
|
||||
// The transaction applied successfully. Merge its changes into the pending block session.
|
||||
temp_session.merge();
|
||||
|
||||
|
|
@ -285,13 +286,13 @@ processed_transaction database::push_proposal(const proposal_object& proposal)
|
|||
{
|
||||
_applied_ops.resize( old_applied_ops_size );
|
||||
}
|
||||
elog( "e", ("e",e.to_detail_string() ) );
|
||||
edump((e));
|
||||
throw;
|
||||
}
|
||||
|
||||
ptrx.operation_results = std::move(eval_state.operation_results);
|
||||
return ptrx;
|
||||
} FC_CAPTURE_AND_RETHROW( (proposal) ) }
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
signed_block database::generate_block(
|
||||
fc::time_point_sec when,
|
||||
|
|
@ -478,6 +479,10 @@ const vector<optional< operation_history_object > >& database::get_applied_opera
|
|||
return _applied_ops;
|
||||
}
|
||||
|
||||
vector<optional< operation_history_object > >& database::get_applied_operations()
|
||||
{
|
||||
return _applied_ops;
|
||||
}
|
||||
//////////////////// private methods ////////////////////
|
||||
|
||||
void database::apply_block( const signed_block& next_block, uint32_t skip )
|
||||
|
|
@ -539,12 +544,14 @@ void database::_apply_block( const signed_block& next_block )
|
|||
perform_chain_maintenance(next_block, global_props);
|
||||
|
||||
create_block_summary(next_block);
|
||||
place_delayed_bets(); // must happen after update_global_dynamic_data() updates the time
|
||||
clear_expired_transactions();
|
||||
clear_expired_proposals();
|
||||
clear_expired_orders();
|
||||
update_expired_feeds();
|
||||
update_withdraw_permissions();
|
||||
update_tournaments();
|
||||
update_betting_markets(next_block.timestamp);
|
||||
|
||||
// n.b., update_maintenance_flag() happens this late
|
||||
// because get_slot_time() / get_slot_at_time() is needed above
|
||||
|
|
@ -562,27 +569,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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ const chain_property_object& database::get_chain_properties()const
|
|||
return get( chain_property_id_type() );
|
||||
}
|
||||
|
||||
const dynamic_global_property_object&database::get_dynamic_global_properties() const
|
||||
const dynamic_global_property_object& database::get_dynamic_global_properties() const
|
||||
{
|
||||
return get( dynamic_global_property_id_type() );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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/assert_evaluator.hpp>
|
||||
|
|
@ -64,6 +71,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>
|
||||
|
|
@ -130,6 +141,33 @@ const uint8_t witness_object::type_id;
|
|||
const uint8_t worker_object::space_id;
|
||||
const uint8_t worker_object::type_id;
|
||||
|
||||
const uint8_t sport_object::space_id;
|
||||
const uint8_t sport_object::type_id;
|
||||
|
||||
const uint8_t event_group_object::space_id;
|
||||
const uint8_t event_group_object::type_id;
|
||||
|
||||
const uint8_t event_object::space_id;
|
||||
const uint8_t event_object::type_id;
|
||||
|
||||
const uint8_t betting_market_rules_object::space_id;
|
||||
const uint8_t betting_market_rules_object::type_id;
|
||||
|
||||
const uint8_t betting_market_group_object::space_id;
|
||||
const uint8_t betting_market_group_object::type_id;
|
||||
|
||||
const uint8_t betting_market_object::space_id;
|
||||
const uint8_t betting_market_object::type_id;
|
||||
|
||||
const uint8_t bet_object::space_id;
|
||||
const uint8_t bet_object::type_id;
|
||||
|
||||
const uint8_t betting_market_position_object::space_id;
|
||||
const uint8_t betting_market_position_object::type_id;
|
||||
|
||||
const uint8_t global_betting_statistics_object::space_id;
|
||||
const uint8_t global_betting_statistics_object::type_id;
|
||||
|
||||
|
||||
void database::initialize_evaluators()
|
||||
{
|
||||
|
|
@ -176,6 +214,23 @@ void database::initialize_evaluators()
|
|||
register_evaluator<transfer_from_blind_evaluator>();
|
||||
register_evaluator<blind_transfer_evaluator>();
|
||||
register_evaluator<asset_claim_fees_evaluator>();
|
||||
register_evaluator<sport_create_evaluator>();
|
||||
register_evaluator<sport_update_evaluator>();
|
||||
register_evaluator<event_group_create_evaluator>();
|
||||
register_evaluator<event_group_update_evaluator>();
|
||||
register_evaluator<event_create_evaluator>();
|
||||
register_evaluator<event_update_evaluator>();
|
||||
register_evaluator<event_update_status_evaluator>();
|
||||
register_evaluator<betting_market_rules_create_evaluator>();
|
||||
register_evaluator<betting_market_rules_update_evaluator>();
|
||||
register_evaluator<betting_market_group_create_evaluator>();
|
||||
register_evaluator<betting_market_group_update_evaluator>();
|
||||
register_evaluator<betting_market_create_evaluator>();
|
||||
register_evaluator<betting_market_update_evaluator>();
|
||||
register_evaluator<bet_place_evaluator>();
|
||||
register_evaluator<bet_cancel_evaluator>();
|
||||
register_evaluator<betting_market_group_resolve_evaluator>();
|
||||
register_evaluator<betting_market_group_cancel_unmatched_bets_evaluator>();
|
||||
register_evaluator<tournament_create_evaluator>();
|
||||
register_evaluator<tournament_join_evaluator>();
|
||||
register_evaluator<game_move_evaluator>();
|
||||
|
|
@ -208,6 +263,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> >();
|
||||
|
|
@ -230,8 +292,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 > >();
|
||||
}
|
||||
|
|
@ -329,7 +394,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;
|
||||
|
|
@ -337,7 +402,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 )
|
||||
{
|
||||
|
|
@ -368,9 +433,11 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
create<asset_dividend_data_object>([&](asset_dividend_data_object& a) {
|
||||
a.options.minimum_distribution_interval = 3*24*60*60;
|
||||
a.options.minimum_fee_percentage = 10*GRAPHENE_1_PERCENT;
|
||||
a.options.next_payout_time = genesis_state.initial_timestamp + fc::days(1);
|
||||
a.options.payout_interval = 30*24*60*60;
|
||||
a.dividend_distribution_account = TOURNAMENT_RAKE_FEE_ACCOUNT_ID;
|
||||
a.options.next_payout_time = genesis_state.initial_timestamp + fc::hours(1);
|
||||
a.options.payout_interval = 7*24*60*60;
|
||||
a.dividend_distribution_account = GRAPHENE_RAKE_FEE_ACCOUNT_ID;
|
||||
//a.options.next_payout_time = genesis_state.initial_timestamp + fc::days(1);
|
||||
//a.options.payout_interval = 30*24*60*60;
|
||||
});
|
||||
|
||||
const asset_object& core_asset =
|
||||
|
|
@ -387,7 +454,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) );
|
||||
|
||||
|
|
@ -403,18 +470,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;
|
||||
|
|
@ -424,6 +491,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 )
|
||||
{
|
||||
|
|
@ -467,6 +535,9 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
p.witness_budget = 0;
|
||||
p.recent_slots_filled = fc::uint128::max_value();
|
||||
});
|
||||
create<global_betting_statistics_object>([&](global_betting_statistics_object& betting_statistics) {
|
||||
betting_statistics.number_of_active_events = 0;
|
||||
});
|
||||
|
||||
FC_ASSERT( (genesis_state.immutable_parameters.min_witness_count & 1) == 1, "min_witness_count must be odd" );
|
||||
FC_ASSERT( (genesis_state.immutable_parameters.min_committee_member_count & 1) == 1, "min_committee_member_count must be odd" );
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@
|
|||
#include <graphene/chain/witness_schedule_object.hpp>
|
||||
#include <graphene/chain/worker_object.hpp>
|
||||
|
||||
#define USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX // vesting_balance_object by_asset_balance index needed
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
template<class Index>
|
||||
|
|
@ -226,7 +228,7 @@ void database::update_active_witnesses()
|
|||
{
|
||||
vote_counter vc;
|
||||
for( const witness_object& wit : wits )
|
||||
vc.add( wit.witness_account, _vote_tally_buffer[wit.vote_id] );
|
||||
vc.add( wit.witness_account, std::max(_vote_tally_buffer[wit.vote_id], UINT64_C(1)) );
|
||||
vc.finish( a.active );
|
||||
}
|
||||
} );
|
||||
|
|
@ -308,7 +310,7 @@ void database::update_active_committee_members()
|
|||
{
|
||||
vote_counter vc;
|
||||
for( const committee_member_object& cm : committee_members )
|
||||
vc.add( cm.committee_member_account, _vote_tally_buffer[cm.vote_id] );
|
||||
vc.add( cm.committee_member_account, std::max(_vote_tally_buffer[cm.vote_id], UINT64_C(1)) );
|
||||
vc.finish( a.active );
|
||||
}
|
||||
} );
|
||||
|
|
@ -735,7 +737,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
const vesting_balance_index& vesting_index,
|
||||
const total_distributed_dividend_balance_object_index& distributed_dividend_balance_index,
|
||||
const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index)
|
||||
{
|
||||
{ try {
|
||||
dlog("Processing dividend payments for dividend holder asset type ${holder_asset} at time ${t}",
|
||||
("holder_asset", dividend_holder_asset_obj.symbol)("t", db.head_block_time()));
|
||||
auto current_distribution_account_balance_range =
|
||||
|
|
@ -759,6 +761,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
uint64_t total_fee_per_asset_in_core = distribution_base_fee + holder_account_count * (uint64_t)distribution_fee_per_holder;
|
||||
|
||||
std::map<account_id_type, share_type> vesting_amounts;
|
||||
#ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX
|
||||
// get only once a collection of accounts that hold nonzero vesting balances of the dividend asset
|
||||
auto vesting_balances_begin =
|
||||
vesting_index.indices().get<by_asset_balance>().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id));
|
||||
|
|
@ -771,6 +774,20 @@ void schedule_pending_dividend_balances(database& db,
|
|||
// ("owner", vesting_balance_obj.owner(db).name)
|
||||
// ("amount", vesting_balance_obj.balance.amount));
|
||||
}
|
||||
#else
|
||||
// get only once a collection of accounts that hold nonzero vesting balances of the dividend asset
|
||||
const auto& vesting_balances = vesting_index.indices().get<by_id>();
|
||||
for (const vesting_balance_object& vesting_balance_obj : vesting_balances)
|
||||
{
|
||||
if (vesting_balance_obj.balance.asset_id == dividend_holder_asset_obj.id && vesting_balance_obj.balance.amount)
|
||||
{
|
||||
vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount;
|
||||
dlog("Vesting balance for account: ${owner}, amount: ${amount}",
|
||||
("owner", vesting_balance_obj.owner(db).name)
|
||||
("amount", vesting_balance_obj.balance.amount));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
auto current_distribution_account_balance_iter = current_distribution_account_balance_range.first;
|
||||
auto previous_distribution_account_balance_iter = previous_distribution_account_balance_range.first;
|
||||
|
|
@ -1042,10 +1059,10 @@ void schedule_pending_dividend_balances(database& db,
|
|||
dividend_data_obj.last_distribution_time = current_head_block_time;
|
||||
});
|
||||
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void process_dividend_assets(database& db)
|
||||
{
|
||||
{ try {
|
||||
ilog("In process_dividend_assets time ${time}", ("time", db.head_block_time()));
|
||||
|
||||
const account_balance_index& balance_index = db.get_index_type<account_balance_index>();
|
||||
|
|
@ -1067,143 +1084,146 @@ void process_dividend_assets(database& db)
|
|||
if (dividend_data.options.next_payout_time &&
|
||||
db.head_block_time() >= *dividend_data.options.next_payout_time)
|
||||
{
|
||||
dlog("Dividend payout time has arrived for asset ${holder_asset}",
|
||||
("holder_asset", dividend_holder_asset_obj.symbol));
|
||||
|
||||
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>();
|
||||
auto holder_account_balance_range = balance_idx.equal_range(boost::make_tuple(dividend_data.dividend_distribution_account));
|
||||
for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_account_balance_range.first, holder_account_balance_range.second))
|
||||
ilog(" Current balance: ${asset}", ("asset", asset(holder_balance_object.balance, holder_balance_object.asset_type)));
|
||||
// dump balances before the payouts for debugging
|
||||
const auto& balance_idx = db.get_index_type<account_balance_index>().indices().get<by_account_asset>();
|
||||
auto holder_account_balance_range = balance_idx.equal_range(boost::make_tuple(dividend_data.dividend_distribution_account));
|
||||
for (const account_balance_object& holder_balance_object : boost::make_iterator_range(holder_account_balance_range.first, holder_account_balance_range.second))
|
||||
ilog(" Current balance: ${asset}", ("asset", asset(holder_balance_object.balance, holder_balance_object.asset_type)));
|
||||
#endif
|
||||
|
||||
// when we do the payouts, we first increase the balances in all of the receiving accounts
|
||||
// and use this map to keep track of the total amount of each asset paid out.
|
||||
// Afterwards, we decrease the distribution account's balance by the total amount paid out,
|
||||
// and modify the distributed_balances accordingly
|
||||
std::map<asset_id_type, share_type> amounts_paid_out_by_asset;
|
||||
// when we do the payouts, we first increase the balances in all of the receiving accounts
|
||||
// and use this map to keep track of the total amount of each asset paid out.
|
||||
// Afterwards, we decrease the distribution account's balance by the total amount paid out,
|
||||
// and modify the distributed_balances accordingly
|
||||
std::map<asset_id_type, share_type> amounts_paid_out_by_asset;
|
||||
|
||||
auto pending_payouts_range =
|
||||
pending_payout_balance_index.indices().get<by_dividend_account_payout>().equal_range(boost::make_tuple(dividend_holder_asset_obj.id));
|
||||
// the pending_payouts_range is all payouts for this dividend asset, sorted by the holder's account
|
||||
// we iterate in this order so we can build up a list of payouts for each account to put in the
|
||||
// virtual op
|
||||
flat_set<asset> payouts_for_this_holder;
|
||||
fc::optional<account_id_type> last_holder_account_id;
|
||||
auto pending_payouts_range =
|
||||
pending_payout_balance_index.indices().get<by_dividend_account_payout>().equal_range(boost::make_tuple(dividend_holder_asset_obj.id));
|
||||
// the pending_payouts_range is all payouts for this dividend asset, sorted by the holder's account
|
||||
// we iterate in this order so we can build up a list of payouts for each account to put in the
|
||||
// virtual op
|
||||
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
|
||||
// for these often
|
||||
flat_map<asset_id_type, bool> approved_assets; // assets that the dividend distribution account is authorized to send/receive
|
||||
auto is_asset_approved_for_distribution_account = [&](const asset_id_type& asset_id) {
|
||||
auto approved_assets_iter = approved_assets.find(asset_id);
|
||||
if (approved_assets_iter != approved_assets.end())
|
||||
return approved_assets_iter->second;
|
||||
bool is_approved = is_authorized_asset(db, dividend_distribution_account_object,
|
||||
asset_id(db));
|
||||
approved_assets[asset_id] = is_approved;
|
||||
return is_approved;
|
||||
};
|
||||
// cache the assets the distribution account is approved to send, we will be asking
|
||||
// for these often
|
||||
flat_map<asset_id_type, bool> approved_assets; // assets that the dividend distribution account is authorized to send/receive
|
||||
auto is_asset_approved_for_distribution_account = [&](const asset_id_type& asset_id) {
|
||||
auto approved_assets_iter = approved_assets.find(asset_id);
|
||||
if (approved_assets_iter != approved_assets.end())
|
||||
return approved_assets_iter->second;
|
||||
bool is_approved = is_authorized_asset(db, dividend_distribution_account_object,
|
||||
asset_id(db));
|
||||
approved_assets[asset_id] = is_approved;
|
||||
return is_approved;
|
||||
};
|
||||
|
||||
for (auto pending_balance_object_iter = pending_payouts_range.first; pending_balance_object_iter != pending_payouts_range.second; )
|
||||
{
|
||||
const pending_dividend_payout_balance_for_holder_object& pending_balance_object = *pending_balance_object_iter;
|
||||
for (auto pending_balance_object_iter = pending_payouts_range.first; pending_balance_object_iter != pending_payouts_range.second; )
|
||||
{
|
||||
const pending_dividend_payout_balance_for_holder_object& pending_balance_object = *pending_balance_object_iter;
|
||||
|
||||
if (last_holder_account_id && *last_holder_account_id != pending_balance_object.owner && payouts_for_this_holder.size())
|
||||
if (last_holder_account_id && *last_holder_account_id != pending_balance_object.owner && payouts_for_this_holder.size())
|
||||
{
|
||||
// we've moved on to a new account, generate the dividend payment virtual op for the previous one
|
||||
db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id,
|
||||
*last_holder_account_id,
|
||||
payouts_for_this_holder));
|
||||
dlog("Just pushed virtual op for payout to ${account}", ("account", (*last_holder_account_id)(db).name));
|
||||
payouts_for_this_holder.clear();
|
||||
last_holder_account_id.reset();
|
||||
}
|
||||
|
||||
|
||||
if (pending_balance_object.pending_balance.value &&
|
||||
is_authorized_asset(db, pending_balance_object.owner(db), pending_balance_object.dividend_payout_asset_type(db)) &&
|
||||
is_asset_approved_for_distribution_account(pending_balance_object.dividend_payout_asset_type))
|
||||
{
|
||||
dlog("Processing payout of ${asset} to account ${account}",
|
||||
("asset", asset(pending_balance_object.pending_balance, pending_balance_object.dividend_payout_asset_type))
|
||||
("account", pending_balance_object.owner(db).name));
|
||||
|
||||
db.adjust_balance(pending_balance_object.owner,
|
||||
asset(pending_balance_object.pending_balance,
|
||||
pending_balance_object.dividend_payout_asset_type));
|
||||
payouts_for_this_holder.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;
|
||||
|
||||
db.modify(pending_balance_object, [&]( pending_dividend_payout_balance_for_holder_object& pending_balance ){
|
||||
pending_balance.pending_balance = 0;
|
||||
});
|
||||
}
|
||||
|
||||
++pending_balance_object_iter;
|
||||
}
|
||||
// we will always be left with the last holder's data, generate the virtual op for it now.
|
||||
if (last_holder_account_id && payouts_for_this_holder.size())
|
||||
{
|
||||
// we've moved on to a new account, generate the dividend payment virtual op for the previous one
|
||||
db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id,
|
||||
*last_holder_account_id,
|
||||
payouts_for_this_holder));
|
||||
dlog("Just pushed virtual op for payout to ${account}", ("account", (*last_holder_account_id)(db).name));
|
||||
payouts_for_this_holder.clear();
|
||||
last_holder_account_id.reset();
|
||||
}
|
||||
|
||||
// now debit the total amount of dividends paid out from the distribution account
|
||||
// and reduce the distributed_balances accordingly
|
||||
|
||||
if (pending_balance_object.pending_balance.value &&
|
||||
is_authorized_asset(db, pending_balance_object.owner(db), pending_balance_object.dividend_payout_asset_type(db)) &&
|
||||
is_asset_approved_for_distribution_account(pending_balance_object.dividend_payout_asset_type))
|
||||
for (const auto& value : amounts_paid_out_by_asset)
|
||||
{
|
||||
dlog("Processing payout of ${asset} to account ${account}",
|
||||
("asset", asset(pending_balance_object.pending_balance, pending_balance_object.dividend_payout_asset_type))
|
||||
("account", pending_balance_object.owner(db).name));
|
||||
const asset_id_type& asset_paid_out = value.first;
|
||||
const share_type& amount_paid_out = value.second;
|
||||
|
||||
db.adjust_balance(pending_balance_object.owner,
|
||||
asset(pending_balance_object.pending_balance,
|
||||
pending_balance_object.dividend_payout_asset_type));
|
||||
payouts_for_this_holder.insert(asset(pending_balance_object.pending_balance,
|
||||
pending_balance_object.dividend_payout_asset_type));
|
||||
last_holder_account_id = pending_balance_object.owner;
|
||||
amounts_paid_out_by_asset[pending_balance_object.dividend_payout_asset_type] += pending_balance_object.pending_balance;
|
||||
db.adjust_balance(dividend_data.dividend_distribution_account,
|
||||
asset(-amount_paid_out,
|
||||
asset_paid_out));
|
||||
auto distributed_balance_iter =
|
||||
distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().find(boost::make_tuple(dividend_holder_asset_obj.id,
|
||||
asset_paid_out));
|
||||
assert(distributed_balance_iter != distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().end());
|
||||
if (distributed_balance_iter != distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().end())
|
||||
db.modify(*distributed_balance_iter, [&]( total_distributed_dividend_balance_object& obj ){
|
||||
obj.balance_at_last_maintenance_interval -= amount_paid_out; // now they've been paid out, reset to zero
|
||||
});
|
||||
|
||||
db.modify(pending_balance_object, [&]( pending_dividend_payout_balance_for_holder_object& pending_balance ){
|
||||
pending_balance.pending_balance = 0;
|
||||
});
|
||||
}
|
||||
|
||||
++pending_balance_object_iter;
|
||||
}
|
||||
// we will always be left with the last holder's data, generate the virtual op for it now.
|
||||
if (last_holder_account_id && payouts_for_this_holder.size())
|
||||
{
|
||||
// we've moved on to a new account, generate the dividend payment virtual op for the previous one
|
||||
db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id,
|
||||
*last_holder_account_id,
|
||||
payouts_for_this_holder));
|
||||
dlog("Just pushed virtual op for payout to ${account}", ("account", (*last_holder_account_id)(db).name));
|
||||
}
|
||||
// now schedule the next payout time
|
||||
db.modify(dividend_data, [current_head_block_time](asset_dividend_data_object& dividend_data_obj) {
|
||||
dividend_data_obj.last_scheduled_payout_time = dividend_data_obj.options.next_payout_time;
|
||||
dividend_data_obj.last_payout_time = current_head_block_time;
|
||||
fc::optional<fc::time_point_sec> next_payout_time;
|
||||
if (dividend_data_obj.options.payout_interval)
|
||||
{
|
||||
// if there was a previous payout, make our next payment one interval
|
||||
uint32_t current_time_sec = current_head_block_time.sec_since_epoch();
|
||||
fc::time_point_sec reference_time = *dividend_data_obj.last_scheduled_payout_time;
|
||||
uint32_t next_possible_time_sec = dividend_data_obj.last_scheduled_payout_time->sec_since_epoch();
|
||||
do
|
||||
next_possible_time_sec += *dividend_data_obj.options.payout_interval;
|
||||
while (next_possible_time_sec <= current_time_sec);
|
||||
|
||||
// now debit the total amount of dividends paid out from the distribution account
|
||||
// and reduce the distributed_balances accordingly
|
||||
|
||||
for (const auto& value : amounts_paid_out_by_asset)
|
||||
{
|
||||
const asset_id_type& asset_paid_out = value.first;
|
||||
const share_type& amount_paid_out = value.second;
|
||||
|
||||
db.adjust_balance(dividend_data.dividend_distribution_account,
|
||||
asset(-amount_paid_out,
|
||||
asset_paid_out));
|
||||
auto distributed_balance_iter =
|
||||
distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().find(boost::make_tuple(dividend_holder_asset_obj.id,
|
||||
asset_paid_out));
|
||||
assert(distributed_balance_iter != distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().end());
|
||||
if (distributed_balance_iter != distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().end())
|
||||
db.modify(*distributed_balance_iter, [&]( total_distributed_dividend_balance_object& obj ){
|
||||
obj.balance_at_last_maintenance_interval -= amount_paid_out; // now they've been paid out, reset to zero
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// now schedule the next payout time
|
||||
db.modify(dividend_data, [current_head_block_time](asset_dividend_data_object& dividend_data_obj) {
|
||||
dividend_data_obj.last_scheduled_payout_time = dividend_data_obj.options.next_payout_time;
|
||||
dividend_data_obj.last_payout_time = current_head_block_time;
|
||||
fc::optional<fc::time_point_sec> next_payout_time;
|
||||
if (dividend_data_obj.options.payout_interval)
|
||||
{
|
||||
// if there was a previous payout, make our next payment one interval
|
||||
uint32_t current_time_sec = current_head_block_time.sec_since_epoch();
|
||||
fc::time_point_sec reference_time = *dividend_data_obj.last_scheduled_payout_time;
|
||||
uint32_t next_possible_time_sec = dividend_data_obj.last_scheduled_payout_time->sec_since_epoch();
|
||||
do
|
||||
next_possible_time_sec += *dividend_data_obj.options.payout_interval;
|
||||
while (next_possible_time_sec <= current_time_sec);
|
||||
|
||||
next_payout_time = next_possible_time_sec;
|
||||
}
|
||||
dividend_data_obj.options.next_payout_time = next_payout_time;
|
||||
idump((dividend_data_obj.last_scheduled_payout_time)
|
||||
(dividend_data_obj.last_payout_time)
|
||||
(dividend_data_obj.options.next_payout_time));
|
||||
});
|
||||
next_payout_time = next_possible_time_sec;
|
||||
}
|
||||
dividend_data_obj.options.next_payout_time = next_payout_time;
|
||||
idump((dividend_data_obj.last_scheduled_payout_time)
|
||||
(dividend_data_obj.last_payout_time)
|
||||
(dividend_data_obj.options.next_payout_time));
|
||||
});
|
||||
}
|
||||
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) {
|
||||
|
|
@ -1262,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();
|
||||
|
|
@ -1384,12 +1417,13 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
});
|
||||
|
||||
// Reset all BitAsset force settlement volumes to zero
|
||||
for( const asset_bitasset_data_object* d : get_index_type<asset_bitasset_data_index>() )
|
||||
modify(*d, [](asset_bitasset_data_object& d) { d.force_settled_volume = 0; });
|
||||
//for( const asset_bitasset_data_object* d : get_index_type<asset_bitasset_data_index>() )
|
||||
for( const auto& d : get_index_type<asset_bitasset_data_index>().indices() )
|
||||
modify( d, [](asset_bitasset_data_object& o) { o.force_settled_volume = 0; });
|
||||
|
||||
// process_budget needs to run at the bottom because
|
||||
// it needs to know the next_maintenance_time
|
||||
process_budget();
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -64,10 +64,16 @@ void database::reindex(fc::path data_dir, const genesis_state_type& initial_allo
|
|||
const auto last_block_num = last_block->block_num();
|
||||
|
||||
ilog( "Replaying blocks..." );
|
||||
_undo_db.disable();
|
||||
// Right now, we leave undo_db enabled when replaying when the bookie plugin is
|
||||
// enabled. It depends on new/changed/removed object notifications, and those are
|
||||
// only fired when the undo_db is enabled
|
||||
if (!_slow_replays)
|
||||
_undo_db.disable();
|
||||
for( uint32_t i = 1; i <= last_block_num; ++i )
|
||||
{
|
||||
if( i % 2000 == 0 ) std::cerr << " " << double(i*100)/last_block_num << "% "<<i << " of " <<last_block_num<<" \n";
|
||||
if( i == 1 ||
|
||||
i % 10000 == 0 )
|
||||
std::cerr << " " << double(i*100)/last_block_num << "% "<< i << " of " <<last_block_num<<" \n";
|
||||
fc::optional< signed_block > block = _block_id_to_block.fetch_by_number(i);
|
||||
if( !block.valid() )
|
||||
{
|
||||
|
|
@ -88,14 +94,24 @@ void database::reindex(fc::path data_dir, const genesis_state_type& initial_allo
|
|||
wlog( "Dropped ${n} blocks from after the gap", ("n", dropped_count) );
|
||||
break;
|
||||
}
|
||||
apply_block(*block, skip_witness_signature |
|
||||
skip_transaction_signatures |
|
||||
skip_transaction_dupe_check |
|
||||
skip_tapos_check |
|
||||
skip_witness_schedule_check |
|
||||
skip_authority_check);
|
||||
if (_slow_replays)
|
||||
push_block(*block, skip_fork_db |
|
||||
skip_witness_signature |
|
||||
skip_transaction_signatures |
|
||||
skip_transaction_dupe_check |
|
||||
skip_tapos_check |
|
||||
skip_witness_schedule_check |
|
||||
skip_authority_check);
|
||||
else
|
||||
apply_block(*block, skip_witness_signature |
|
||||
skip_transaction_signatures |
|
||||
skip_transaction_dupe_check |
|
||||
skip_tapos_check |
|
||||
skip_witness_schedule_check |
|
||||
skip_authority_check);
|
||||
}
|
||||
_undo_db.enable();
|
||||
if (!_slow_replays)
|
||||
_undo_db.enable();
|
||||
auto end = fc::time_point::now();
|
||||
ilog( "Done reindexing, elapsed time: ${t} sec", ("t",double((end-start).count())/1000000.0 ) );
|
||||
} FC_CAPTURE_AND_RETHROW( (data_dir) ) }
|
||||
|
|
@ -166,8 +182,9 @@ void database::close(bool rewind)
|
|||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
catch ( const fc::exception& e )
|
||||
{
|
||||
wlog( "Database close unexpected exception: ${e}", ("e", e) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -185,4 +202,10 @@ void database::close(bool rewind)
|
|||
_fork_db.reset();
|
||||
}
|
||||
|
||||
void database::force_slow_replays()
|
||||
{
|
||||
ilog("enabling slow replays");
|
||||
_slow_replays = true;
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ bool maybe_cull_small_order( database& db, const limit_order_object& order )
|
|||
*/
|
||||
if( order.amount_to_receive().amount == 0 )
|
||||
{
|
||||
ilog( "applied epsilon logic" );
|
||||
//ilog( "applied epsilon logic" );
|
||||
db.cancel_order(order);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
444
libraries/chain/db_notify.cpp
Normal file
444
libraries/chain/db_notify.cpp
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
#include <fc/container/flat.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/authority.hpp>
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/chain/protocol/transaction.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/withdraw_permission_object.hpp>
|
||||
#include <graphene/chain/worker_object.hpp>
|
||||
#include <graphene/chain/confidential_object.hpp>
|
||||
#include <graphene/chain/market_object.hpp>
|
||||
#include <graphene/chain/committee_member_object.hpp>
|
||||
|
||||
using namespace fc;
|
||||
using namespace graphene::chain;
|
||||
|
||||
// TODO: Review all of these, especially no-ops
|
||||
struct get_impacted_account_visitor
|
||||
{
|
||||
flat_set<account_id_type>& _impacted;
|
||||
get_impacted_account_visitor( flat_set<account_id_type>& impact ):_impacted(impact) {}
|
||||
typedef void result_type;
|
||||
|
||||
void operator()( const transfer_operation& op )
|
||||
{
|
||||
_impacted.insert( op.to );
|
||||
}
|
||||
|
||||
void operator()( const asset_claim_fees_operation& op ){}
|
||||
void operator()( const limit_order_create_operation& op ) {}
|
||||
void operator()( const limit_order_cancel_operation& op )
|
||||
{
|
||||
_impacted.insert( op.fee_paying_account );
|
||||
}
|
||||
void operator()( const call_order_update_operation& op ) {}
|
||||
void operator()( const fill_order_operation& op )
|
||||
{
|
||||
_impacted.insert( op.account_id );
|
||||
}
|
||||
|
||||
void operator()( const account_create_operation& op )
|
||||
{
|
||||
_impacted.insert( op.registrar );
|
||||
_impacted.insert( op.referrer );
|
||||
add_authority_accounts( _impacted, op.owner );
|
||||
add_authority_accounts( _impacted, op.active );
|
||||
}
|
||||
|
||||
void operator()( const account_update_operation& op )
|
||||
{
|
||||
_impacted.insert( op.account );
|
||||
if( op.owner )
|
||||
add_authority_accounts( _impacted, *(op.owner) );
|
||||
if( op.active )
|
||||
add_authority_accounts( _impacted, *(op.active) );
|
||||
}
|
||||
|
||||
void operator()( const account_whitelist_operation& op )
|
||||
{
|
||||
_impacted.insert( op.account_to_list );
|
||||
}
|
||||
|
||||
void operator()( const account_upgrade_operation& op ) {}
|
||||
void operator()( const account_transfer_operation& op )
|
||||
{
|
||||
_impacted.insert( op.new_owner );
|
||||
}
|
||||
|
||||
void operator()( const asset_create_operation& op ) {}
|
||||
void operator()( const asset_update_operation& op )
|
||||
{
|
||||
if( op.new_issuer )
|
||||
_impacted.insert( *(op.new_issuer) );
|
||||
}
|
||||
|
||||
void operator()( const asset_update_bitasset_operation& op ) {}
|
||||
void operator()( const asset_update_dividend_operation& op ) {}
|
||||
void operator()( const asset_dividend_distribution_operation& op )
|
||||
{
|
||||
_impacted.insert( op.account_id );
|
||||
}
|
||||
|
||||
void operator()( const asset_update_feed_producers_operation& op ) {}
|
||||
|
||||
void operator()( const asset_issue_operation& op )
|
||||
{
|
||||
_impacted.insert( op.issue_to_account );
|
||||
}
|
||||
|
||||
void operator()( const asset_reserve_operation& op ) {}
|
||||
void operator()( const asset_fund_fee_pool_operation& op ) {}
|
||||
void operator()( const asset_settle_operation& op ) {}
|
||||
void operator()( const asset_global_settle_operation& op ) {}
|
||||
void operator()( const asset_publish_feed_operation& op ) {}
|
||||
void operator()( const witness_create_operation& op )
|
||||
{
|
||||
_impacted.insert( op.witness_account );
|
||||
}
|
||||
void operator()( const witness_update_operation& op )
|
||||
{
|
||||
_impacted.insert( op.witness_account );
|
||||
}
|
||||
|
||||
void operator()( const proposal_create_operation& op )
|
||||
{
|
||||
vector<authority> other;
|
||||
for( const auto& proposed_op : op.proposed_ops )
|
||||
operation_get_required_authorities( proposed_op.op, _impacted, _impacted, other );
|
||||
for( auto& o : other )
|
||||
add_authority_accounts( _impacted, o );
|
||||
}
|
||||
|
||||
void operator()( const proposal_update_operation& op ) {}
|
||||
void operator()( const proposal_delete_operation& op ) {}
|
||||
|
||||
void operator()( const withdraw_permission_create_operation& op )
|
||||
{
|
||||
_impacted.insert( op.authorized_account );
|
||||
}
|
||||
|
||||
void operator()( const withdraw_permission_update_operation& op )
|
||||
{
|
||||
_impacted.insert( op.authorized_account );
|
||||
}
|
||||
|
||||
void operator()( const withdraw_permission_claim_operation& op )
|
||||
{
|
||||
_impacted.insert( op.withdraw_from_account );
|
||||
}
|
||||
|
||||
void operator()( const withdraw_permission_delete_operation& op )
|
||||
{
|
||||
_impacted.insert( op.authorized_account );
|
||||
}
|
||||
|
||||
void operator()( const committee_member_create_operation& op )
|
||||
{
|
||||
_impacted.insert( op.committee_member_account );
|
||||
}
|
||||
void operator()( const committee_member_update_operation& op )
|
||||
{
|
||||
_impacted.insert( op.committee_member_account );
|
||||
}
|
||||
void operator()( const committee_member_update_global_parameters_operation& op ) {}
|
||||
|
||||
void operator()( const vesting_balance_create_operation& op )
|
||||
{
|
||||
_impacted.insert( op.owner );
|
||||
}
|
||||
|
||||
void operator()( const vesting_balance_withdraw_operation& op ) {}
|
||||
void operator()( const worker_create_operation& op ) {}
|
||||
void operator()( const custom_operation& op ) {}
|
||||
void operator()( const assert_operation& op ) {}
|
||||
void operator()( const balance_claim_operation& op ) {}
|
||||
|
||||
void operator()( const override_transfer_operation& op )
|
||||
{
|
||||
_impacted.insert( op.to );
|
||||
_impacted.insert( op.from );
|
||||
_impacted.insert( op.issuer );
|
||||
}
|
||||
|
||||
void operator()( const transfer_to_blind_operation& op )
|
||||
{
|
||||
_impacted.insert( op.from );
|
||||
for( const auto& out : op.outputs )
|
||||
add_authority_accounts( _impacted, out.owner );
|
||||
}
|
||||
|
||||
void operator()( const blind_transfer_operation& op )
|
||||
{
|
||||
for( const auto& in : op.inputs )
|
||||
add_authority_accounts( _impacted, in.owner );
|
||||
for( const auto& out : op.outputs )
|
||||
add_authority_accounts( _impacted, out.owner );
|
||||
}
|
||||
|
||||
void operator()( const transfer_from_blind_operation& op )
|
||||
{
|
||||
_impacted.insert( op.to );
|
||||
for( const auto& in : op.inputs )
|
||||
add_authority_accounts( _impacted, in.owner );
|
||||
}
|
||||
|
||||
void operator()( const asset_settle_cancel_operation& op )
|
||||
{
|
||||
_impacted.insert( op.account );
|
||||
}
|
||||
|
||||
void operator()( const fba_distribute_operation& op )
|
||||
{
|
||||
_impacted.insert( op.account_id );
|
||||
}
|
||||
void operator()(const sport_create_operation&){}
|
||||
void operator()(const sport_update_operation&){}
|
||||
void operator()(const event_group_create_operation&){}
|
||||
void operator()(const event_group_update_operation& op ) {}
|
||||
void operator()(const event_create_operation&){}
|
||||
void operator()(const event_update_operation& op ) {}
|
||||
void operator()(const event_update_status_operation& op ) {}
|
||||
void operator()(const betting_market_rules_create_operation&){}
|
||||
void operator()(const betting_market_rules_update_operation& op ) {}
|
||||
void operator()(const betting_market_group_create_operation&){}
|
||||
void operator()(const betting_market_group_update_operation& op ) {}
|
||||
void operator()(const betting_market_create_operation&){}
|
||||
void operator()(const betting_market_update_operation&){}
|
||||
void operator()(const bet_place_operation&){}
|
||||
void operator()(const betting_market_group_resolve_operation&){}
|
||||
void operator()(const betting_market_group_resolved_operation &){}
|
||||
void operator()(const betting_market_group_cancel_unmatched_bets_operation&){}
|
||||
void operator()(const bet_matched_operation &){}
|
||||
void operator()(const bet_cancel_operation&){}
|
||||
void operator()(const bet_canceled_operation &){}
|
||||
void operator()(const bet_adjusted_operation &){}
|
||||
|
||||
void operator()( const tournament_create_operation& op )
|
||||
{
|
||||
_impacted.insert( op.creator );
|
||||
_impacted.insert( op.options.whitelist.begin(), op.options.whitelist.end() );
|
||||
}
|
||||
void operator()( const tournament_join_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payer_account_id );
|
||||
_impacted.insert( op.player_account_id );
|
||||
}
|
||||
void operator()( const tournament_leave_operation& op )
|
||||
{
|
||||
//if account canceling registration is not the player, it must be the payer
|
||||
if (op.canceling_account_id != op.player_account_id)
|
||||
_impacted.erase( op.canceling_account_id );
|
||||
_impacted.erase( op.player_account_id );
|
||||
}
|
||||
void operator()( const game_move_operation& op )
|
||||
{
|
||||
_impacted.insert( op.player_account_id );
|
||||
}
|
||||
void operator()( const tournament_payout_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payout_account_id );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
{
|
||||
get_impacted_account_visitor vtor = get_impacted_account_visitor( result );
|
||||
op.visit( vtor );
|
||||
}
|
||||
|
||||
void transaction_get_impacted_accounts( const transaction& tx, flat_set<account_id_type>& result )
|
||||
{
|
||||
for( const auto& op : tx.operations )
|
||||
operation_get_impacted_accounts( op, result );
|
||||
}
|
||||
|
||||
void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accounts )
|
||||
{
|
||||
if( obj->id.space() == protocol_ids )
|
||||
{
|
||||
switch( (object_type)obj->id.type() )
|
||||
{
|
||||
case null_object_type:
|
||||
case base_object_type:
|
||||
case OBJECT_TYPE_COUNT:
|
||||
return;
|
||||
case account_object_type:{
|
||||
accounts.insert( obj->id );
|
||||
break;
|
||||
} case asset_object_type:{
|
||||
const auto& aobj = dynamic_cast<const asset_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->issuer );
|
||||
break;
|
||||
} case force_settlement_object_type:{
|
||||
const auto& aobj = dynamic_cast<const force_settlement_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->owner );
|
||||
break;
|
||||
} case committee_member_object_type:{
|
||||
const auto& aobj = dynamic_cast<const committee_member_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->committee_member_account );
|
||||
break;
|
||||
} case witness_object_type:{
|
||||
const auto& aobj = dynamic_cast<const witness_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->witness_account );
|
||||
break;
|
||||
} case limit_order_object_type:{
|
||||
const auto& aobj = dynamic_cast<const limit_order_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->seller );
|
||||
break;
|
||||
} case call_order_object_type:{
|
||||
const auto& aobj = dynamic_cast<const call_order_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->borrower );
|
||||
break;
|
||||
} case custom_object_type:{
|
||||
break;
|
||||
} case proposal_object_type:{
|
||||
const auto& aobj = dynamic_cast<const proposal_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
transaction_get_impacted_accounts( aobj->proposed_transaction, accounts );
|
||||
break;
|
||||
} case operation_history_object_type:{
|
||||
const auto& aobj = dynamic_cast<const operation_history_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
operation_get_impacted_accounts( aobj->op, accounts );
|
||||
break;
|
||||
} case withdraw_permission_object_type:{
|
||||
const auto& aobj = dynamic_cast<const withdraw_permission_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->withdraw_from_account );
|
||||
accounts.insert( aobj->authorized_account );
|
||||
break;
|
||||
} case vesting_balance_object_type:{
|
||||
const auto& aobj = dynamic_cast<const vesting_balance_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->owner );
|
||||
break;
|
||||
} case worker_object_type:{
|
||||
const auto& aobj = dynamic_cast<const worker_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->worker_account );
|
||||
break;
|
||||
} case balance_object_type:{
|
||||
/** these are free from any accounts */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( obj->id.space() == implementation_ids )
|
||||
{
|
||||
switch( (impl_object_type)obj->id.type() )
|
||||
{
|
||||
case impl_global_property_object_type:
|
||||
break;
|
||||
case impl_dynamic_global_property_object_type:
|
||||
break;
|
||||
case impl_reserved0_object_type:
|
||||
break;
|
||||
case impl_asset_dynamic_data_type:
|
||||
break;
|
||||
case impl_asset_bitasset_data_type:
|
||||
break;
|
||||
case impl_account_balance_object_type:{
|
||||
const auto& aobj = dynamic_cast<const account_balance_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->owner );
|
||||
break;
|
||||
} case impl_account_statistics_object_type:{
|
||||
const auto& aobj = dynamic_cast<const account_statistics_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->owner );
|
||||
break;
|
||||
} case impl_transaction_object_type:{
|
||||
const auto& aobj = dynamic_cast<const transaction_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
transaction_get_impacted_accounts( aobj->trx, accounts );
|
||||
break;
|
||||
} case impl_blinded_balance_object_type:{
|
||||
const auto& aobj = dynamic_cast<const blinded_balance_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
for( const auto& a : aobj->owner.account_auths )
|
||||
accounts.insert( a.first );
|
||||
break;
|
||||
} case impl_block_summary_object_type:
|
||||
break;
|
||||
case impl_account_transaction_history_object_type:
|
||||
break;
|
||||
case impl_chain_property_object_type:
|
||||
break;
|
||||
case impl_witness_schedule_object_type:
|
||||
break;
|
||||
case impl_budget_record_object_type:
|
||||
break;
|
||||
case impl_special_authority_object_type:
|
||||
break;
|
||||
case impl_buyback_object_type:
|
||||
break;
|
||||
case impl_fba_accumulator_object_type:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // end get_relevant_accounts( const object* obj, flat_set<account_id_type>& accounts )
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void database::notify_changed_objects()
|
||||
{ try {
|
||||
if( _undo_db.enabled() )
|
||||
{
|
||||
const auto& head_undo = _undo_db.head();
|
||||
|
||||
// New
|
||||
if( !new_objects.empty() )
|
||||
{
|
||||
vector<object_id_type> new_ids; new_ids.reserve(head_undo.new_ids.size());
|
||||
flat_set<account_id_type> new_accounts_impacted;
|
||||
for( const auto& item : head_undo.new_ids )
|
||||
{
|
||||
new_ids.push_back(item);
|
||||
auto obj = find_object(item);
|
||||
if(obj != nullptr)
|
||||
get_relevant_accounts(obj, new_accounts_impacted);
|
||||
}
|
||||
|
||||
new_objects(new_ids, new_accounts_impacted);
|
||||
}
|
||||
|
||||
// Changed
|
||||
if( !changed_objects.empty() )
|
||||
{
|
||||
vector<object_id_type> changed_ids; changed_ids.reserve(head_undo.old_values.size());
|
||||
flat_set<account_id_type> changed_accounts_impacted;
|
||||
for( const auto& item : head_undo.old_values )
|
||||
{
|
||||
changed_ids.push_back(item.first);
|
||||
get_relevant_accounts(item.second.get(), changed_accounts_impacted);
|
||||
}
|
||||
|
||||
changed_objects(changed_ids, changed_accounts_impacted);
|
||||
}
|
||||
|
||||
// Removed
|
||||
if( !removed_objects.empty() )
|
||||
{
|
||||
vector<object_id_type> removed_ids; removed_ids.reserve( head_undo.removed.size() );
|
||||
vector<const object*> removed; removed.reserve( head_undo.removed.size() );
|
||||
flat_set<account_id_type> removed_accounts_impacted;
|
||||
for( const auto& item : head_undo.removed )
|
||||
{
|
||||
removed_ids.emplace_back( item.first );
|
||||
auto obj = item.second.get();
|
||||
removed.emplace_back( obj );
|
||||
get_relevant_accounts(obj, removed_accounts_impacted);
|
||||
}
|
||||
|
||||
removed_objects(removed_ids, removed, removed_accounts_impacted);
|
||||
}
|
||||
}
|
||||
} FC_CAPTURE_AND_LOG( (0) ) }
|
||||
|
||||
} }
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
@ -188,6 +189,52 @@ void database::clear_expired_transactions()
|
|||
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()
|
||||
{
|
||||
const auto& proposal_expiration_index = get_index_type<proposal_index>().indices().get<by_expiration>();
|
||||
|
|
@ -608,4 +655,29 @@ void database::update_tournaments()
|
|||
initiate_next_games(*this);
|
||||
}
|
||||
|
||||
void process_settled_betting_markets(database& db, fc::time_point_sec current_block_time)
|
||||
{
|
||||
// after a betting market is graded, it goes through a delay period in which it
|
||||
// can be flagged for re-grading. If it isn't flagged during this interval,
|
||||
// it is automatically settled (paid). Process these now.
|
||||
const auto& betting_market_group_index = db.get_index_type<betting_market_group_object_index>().indices().get<by_settling_time>();
|
||||
|
||||
// this index will be sorted with all bmgs with no settling time set first, followed by
|
||||
// ones with the settling time set by increasing time. Start at the first bmg with a time set
|
||||
auto betting_market_group_iter = betting_market_group_index.upper_bound(fc::optional<fc::time_point_sec>());
|
||||
while (betting_market_group_iter != betting_market_group_index.end() &&
|
||||
*betting_market_group_iter->settling_time <= current_block_time)
|
||||
{
|
||||
auto next_iter = std::next(betting_market_group_iter);
|
||||
db.settle_betting_market_group(*betting_market_group_iter);
|
||||
betting_market_group_iter = next_iter;
|
||||
}
|
||||
}
|
||||
|
||||
void database::update_betting_markets(fc::time_point_sec current_block_time)
|
||||
{
|
||||
process_settled_betting_markets(*this, current_block_time);
|
||||
remove_completed_events();
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -128,4 +128,18 @@ database& generic_evaluator::db()const { return trx_state->db(); }
|
|||
db().adjust_balance(fee_payer, fee_from_account);
|
||||
}
|
||||
|
||||
object_id_type generic_evaluator::get_relative_id( object_id_type rel_id )const
|
||||
{
|
||||
if (!is_relative(rel_id))
|
||||
FC_THROW("get_relative_id() called for non-relative id ${id}", ("id", rel_id));
|
||||
if (rel_id.instance() >= trx_state->operation_results.size())
|
||||
FC_THROW("get_relative_id() asked for id of operation ${op_num} (zero-based), but we only have ${count} operations",
|
||||
("op_num", rel_id.instance())("count", trx_state->operation_results.size()));
|
||||
if (trx_state->operation_results[rel_id.instance()].which() != operation_result::tag<object_id_type>::value)
|
||||
FC_THROW("get_relative_id() asked for the result of operation ${op_num}, but that operation did not return an object_id",
|
||||
("op_num", rel_id.instance()));
|
||||
return trx_state->operation_results[rel_id.instance()].get<object_id_type>();
|
||||
}
|
||||
|
||||
|
||||
} }
|
||||
|
|
|
|||
140
libraries/chain/event_evaluator.cpp
Normal file
140
libraries/chain/event_evaluator.cpp
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/event_evaluator.hpp>
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/event_group_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/is_authorized_asset.hpp>
|
||||
#include <graphene/chain/global_betting_statistics_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result event_create_evaluator::do_evaluate(const event_create_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(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
|
||||
100
libraries/chain/event_group_evaluator.cpp
Normal file
100
libraries/chain/event_group_evaluator.cpp
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/event_group_evaluator.hpp>
|
||||
#include <graphene/chain/event_group_object.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/is_authorized_asset.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result event_group_create_evaluator::do_evaluate(const event_group_create_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(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) ) }
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
553
libraries/chain/event_object.cpp
Normal file
553
libraries/chain/event_object.cpp
Normal file
|
|
@ -0,0 +1,553 @@
|
|||
#define DEFAULT_LOGGER "betting"
|
||||
#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
|
||||
#define BOOST_MPL_LIMIT_VECTOR_SIZE 30
|
||||
#define BOOST_MPL_LIMIT_MAP_SIZE 30
|
||||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <boost/msm/back/state_machine.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
#include <boost/archive/binary_oarchive.hpp>
|
||||
#include <boost/archive/binary_iarchive.hpp>
|
||||
#include <boost/msm/back/tools.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
enum class event_state {
|
||||
upcoming,
|
||||
frozen_upcoming,
|
||||
in_progress,
|
||||
frozen_in_progress,
|
||||
finished,
|
||||
canceled,
|
||||
settled
|
||||
};
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::event_state,
|
||||
(upcoming)
|
||||
(frozen_upcoming)
|
||||
(in_progress)
|
||||
(frozen_in_progress)
|
||||
(finished)
|
||||
(canceled)
|
||||
(settled))
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
namespace msm = boost::msm;
|
||||
namespace mpl = boost::mpl;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Events -- most events happen when the witnesses publish an event_update operation with a new
|
||||
// status, so if they publish an event with the status set to `frozen`, we'll generate a `frozen_event`
|
||||
struct upcoming_event
|
||||
{
|
||||
database& db;
|
||||
upcoming_event(database& db) : db(db) {}
|
||||
};
|
||||
struct in_progress_event
|
||||
{
|
||||
database& db;
|
||||
in_progress_event(database& db) : db(db) {}
|
||||
};
|
||||
struct frozen_event
|
||||
{
|
||||
database& db;
|
||||
frozen_event(database& db) : db(db) {}
|
||||
};
|
||||
struct finished_event
|
||||
{
|
||||
database& db;
|
||||
finished_event(database& db) : db(db) {}
|
||||
};
|
||||
struct canceled_event
|
||||
{
|
||||
database& db;
|
||||
canceled_event(database& db) : db(db) {}
|
||||
};
|
||||
|
||||
// event triggered when a betting market group in this event is resolved,
|
||||
// when we get this, check and see if all betting market groups are now
|
||||
// canceled/settled; if so, transition the event to canceled/settled,
|
||||
// otherwise remain in the current state
|
||||
struct betting_market_group_resolved_event
|
||||
{
|
||||
database& db;
|
||||
betting_market_group_id_type resolved_group;
|
||||
bool was_canceled;
|
||||
betting_market_group_resolved_event(database& db, betting_market_group_id_type resolved_group, bool was_canceled) : db(db), resolved_group(resolved_group), was_canceled(was_canceled) {}
|
||||
};
|
||||
|
||||
// event triggered when a betting market group is closed. When we get this,
|
||||
// if all child betting market groups are closed, transition to finished
|
||||
struct betting_market_group_closed_event
|
||||
{
|
||||
database& db;
|
||||
betting_market_group_id_type closed_group;
|
||||
betting_market_group_closed_event(database& db, betting_market_group_id_type closed_group) : db(db), closed_group(closed_group) {}
|
||||
};
|
||||
|
||||
// Events
|
||||
struct event_state_machine_ : public msm::front::state_machine_def<event_state_machine_>
|
||||
|
||||
{
|
||||
// disable a few state machine features we don't use for performance
|
||||
typedef int no_exception_thrown;
|
||||
typedef int no_message_queue;
|
||||
|
||||
// States
|
||||
struct upcoming : public msm::front::state<> {
|
||||
void on_entry(const betting_market_group_resolved_event& event, event_state_machine_& fsm) {} // transition to self
|
||||
void on_entry(const upcoming_event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> upcoming", ("id", fsm.event_obj->id));
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(fsm.event_obj->id)))
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_upcoming_event(event.db);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
// it's possible a betting market group has already been closed or canceled,
|
||||
// in which case we can't freeze the group. just ignore the exception
|
||||
}
|
||||
}
|
||||
};
|
||||
struct in_progress : public msm::front::state<> {
|
||||
void on_entry(const betting_market_group_resolved_event& event, event_state_machine_& fsm) {} // transition to self
|
||||
void on_entry(const in_progress_event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> in_progress", ("id", fsm.event_obj->id));
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(fsm.event_obj->id)))
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_in_play_event(event.db);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
// it's possible a betting market group has already been closed or canceled,
|
||||
// in which case we can't freeze the group. just ignore the exception
|
||||
}
|
||||
}
|
||||
};
|
||||
struct frozen_upcoming : public msm::front::state<> {
|
||||
void on_entry(const betting_market_group_resolved_event& event, event_state_machine_& fsm) {} // transition to self
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> frozen_upcoming", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
struct frozen_in_progress : public msm::front::state<> {
|
||||
void on_entry(const betting_market_group_resolved_event& event, event_state_machine_& fsm) {} // transition to self
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> frozen_in_progress", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
struct finished : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> finished", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
struct settled : public msm::front::state<>{
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> settled", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
struct canceled : public msm::front::state<> {
|
||||
template <class Event>
|
||||
void on_entry(const Event& event, event_state_machine_& fsm) {
|
||||
dlog("event ${id} -> canceled", ("id", fsm.event_obj->id));
|
||||
}
|
||||
};
|
||||
|
||||
// actions
|
||||
void record_whether_group_settled_or_canceled(const betting_market_group_resolved_event& event) {
|
||||
if (!event.was_canceled)
|
||||
event_obj->at_least_one_betting_market_group_settled = true;
|
||||
}
|
||||
|
||||
void freeze_betting_market_groups(const frozen_event& event) {
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
{
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_frozen_event(event.db);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
// it's possible a betting market group has already been closed or canceled,
|
||||
// in which case we can't freeze the group. just ignore the exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void close_all_betting_market_groups(const finished_event& event) {
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
{
|
||||
try
|
||||
{
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_closed_event(event.db, true);
|
||||
});
|
||||
}
|
||||
catch (const graphene::chain::no_transition&)
|
||||
{
|
||||
// it's possible a betting market group has already been closed or canceled,
|
||||
// in which case we can't close the group. just ignore the exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cancel_all_betting_market_groups(const canceled_event& event) {
|
||||
auto& betting_market_group_index = event.db.template get_index_type<betting_market_group_object_index>().indices().template get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) {
|
||||
betting_market_group_obj.on_canceled_event(event.db, true);
|
||||
});
|
||||
}
|
||||
|
||||
// Guards
|
||||
bool all_betting_market_groups_are_closed(const betting_market_group_closed_event& event)
|
||||
{
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
if (betting_market_group.id != event.closed_group)
|
||||
{
|
||||
betting_market_group_status status = betting_market_group.get_status();
|
||||
if (status != betting_market_group_status::closed &&
|
||||
status != betting_market_group_status::graded &&
|
||||
status != betting_market_group_status::re_grading &&
|
||||
status != betting_market_group_status::settled &&
|
||||
status != betting_market_group_status::canceled)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool all_betting_market_groups_are_canceled(const betting_market_group_resolved_event& event)
|
||||
{
|
||||
// if the bmg that just resolved was settled, obviously all didn't cancel
|
||||
if (!event.was_canceled)
|
||||
return false;
|
||||
// if a previously-resolved group was settled, all didn't cancel
|
||||
if (event_obj->at_least_one_betting_market_group_settled)
|
||||
return false;
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id)))
|
||||
if (betting_market_group.id != event.resolved_group)
|
||||
if (betting_market_group.get_status() != betting_market_group_status::canceled)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool all_betting_market_groups_are_resolved(const betting_market_group_resolved_event& event)
|
||||
{
|
||||
if (!event.was_canceled)
|
||||
event_obj->at_least_one_betting_market_group_settled = true;
|
||||
|
||||
auto& betting_market_group_index = event.db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
||||
for (const betting_market_group_object& betting_market_group :
|
||||
boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id))) {
|
||||
if (betting_market_group.id != event.resolved_group) {
|
||||
betting_market_group_status status = betting_market_group.get_status();
|
||||
if (status != betting_market_group_status::canceled && status != betting_market_group_status::settled)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef upcoming initial_state;
|
||||
typedef event_state_machine_ x; // makes transition table cleaner
|
||||
|
||||
// Transition table for tournament
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
_row < upcoming, in_progress_event, in_progress >,
|
||||
a_row< upcoming, finished_event, finished, &x::close_all_betting_market_groups >,
|
||||
a_row< upcoming, frozen_event, frozen_upcoming, &x::freeze_betting_market_groups >,
|
||||
a_row< upcoming, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
a_row< upcoming, betting_market_group_resolved_event,upcoming, &x::record_whether_group_settled_or_canceled >,
|
||||
g_row< upcoming, betting_market_group_closed_event, finished, &x::all_betting_market_groups_are_closed >,
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
_row < frozen_upcoming, upcoming_event, upcoming >,
|
||||
_row < frozen_upcoming, in_progress_event, in_progress >,
|
||||
a_row< frozen_upcoming, finished_event, finished, &x::close_all_betting_market_groups >,
|
||||
a_row< frozen_upcoming, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
a_row< frozen_upcoming, betting_market_group_resolved_event,frozen_upcoming, &x::record_whether_group_settled_or_canceled >,
|
||||
g_row< frozen_upcoming, betting_market_group_closed_event, finished, &x::all_betting_market_groups_are_closed >,
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
a_row< in_progress, frozen_event, frozen_in_progress, &x::freeze_betting_market_groups >,
|
||||
a_row< in_progress, finished_event, finished, &x::close_all_betting_market_groups >,
|
||||
a_row< in_progress, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
a_row< in_progress, betting_market_group_resolved_event,in_progress, &x::record_whether_group_settled_or_canceled >,
|
||||
g_row< in_progress, betting_market_group_closed_event, finished, &x::all_betting_market_groups_are_closed >,
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
_row < frozen_in_progress, in_progress_event, in_progress >,
|
||||
a_row< frozen_in_progress, finished_event, finished, &x::close_all_betting_market_groups >,
|
||||
a_row< frozen_in_progress, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
a_row< frozen_in_progress, betting_market_group_resolved_event,frozen_in_progress, &x::record_whether_group_settled_or_canceled >,
|
||||
g_row< frozen_in_progress, betting_market_group_closed_event, finished, &x::all_betting_market_groups_are_closed >,
|
||||
// +---------------------+-----------------------------+--------------------+--------------------------------+----------------------+
|
||||
a_row< finished, canceled_event, canceled, &x::cancel_all_betting_market_groups >,
|
||||
g_row< finished, betting_market_group_resolved_event,settled, &x::all_betting_market_groups_are_resolved >,
|
||||
g_row< finished, betting_market_group_resolved_event,canceled, &x::all_betting_market_groups_are_canceled >
|
||||
> {};
|
||||
|
||||
template <class Fsm,class Event>
|
||||
void no_transition(Event const& e, Fsm& ,int state)
|
||||
{
|
||||
FC_THROW_EXCEPTION(graphene::chain::no_transition, "No transition");
|
||||
}
|
||||
|
||||
event_object* event_obj;
|
||||
event_state_machine_(event_object* event_obj) : event_obj(event_obj) {}
|
||||
};
|
||||
typedef msm::back::state_machine<event_state_machine_> event_state_machine;
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
class event_object::impl {
|
||||
public:
|
||||
event_state_machine state_machine;
|
||||
|
||||
impl(event_object* self) : state_machine(self) {}
|
||||
};
|
||||
|
||||
event_object::event_object() :
|
||||
at_least_one_betting_market_group_settled(false),
|
||||
my(new impl(this))
|
||||
{
|
||||
}
|
||||
|
||||
event_object::event_object(const event_object& rhs) :
|
||||
graphene::db::abstract_object<event_object>(rhs),
|
||||
name(rhs.name),
|
||||
season(rhs.season),
|
||||
start_time(rhs.start_time),
|
||||
event_group_id(rhs.event_group_id),
|
||||
at_least_one_betting_market_group_settled(rhs.at_least_one_betting_market_group_settled),
|
||||
scores(rhs.scores),
|
||||
my(new impl(this))
|
||||
{
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.event_obj = this;
|
||||
}
|
||||
|
||||
event_object& event_object::operator=(const event_object& rhs)
|
||||
{
|
||||
//graphene::db::abstract_object<event_object>::operator=(rhs);
|
||||
id = rhs.id;
|
||||
name = rhs.name;
|
||||
season = rhs.season;
|
||||
start_time = rhs.start_time;
|
||||
event_group_id = rhs.event_group_id;
|
||||
at_least_one_betting_market_group_settled = rhs.at_least_one_betting_market_group_settled;
|
||||
scores = rhs.scores;
|
||||
|
||||
my->state_machine = rhs.my->state_machine;
|
||||
my->state_machine.event_obj = this;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
event_object::~event_object()
|
||||
{
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool verify_event_status_constants()
|
||||
{
|
||||
unsigned error_count = 0;
|
||||
typedef msm::back::generate_state_set<event_state_machine::stt>::type all_states;
|
||||
static char const* filled_state_names[mpl::size<all_states>::value];
|
||||
mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> >
|
||||
(msm::back::fill_state_names<event_state_machine::stt>(filled_state_names));
|
||||
for (unsigned i = 0; i < mpl::size<all_states>::value; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
// this is an approximate test, the state name provided by typeinfo will be mangled, but should
|
||||
// at least contain the string we're looking for
|
||||
const char* fc_reflected_value_name = fc::reflector<event_state>::to_string((event_state)i);
|
||||
if (!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
|
||||
|
||||
|
||||
4
libraries/chain/hardfork.d/1000.hf
Normal file
4
libraries/chain/hardfork.d/1000.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// #615 Fix price feed expiration check, so websocket server will never spam too much data
|
||||
#ifndef HARDFORK_1000_TIME
|
||||
#define HARDFORK_1000_TIME (fc::time_point_sec( 1523534400 ))
|
||||
#endif
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// #599 Unpacking of extension is incorrect
|
||||
#ifndef HARDFORK_599_TIME
|
||||
#define HARDFORK_599_TIME (fc::time_point_sec( 1458061200 ))
|
||||
#define HARDFORK_599_TIME (fc::time_point_sec( 1459789200 ))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// #607 Disable negative voting on workers
|
||||
#ifndef HARDFORK_607_TIME
|
||||
#define HARDFORK_607_TIME (fc::time_point_sec( 1458061200 ))
|
||||
#define HARDFORK_607_TIME (fc::time_point_sec( 1458752400 ))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// #613 Deprecate annual membership
|
||||
#ifndef HARDFORK_613_TIME
|
||||
#define HARDFORK_613_TIME (fc::time_point_sec( 1458061200 ))
|
||||
#define HARDFORK_613_TIME (fc::time_point_sec( 1458752400 ))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// #615 Fix price feed expiration check, so websocket server will never spam too much data
|
||||
#ifndef HARDFORK_615_TIME
|
||||
#define HARDFORK_615_TIME (fc::time_point_sec( 1457550000 ))
|
||||
#define HARDFORK_615_TIME (fc::time_point_sec( 1458752400 ))
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -454,7 +457,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)
|
||||
|
|
|
|||
|
|
@ -235,15 +235,18 @@ namespace graphene { namespace chain {
|
|||
>
|
||||
>
|
||||
> asset_bitasset_data_object_multi_index_type;
|
||||
typedef flat_index<asset_bitasset_data_object> asset_bitasset_data_index;
|
||||
//typedef flat_index<asset_bitasset_data_object> asset_bitasset_data_index;
|
||||
typedef generic_index<asset_bitasset_data_object, asset_bitasset_data_object_multi_index_type> asset_bitasset_data_index;
|
||||
|
||||
struct by_symbol;
|
||||
struct by_type;
|
||||
struct by_issuer;
|
||||
typedef multi_index_container<
|
||||
asset_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag<by_symbol>, member<asset_object, string, &asset_object::symbol> >,
|
||||
ordered_non_unique< tag<by_issuer>, member<asset_object, account_id_type, &asset_object::issuer > >,
|
||||
ordered_unique< tag<by_type>,
|
||||
composite_key< asset_object,
|
||||
const_mem_fun<asset_object, bool, &asset_object::is_market_issued>,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class betting_market_rules_create_evaluator : public evaluator<betting_market_rules_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_rules_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const betting_market_rules_create_operation& o );
|
||||
object_id_type do_apply( const betting_market_rules_create_operation& o );
|
||||
};
|
||||
|
||||
class betting_market_rules_update_evaluator : public evaluator<betting_market_rules_update_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_rules_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const betting_market_rules_update_operation& o );
|
||||
void_result do_apply( const betting_market_rules_update_operation& o );
|
||||
private:
|
||||
const betting_market_rules_object* _rules;
|
||||
};
|
||||
|
||||
class betting_market_group_create_evaluator : public evaluator<betting_market_group_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_group_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const betting_market_group_create_operation& o);
|
||||
object_id_type do_apply(const betting_market_group_create_operation& o);
|
||||
private:
|
||||
event_id_type _event_id;
|
||||
betting_market_rules_id_type _rules_id;
|
||||
};
|
||||
|
||||
class betting_market_group_update_evaluator : public evaluator<betting_market_group_update_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_group_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const betting_market_group_update_operation& o);
|
||||
void_result do_apply(const betting_market_group_update_operation& o);
|
||||
private:
|
||||
betting_market_rules_id_type _rules_id;
|
||||
const betting_market_group_object* _betting_market_group;
|
||||
};
|
||||
|
||||
class betting_market_create_evaluator : public evaluator<betting_market_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const betting_market_create_operation& o );
|
||||
object_id_type do_apply( const betting_market_create_operation& o );
|
||||
private:
|
||||
betting_market_group_id_type _group_id;
|
||||
};
|
||||
|
||||
class betting_market_update_evaluator : public evaluator<betting_market_update_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const betting_market_update_operation& o );
|
||||
void_result do_apply( const betting_market_update_operation& o );
|
||||
private:
|
||||
const betting_market_object* _betting_market;
|
||||
betting_market_group_id_type _group_id;
|
||||
};
|
||||
|
||||
class bet_place_evaluator : public evaluator<bet_place_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef bet_place_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const bet_place_operation& o );
|
||||
object_id_type do_apply( const bet_place_operation& o );
|
||||
private:
|
||||
const betting_market_group_object* _betting_market_group;
|
||||
const betting_market_object* _betting_market;
|
||||
const chain_parameters* _current_params;
|
||||
const asset_object* _asset;
|
||||
share_type _stake_plus_fees;
|
||||
};
|
||||
|
||||
class bet_cancel_evaluator : public evaluator<bet_cancel_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef bet_cancel_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const bet_cancel_operation& o );
|
||||
void_result do_apply( const bet_cancel_operation& o );
|
||||
private:
|
||||
const bet_object* _bet_to_cancel;
|
||||
};
|
||||
|
||||
class betting_market_group_resolve_evaluator : public evaluator<betting_market_group_resolve_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_group_resolve_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const betting_market_group_resolve_operation& o );
|
||||
void_result do_apply( const betting_market_group_resolve_operation& o );
|
||||
private:
|
||||
const betting_market_group_object* _betting_market_group;
|
||||
};
|
||||
|
||||
class betting_market_group_cancel_unmatched_bets_evaluator : public evaluator<betting_market_group_cancel_unmatched_bets_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef betting_market_group_cancel_unmatched_bets_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const betting_market_group_cancel_unmatched_bets_operation& o );
|
||||
void_result do_apply( const betting_market_group_cancel_unmatched_bets_operation& o );
|
||||
private:
|
||||
const betting_market_group_object* _betting_market_group;
|
||||
};
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
713
libraries/chain/include/graphene/chain/betting_market_object.hpp
Normal file
713
libraries/chain/include/graphene/chain/betting_market_object.hpp
Normal file
|
|
@ -0,0 +1,713 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <graphene/chain/protocol/betting_market.hpp>
|
||||
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class betting_market_object;
|
||||
class betting_market_group_object;
|
||||
} }
|
||||
|
||||
namespace fc {
|
||||
void to_variant(const graphene::chain::betting_market_object& betting_market_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::betting_market_object& betting_market_obj);
|
||||
void to_variant(const graphene::chain::betting_market_group_object& betting_market_group_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::betting_market_group_object& betting_market_group_obj);
|
||||
} //end namespace fc
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
FC_DECLARE_EXCEPTION(no_transition, 100000, "Invalid state transition");
|
||||
class database;
|
||||
|
||||
struct by_event_id;
|
||||
struct by_settling_time;
|
||||
struct by_betting_market_group_id;
|
||||
|
||||
class betting_market_rules_object : public graphene::db::abstract_object< betting_market_rules_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = betting_market_rules_object_type;
|
||||
|
||||
internationalized_string_type name;
|
||||
|
||||
internationalized_string_type description;
|
||||
};
|
||||
|
||||
class betting_market_group_object : public graphene::db::abstract_object< betting_market_group_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = betting_market_group_object_type;
|
||||
|
||||
betting_market_group_object();
|
||||
betting_market_group_object(const betting_market_group_object& rhs);
|
||||
~betting_market_group_object();
|
||||
betting_market_group_object& operator=(const betting_market_group_object& rhs);
|
||||
|
||||
internationalized_string_type description;
|
||||
|
||||
event_id_type event_id;
|
||||
|
||||
betting_market_rules_id_type rules_id;
|
||||
|
||||
asset_id_type asset_id;
|
||||
|
||||
share_type total_matched_bets_amount;
|
||||
|
||||
bool never_in_play;
|
||||
|
||||
uint32_t delay_before_settling;
|
||||
|
||||
fc::optional<fc::time_point_sec> settling_time; // the time the payout will occur (set after grading)
|
||||
|
||||
bool bets_are_allowed() const {
|
||||
return get_status() == betting_market_group_status::upcoming ||
|
||||
get_status() == betting_market_group_status::in_play;
|
||||
}
|
||||
|
||||
bool bets_are_delayed() const {
|
||||
return get_status() == betting_market_group_status::in_play;
|
||||
}
|
||||
|
||||
betting_market_group_status get_status() const;
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const betting_market_group_object& betting_market_group_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, betting_market_group_object& betting_market_group_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::betting_market_group_object& betting_market_group_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::betting_market_group_object& betting_market_group_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
|
||||
void on_upcoming_event(database& db);
|
||||
void on_in_play_event(database& db);
|
||||
void on_frozen_event(database& db);
|
||||
void on_closed_event(database& db, bool closed_by_event);
|
||||
void on_graded_event(database& db);
|
||||
void on_re_grading_event(database& db);
|
||||
void on_settled_event(database& db);
|
||||
void on_canceled_event(database& db, bool canceled_by_event);
|
||||
void dispatch_new_status(database& db, betting_market_group_status new_status);
|
||||
|
||||
private:
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
class betting_market_object : public graphene::db::abstract_object< betting_market_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = betting_market_object_type;
|
||||
|
||||
betting_market_object();
|
||||
betting_market_object(const betting_market_object& rhs);
|
||||
~betting_market_object();
|
||||
betting_market_object& operator=(const betting_market_object& rhs);
|
||||
|
||||
betting_market_group_id_type group_id;
|
||||
|
||||
internationalized_string_type description;
|
||||
|
||||
internationalized_string_type payout_condition;
|
||||
|
||||
// once the market is graded, this holds the proposed grading
|
||||
// after settling/canceling, this is the actual grading
|
||||
fc::optional<betting_market_resolution_type> resolution;
|
||||
|
||||
betting_market_status get_status() const;
|
||||
|
||||
void cancel_all_unmatched_bets(database& db) const;
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const betting_market_object& betting_market_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, betting_market_object& betting_market_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::betting_market_object& betting_market_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::betting_market_object& betting_market_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
|
||||
void on_unresolved_event(database& db);
|
||||
void on_frozen_event(database& db);
|
||||
void on_closed_event(database& db);
|
||||
void on_graded_event(database& db, betting_market_resolution_type new_grading);
|
||||
void on_settled_event(database& db);
|
||||
void on_canceled_event(database& db);
|
||||
private:
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
class bet_object : public graphene::db::abstract_object< bet_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = bet_object_type;
|
||||
|
||||
account_id_type bettor_id;
|
||||
|
||||
betting_market_id_type betting_market_id;
|
||||
|
||||
asset amount_to_bet;
|
||||
|
||||
bet_multiplier_type backer_multiplier;
|
||||
|
||||
bet_type back_or_lay;
|
||||
|
||||
fc::optional<fc::time_point_sec> end_of_delay;
|
||||
|
||||
static share_type get_approximate_matching_amount(share_type bet_amount, bet_multiplier_type backer_multiplier, bet_type back_or_lay, bool round_up = false);
|
||||
|
||||
// returns the amount of a bet that completely matches this bet
|
||||
share_type get_approximate_matching_amount(bool round_up = false) const;
|
||||
|
||||
static share_type get_exact_matching_amount(share_type bet_amount, bet_multiplier_type backer_multiplier, bet_type back_or_lay);
|
||||
share_type get_exact_matching_amount() const;
|
||||
|
||||
static std::pair<share_type, share_type> get_ratio(bet_multiplier_type backer_multiplier);
|
||||
std::pair<share_type, share_type> get_ratio() const;
|
||||
|
||||
// returns the minimum amount this bet could have that could be matched at these odds
|
||||
share_type get_minimum_matchable_amount() const;
|
||||
// returns the minimum amount another user could bet to match this bet at these odds
|
||||
share_type get_minimum_matching_amount() const;
|
||||
};
|
||||
|
||||
class betting_market_position_object : public graphene::db::abstract_object< betting_market_position_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_betting_market_position_object_type;
|
||||
|
||||
account_id_type bettor_id;
|
||||
|
||||
betting_market_id_type betting_market_id;
|
||||
|
||||
share_type pay_if_payout_condition;
|
||||
share_type pay_if_not_payout_condition;
|
||||
share_type pay_if_canceled;
|
||||
share_type pay_if_not_canceled;
|
||||
share_type fees_collected;
|
||||
|
||||
share_type reduce();
|
||||
};
|
||||
|
||||
typedef multi_index_container<
|
||||
betting_market_rules_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >
|
||||
> > betting_market_rules_object_multi_index_type;
|
||||
typedef generic_index<betting_market_rules_object, betting_market_rules_object_multi_index_type> betting_market_rules_object_index;
|
||||
|
||||
typedef multi_index_container<
|
||||
betting_market_group_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag<by_event_id>, member<betting_market_group_object, event_id_type, &betting_market_group_object::event_id> >,
|
||||
ordered_non_unique< tag<by_settling_time>, member<betting_market_group_object, fc::optional<fc::time_point_sec>, &betting_market_group_object::settling_time> >
|
||||
> > betting_market_group_object_multi_index_type;
|
||||
typedef generic_index<betting_market_group_object, betting_market_group_object_multi_index_type> betting_market_group_object_index;
|
||||
|
||||
typedef multi_index_container<
|
||||
betting_market_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag<by_betting_market_group_id>, member<betting_market_object, betting_market_group_id_type, &betting_market_object::group_id> >
|
||||
> > betting_market_object_multi_index_type;
|
||||
|
||||
typedef generic_index<betting_market_object, betting_market_object_multi_index_type> betting_market_object_index;
|
||||
|
||||
struct compare_bet_by_odds {
|
||||
bool operator()(const bet_object& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(lhs.end_of_delay, lhs.betting_market_id, lhs.back_or_lay, lhs.backer_multiplier, lhs.id,
|
||||
rhs.end_of_delay, rhs.betting_market_id, rhs.back_or_lay, rhs.backer_multiplier, rhs.id);
|
||||
}
|
||||
|
||||
template<typename T0>
|
||||
bool operator() (const std::tuple<T0>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(fc::optional<time_point_sec>(), std::get<0>(lhs), rhs.end_of_delay, rhs.betting_market_id);
|
||||
}
|
||||
|
||||
template<typename T0>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0>& rhs) const
|
||||
{
|
||||
return compare(lhs.end_of_delay, lhs.betting_market_id, fc::optional<time_point_sec>(), std::get<0>(rhs));
|
||||
}
|
||||
|
||||
template<typename T0, typename T1>
|
||||
bool operator() (const std::tuple<T0, T1>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(fc::optional<fc::time_point_sec>(), std::get<0>(lhs), std::get<1>(lhs), rhs.end_of_delay, rhs.betting_market_id, rhs.back_or_lay);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0, T1>& rhs) const
|
||||
{
|
||||
return compare(lhs.end_of_delay, lhs.betting_market_id, lhs.back_or_lay, fc::optional<time_point_sec>(), std::get<0>(rhs), std::get<1>(rhs));
|
||||
}
|
||||
|
||||
template<typename T0, typename T1, typename T2>
|
||||
bool operator() (const std::tuple<T0, T1, T2>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(fc::optional<time_point_sec>(), std::get<0>(lhs), std::get<1>(lhs), std::get<2>(lhs),
|
||||
rhs.end_of_delay, rhs.betting_market_id, rhs.back_or_lay, rhs.backer_multiplier);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1, typename T2>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0, T1, T2>& rhs) const
|
||||
{
|
||||
return compare(lhs.end_of_delay, lhs.betting_market_id, lhs.back_or_lay, lhs.backer_multiplier,
|
||||
fc::optional<time_point_sec>(), std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs));
|
||||
}
|
||||
template<typename T0, typename T1, typename T2, typename T3>
|
||||
bool operator() (const std::tuple<T0, T1, T2, T3>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(fc::optional<time_point_sec>(), std::get<0>(lhs), std::get<1>(lhs), std::get<2>(lhs), std::get<3>(lhs),
|
||||
rhs.end_of_delay, rhs.betting_market_id, rhs.back_or_lay, rhs.backer_multiplier, rhs.id);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1, typename T2, typename T3>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0, T1, T2, T3>& rhs) const
|
||||
{
|
||||
return compare(lhs.end_of_delay, lhs.betting_market_id, lhs.back_or_lay, lhs.backer_multiplier, lhs.id,
|
||||
fc::optional<time_point_sec>(), std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs), std::get<3>(rhs));
|
||||
}
|
||||
bool compare(const fc::optional<fc::time_point_sec>& lhs_end_of_delay,
|
||||
const betting_market_id_type& lhs_betting_market_id,
|
||||
const fc::optional<fc::time_point_sec>& rhs_end_of_delay,
|
||||
const betting_market_id_type& rhs_betting_market_id) const
|
||||
{
|
||||
// if either bet is delayed, sort the delayed bet to the
|
||||
// front. If both are delayed, the delay expiring soonest
|
||||
// comes first.
|
||||
if (lhs_end_of_delay || rhs_end_of_delay)
|
||||
{
|
||||
if (!rhs_end_of_delay)
|
||||
return true;
|
||||
if (!lhs_end_of_delay)
|
||||
return false;
|
||||
return *lhs_end_of_delay < *rhs_end_of_delay;
|
||||
}
|
||||
|
||||
return lhs_betting_market_id < rhs_betting_market_id;
|
||||
}
|
||||
bool compare(const fc::optional<fc::time_point_sec>& lhs_end_of_delay,
|
||||
const betting_market_id_type& lhs_betting_market_id, bet_type lhs_bet_type,
|
||||
const fc::optional<fc::time_point_sec>& rhs_end_of_delay,
|
||||
const betting_market_id_type& rhs_betting_market_id, bet_type rhs_bet_type) const
|
||||
{
|
||||
// if either bet is delayed, sort the delayed bet to the
|
||||
// front. If both are delayed, the delay expiring soonest
|
||||
// comes first.
|
||||
if (lhs_end_of_delay || rhs_end_of_delay)
|
||||
{
|
||||
if (!rhs_end_of_delay)
|
||||
return true;
|
||||
if (!lhs_end_of_delay)
|
||||
return false;
|
||||
return *lhs_end_of_delay < *rhs_end_of_delay;
|
||||
}
|
||||
|
||||
if (lhs_betting_market_id < rhs_betting_market_id)
|
||||
return true;
|
||||
if (lhs_betting_market_id > rhs_betting_market_id)
|
||||
return false;
|
||||
return lhs_bet_type < rhs_bet_type;
|
||||
}
|
||||
bool compare(const fc::optional<fc::time_point_sec>& lhs_end_of_delay,
|
||||
const betting_market_id_type& lhs_betting_market_id, bet_type lhs_bet_type,
|
||||
bet_multiplier_type lhs_backer_multiplier,
|
||||
const fc::optional<fc::time_point_sec>& rhs_end_of_delay,
|
||||
const betting_market_id_type& rhs_betting_market_id, bet_type rhs_bet_type,
|
||||
bet_multiplier_type rhs_backer_multiplier) const
|
||||
{
|
||||
// if either bet is delayed, sort the delayed bet to the
|
||||
// front. If both are delayed, the delay expiring soonest
|
||||
// comes first.
|
||||
if (lhs_end_of_delay || rhs_end_of_delay)
|
||||
{
|
||||
if (!rhs_end_of_delay)
|
||||
return true;
|
||||
if (!lhs_end_of_delay)
|
||||
return false;
|
||||
return *lhs_end_of_delay < *rhs_end_of_delay;
|
||||
}
|
||||
|
||||
if (lhs_betting_market_id < rhs_betting_market_id)
|
||||
return true;
|
||||
if (lhs_betting_market_id > rhs_betting_market_id)
|
||||
return false;
|
||||
if (lhs_bet_type < rhs_bet_type)
|
||||
return true;
|
||||
if (lhs_bet_type > rhs_bet_type)
|
||||
return false;
|
||||
if (lhs_bet_type == bet_type::back)
|
||||
return lhs_backer_multiplier < rhs_backer_multiplier;
|
||||
else
|
||||
return lhs_backer_multiplier > rhs_backer_multiplier;
|
||||
}
|
||||
bool compare(const fc::optional<fc::time_point_sec>& lhs_end_of_delay,
|
||||
const betting_market_id_type& lhs_betting_market_id, bet_type lhs_bet_type,
|
||||
bet_multiplier_type lhs_backer_multiplier, const bet_id_type& lhs_bet_id,
|
||||
const fc::optional<fc::time_point_sec>& rhs_end_of_delay,
|
||||
const betting_market_id_type& rhs_betting_market_id, bet_type rhs_bet_type,
|
||||
bet_multiplier_type rhs_backer_multiplier, const bet_id_type& rhs_bet_id) const
|
||||
{
|
||||
|
||||
// if either bet is delayed, sort the delayed bet to the
|
||||
// front. If both are delayed, the delay expiring soonest
|
||||
// comes first.
|
||||
if (lhs_end_of_delay || rhs_end_of_delay)
|
||||
{
|
||||
if (!rhs_end_of_delay)
|
||||
return true;
|
||||
if (!lhs_end_of_delay)
|
||||
return false;
|
||||
if (*lhs_end_of_delay < *rhs_end_of_delay)
|
||||
return true;
|
||||
if (*lhs_end_of_delay > *rhs_end_of_delay)
|
||||
return false;
|
||||
// if both bets have the same delay, prefer the one
|
||||
// that was placed first (lowest id)
|
||||
return lhs_bet_id < rhs_bet_id;
|
||||
}
|
||||
|
||||
// if neither bet was delayed
|
||||
if (lhs_betting_market_id < rhs_betting_market_id)
|
||||
return true;
|
||||
if (lhs_betting_market_id > rhs_betting_market_id)
|
||||
return false;
|
||||
if (lhs_bet_type < rhs_bet_type)
|
||||
return true;
|
||||
if (lhs_bet_type > rhs_bet_type)
|
||||
return false;
|
||||
if (lhs_backer_multiplier < rhs_backer_multiplier)
|
||||
return lhs_bet_type == bet_type::back;
|
||||
if (lhs_backer_multiplier > rhs_backer_multiplier)
|
||||
return lhs_bet_type == bet_type::lay;
|
||||
return lhs_bet_id < rhs_bet_id;
|
||||
}
|
||||
};
|
||||
|
||||
struct compare_bet_by_bettor_then_odds {
|
||||
bool operator()(const bet_object& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(lhs.bettor_id, lhs.betting_market_id, lhs.back_or_lay, lhs.backer_multiplier, lhs.id,
|
||||
rhs.bettor_id, rhs.betting_market_id, rhs.back_or_lay, rhs.backer_multiplier, rhs.id);
|
||||
}
|
||||
|
||||
template<typename T0>
|
||||
bool operator() (const std::tuple<T0>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(std::get<0>(lhs), rhs.bettor_id);
|
||||
}
|
||||
|
||||
template<typename T0>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0>& rhs) const
|
||||
{
|
||||
return compare(lhs.bettor_id, std::get<0>(rhs));
|
||||
}
|
||||
|
||||
template<typename T0, typename T1>
|
||||
bool operator() (const std::tuple<T0, T1>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(std::get<0>(lhs), std::get<1>(lhs), rhs.bettor_id, rhs.betting_market_id);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0, T1>& rhs) const
|
||||
{
|
||||
return compare(lhs.bettor_id, lhs.betting_market_id, std::get<0>(rhs), std::get<1>(rhs));
|
||||
}
|
||||
|
||||
template<typename T0, typename T1, typename T2>
|
||||
bool operator() (const std::tuple<T0, T1, T2>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(std::get<0>(lhs), std::get<1>(lhs), std::get<2>(lhs),
|
||||
rhs.bettor_id, rhs.betting_market_id, rhs.back_or_lay);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1, typename T2>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0, T1, T2>& rhs) const
|
||||
{
|
||||
return compare(lhs.bettor_id, lhs.betting_market_id, lhs.back_or_lay,
|
||||
std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs));
|
||||
}
|
||||
template<typename T0, typename T1, typename T2, typename T3>
|
||||
bool operator() (const std::tuple<T0, T1, T2, T3>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(std::get<0>(lhs), std::get<1>(lhs), std::get<2>(lhs), std::get<3>(lhs),
|
||||
rhs.bettor_id, rhs.betting_market_id, rhs.back_or_lay, rhs.backer_multiplier);
|
||||
}
|
||||
|
||||
template<typename T0, typename T1, typename T2, typename T3>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0, T1, T2, T3>& rhs) const
|
||||
{
|
||||
return compare(lhs.bettor_id, lhs.betting_market_id, lhs.back_or_lay, lhs.backer_multiplier, lhs.id,
|
||||
std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs), std::get<3>(rhs));
|
||||
}
|
||||
template<typename T0, typename T1, typename T2, typename T3, typename T4>
|
||||
bool operator() (const std::tuple<T0, T1, T2, T3, T4>& lhs, const bet_object& rhs) const
|
||||
{
|
||||
return compare(std::get<0>(lhs), std::get<1>(lhs), std::get<2>(lhs), std::get<3>(lhs), std::get<4>(lhs),
|
||||
rhs.bettor_id, rhs.betting_market_id, rhs.back_or_lay, rhs.backer_multiplier, rhs.id);
|
||||
}
|
||||
template<typename T0, typename T1, typename T2, typename T3, typename T4>
|
||||
bool operator() (const bet_object& lhs, const std::tuple<T0, T1, T2, T3, T4>& rhs) const
|
||||
{
|
||||
return compare(lhs.bettor_id, lhs.betting_market_id, lhs.back_or_lay, lhs.backer_multiplier, lhs.id,
|
||||
std::get<0>(rhs), std::get<1>(rhs), std::get<2>(rhs), std::get<3>(rhs), std::get<4>(rhs));
|
||||
}
|
||||
bool compare(const account_id_type& lhs_bettor_id, const account_id_type& rhs_bettor_id) const
|
||||
{
|
||||
return lhs_bettor_id < rhs_bettor_id;
|
||||
}
|
||||
bool compare(const account_id_type& lhs_bettor_id, const betting_market_id_type& lhs_betting_market_id,
|
||||
const account_id_type& rhs_bettor_id, const betting_market_id_type& rhs_betting_market_id) const
|
||||
{
|
||||
if (lhs_bettor_id < rhs_bettor_id)
|
||||
return true;
|
||||
if (lhs_bettor_id > rhs_bettor_id)
|
||||
return false;
|
||||
return lhs_betting_market_id < rhs_betting_market_id;
|
||||
}
|
||||
|
||||
bool compare(const account_id_type& lhs_bettor_id, const betting_market_id_type& lhs_betting_market_id,
|
||||
bet_type lhs_bet_type,
|
||||
const account_id_type& rhs_bettor_id, const betting_market_id_type& rhs_betting_market_id,
|
||||
bet_type rhs_bet_type) const
|
||||
{
|
||||
if (lhs_bettor_id < rhs_bettor_id)
|
||||
return true;
|
||||
if (lhs_bettor_id > rhs_bettor_id)
|
||||
return false;
|
||||
if (lhs_betting_market_id < rhs_betting_market_id)
|
||||
return true;
|
||||
if (lhs_betting_market_id > rhs_betting_market_id)
|
||||
return false;
|
||||
return lhs_bet_type < rhs_bet_type;
|
||||
}
|
||||
bool compare(const account_id_type& lhs_bettor_id, const betting_market_id_type& lhs_betting_market_id,
|
||||
bet_type lhs_bet_type,
|
||||
bet_multiplier_type lhs_backer_multiplier,
|
||||
const account_id_type& rhs_bettor_id, const betting_market_id_type& rhs_betting_market_id,
|
||||
bet_type rhs_bet_type,
|
||||
bet_multiplier_type rhs_backer_multiplier) const
|
||||
{
|
||||
if (lhs_bettor_id < rhs_bettor_id)
|
||||
return true;
|
||||
if (lhs_bettor_id > rhs_bettor_id)
|
||||
return false;
|
||||
if (lhs_betting_market_id < rhs_betting_market_id)
|
||||
return true;
|
||||
if (lhs_betting_market_id > rhs_betting_market_id)
|
||||
return false;
|
||||
if (lhs_bet_type < rhs_bet_type)
|
||||
return true;
|
||||
if (lhs_bet_type > rhs_bet_type)
|
||||
return false;
|
||||
if (lhs_bet_type == bet_type::back)
|
||||
return lhs_backer_multiplier < rhs_backer_multiplier;
|
||||
else
|
||||
return lhs_backer_multiplier > rhs_backer_multiplier;
|
||||
}
|
||||
bool compare(const account_id_type& lhs_bettor_id, const betting_market_id_type& lhs_betting_market_id,
|
||||
bet_type lhs_bet_type,
|
||||
bet_multiplier_type lhs_backer_multiplier, const bet_id_type& lhs_bet_id,
|
||||
const account_id_type& rhs_bettor_id, const betting_market_id_type& rhs_betting_market_id,
|
||||
bet_type rhs_bet_type,
|
||||
bet_multiplier_type rhs_backer_multiplier, const bet_id_type& rhs_bet_id) const
|
||||
{
|
||||
if (lhs_bettor_id < rhs_bettor_id)
|
||||
return true;
|
||||
if (lhs_bettor_id > rhs_bettor_id)
|
||||
return false;
|
||||
if (lhs_betting_market_id < rhs_betting_market_id)
|
||||
return true;
|
||||
if (lhs_betting_market_id > rhs_betting_market_id)
|
||||
return false;
|
||||
if (lhs_bet_type < rhs_bet_type)
|
||||
return true;
|
||||
if (lhs_bet_type > rhs_bet_type)
|
||||
return false;
|
||||
if (lhs_backer_multiplier < rhs_backer_multiplier)
|
||||
return lhs_bet_type == bet_type::back;
|
||||
if (lhs_backer_multiplier > rhs_backer_multiplier)
|
||||
return lhs_bet_type == bet_type::lay;
|
||||
|
||||
return lhs_bet_id < rhs_bet_id;
|
||||
}
|
||||
};
|
||||
|
||||
struct by_odds {};
|
||||
struct by_bettor_and_odds {};
|
||||
typedef multi_index_container<
|
||||
bet_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag<by_odds>, identity<bet_object>, compare_bet_by_odds >,
|
||||
ordered_unique< tag<by_bettor_and_odds>, identity<bet_object>, compare_bet_by_bettor_then_odds > > > bet_object_multi_index_type;
|
||||
typedef generic_index<bet_object, bet_object_multi_index_type> bet_object_index;
|
||||
|
||||
struct by_bettor_betting_market{};
|
||||
struct by_betting_market_bettor{};
|
||||
typedef multi_index_container<
|
||||
betting_market_position_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag<by_bettor_betting_market>,
|
||||
composite_key<
|
||||
betting_market_position_object,
|
||||
member<betting_market_position_object, account_id_type, &betting_market_position_object::bettor_id>,
|
||||
member<betting_market_position_object, betting_market_id_type, &betting_market_position_object::betting_market_id> > >,
|
||||
ordered_unique< tag<by_betting_market_bettor>,
|
||||
composite_key<
|
||||
betting_market_position_object,
|
||||
member<betting_market_position_object, betting_market_id_type, &betting_market_position_object::betting_market_id>,
|
||||
member<betting_market_position_object, account_id_type, &betting_market_position_object::bettor_id> > >
|
||||
> > betting_market_position_multi_index_type;
|
||||
|
||||
typedef generic_index<betting_market_position_object, betting_market_position_multi_index_type> betting_market_position_index;
|
||||
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator<<( Stream& s, const betting_market_object& betting_market_obj )
|
||||
{
|
||||
// pack all fields exposed in the header in the usual way
|
||||
// instead of calling the derived pack, just serialize the one field in the base class
|
||||
// fc::raw::pack<Stream, const graphene::db::abstract_object<betting_market_object> >(s, betting_market_obj);
|
||||
fc::raw::pack(s, betting_market_obj.id);
|
||||
fc::raw::pack(s, betting_market_obj.group_id);
|
||||
fc::raw::pack(s, betting_market_obj.description);
|
||||
fc::raw::pack(s, betting_market_obj.payout_condition);
|
||||
fc::raw::pack(s, betting_market_obj.resolution);
|
||||
|
||||
// fc::raw::pack the contents hidden in the impl class
|
||||
std::ostringstream stream;
|
||||
betting_market_obj.pack_impl(stream);
|
||||
std::string stringified_stream(stream.str());
|
||||
fc::raw::pack(s, stream.str());
|
||||
|
||||
return s;
|
||||
}
|
||||
template<typename Stream>
|
||||
inline Stream& operator>>( Stream& s, betting_market_object& betting_market_obj )
|
||||
{
|
||||
// unpack all fields exposed in the header in the usual way
|
||||
//fc::raw::unpack<Stream, graphene::db::abstract_object<betting_market_object> >(s, betting_market_obj);
|
||||
fc::raw::unpack(s, betting_market_obj.id);
|
||||
fc::raw::unpack(s, betting_market_obj.group_id);
|
||||
fc::raw::unpack(s, betting_market_obj.description);
|
||||
fc::raw::unpack(s, betting_market_obj.payout_condition);
|
||||
fc::raw::unpack(s, betting_market_obj.resolution);
|
||||
|
||||
// fc::raw::unpack the contents hidden in the impl class
|
||||
std::string stringified_stream;
|
||||
fc::raw::unpack(s, stringified_stream);
|
||||
std::istringstream stream(stringified_stream);
|
||||
betting_market_obj.unpack_impl(stream);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
template<typename Stream>
|
||||
inline Stream& operator<<( Stream& s, const betting_market_group_object& betting_market_group_obj )
|
||||
{
|
||||
// pack all fields exposed in the header in the usual way
|
||||
// instead of calling the derived pack, just serialize the one field in the base class
|
||||
// fc::raw::pack<Stream, const graphene::db::abstract_object<betting_market_group_object> >(s, betting_market_group_obj);
|
||||
fc::raw::pack(s, betting_market_group_obj.id);
|
||||
fc::raw::pack(s, betting_market_group_obj.description);
|
||||
fc::raw::pack(s, betting_market_group_obj.event_id);
|
||||
fc::raw::pack(s, betting_market_group_obj.rules_id);
|
||||
fc::raw::pack(s, betting_market_group_obj.asset_id);
|
||||
fc::raw::pack(s, betting_market_group_obj.total_matched_bets_amount);
|
||||
fc::raw::pack(s, betting_market_group_obj.never_in_play);
|
||||
fc::raw::pack(s, betting_market_group_obj.delay_before_settling);
|
||||
fc::raw::pack(s, betting_market_group_obj.settling_time);
|
||||
// fc::raw::pack the contents hidden in the impl class
|
||||
std::ostringstream stream;
|
||||
betting_market_group_obj.pack_impl(stream);
|
||||
std::string stringified_stream(stream.str());
|
||||
fc::raw::pack(s, stream.str());
|
||||
|
||||
return s;
|
||||
}
|
||||
template<typename Stream>
|
||||
inline Stream& operator>>( Stream& s, betting_market_group_object& betting_market_group_obj )
|
||||
{
|
||||
// unpack all fields exposed in the header in the usual way
|
||||
//fc::raw::unpack<Stream, graphene::db::abstract_object<betting_market_group_object> >(s, betting_market_group_obj);
|
||||
fc::raw::unpack(s, betting_market_group_obj.id);
|
||||
fc::raw::unpack(s, betting_market_group_obj.description);
|
||||
fc::raw::unpack(s, betting_market_group_obj.event_id);
|
||||
fc::raw::unpack(s, betting_market_group_obj.rules_id);
|
||||
fc::raw::unpack(s, betting_market_group_obj.asset_id);
|
||||
fc::raw::unpack(s, betting_market_group_obj.total_matched_bets_amount);
|
||||
fc::raw::unpack(s, betting_market_group_obj.never_in_play);
|
||||
fc::raw::unpack(s, betting_market_group_obj.delay_before_settling);
|
||||
fc::raw::unpack(s, betting_market_group_obj.settling_time);
|
||||
|
||||
// fc::raw::unpack the contents hidden in the impl class
|
||||
std::string stringified_stream;
|
||||
fc::raw::unpack(s, stringified_stream);
|
||||
std::istringstream stream(stringified_stream);
|
||||
betting_market_group_obj.unpack_impl(stream);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_rules_object, (graphene::db::object), (name)(description) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_group_object, (graphene::db::object), (description) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_object, (graphene::db::object), (group_id) )
|
||||
FC_REFLECT_DERIVED( graphene::chain::bet_object, (graphene::db::object), (bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(back_or_lay)(end_of_delay) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::betting_market_position_object, (graphene::db::object), (bettor_id)(betting_market_id)(pay_if_payout_condition)(pay_if_not_payout_condition)(pay_if_canceled)(pay_if_not_canceled)(fees_collected) )
|
||||
|
|
@ -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 "PPY1.11"
|
||||
|
||||
#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
|
||||
|
|
|
|||
|
|
@ -171,8 +171,18 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
uint32_t push_applied_operation( const operation& op );
|
||||
void set_applied_operation_result( uint32_t op_id, const operation_result& r );
|
||||
|
||||
// most plugins should use the const version of get_applied_operations
|
||||
const vector<optional< operation_history_object > >& get_applied_operations()const;
|
||||
|
||||
// the account_history plugin uses the non-const version. When it decides to track an
|
||||
// operation and assigns an operation_id to it, it will store that id into the operation
|
||||
// history object so other plugins that evaluate later can reference it.
|
||||
vector<optional< operation_history_object > >& get_applied_operations();
|
||||
|
||||
// the bookie plugin depends on change notifications that are skipped during normal replays
|
||||
void force_slow_replays();
|
||||
|
||||
string to_pretty_string( const asset& a )const;
|
||||
|
||||
/**
|
||||
|
|
@ -195,12 +205,18 @@ namespace graphene { namespace chain {
|
|||
* Emitted After a block has been applied and committed. The callback
|
||||
* should not yield and should execute quickly.
|
||||
*/
|
||||
fc::signal<void(const vector<object_id_type>&)> changed_objects;
|
||||
fc::signal<void(const vector<object_id_type>&, const flat_set<account_id_type>&)> new_objects;
|
||||
|
||||
/**
|
||||
* Emitted After a block has been applied and committed. The callback
|
||||
* should not yield and should execute quickly.
|
||||
*/
|
||||
fc::signal<void(const vector<object_id_type>&, const flat_set<account_id_type>&)> changed_objects;
|
||||
|
||||
/** this signal is emitted any time an object is removed and contains a
|
||||
* pointer to the last value of every object that was removed.
|
||||
*/
|
||||
fc::signal<void(const vector<const object*>&)> removed_objects;
|
||||
fc::signal<void(const vector<object_id_type>&, const vector<const object*>&, const flat_set<account_id_type>&)> removed_objects;
|
||||
|
||||
//////////////////// db_witness_schedule.cpp ////////////////////
|
||||
|
||||
|
|
@ -369,6 +385,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.
|
||||
*/
|
||||
|
|
@ -435,12 +474,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
|
||||
|
|
@ -497,6 +538,7 @@ namespace graphene { namespace chain {
|
|||
|
||||
node_property_object _node_property_object;
|
||||
fc::hash_ctr_rng<secret_hash_type, 20> _random_number_generator;
|
||||
bool _slow_replays = false;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
|
|
|
|||
65
libraries/chain/include/graphene/chain/event_evaluator.hpp
Normal file
65
libraries/chain/include/graphene/chain/event_evaluator.hpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class event_create_evaluator : public evaluator<event_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef event_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const event_create_operation& o );
|
||||
object_id_type do_apply( const event_create_operation& o );
|
||||
private:
|
||||
event_group_id_type event_group_id;
|
||||
};
|
||||
|
||||
class event_update_evaluator : public evaluator<event_update_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef event_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const event_update_operation& o );
|
||||
void_result do_apply( const event_update_operation& o );
|
||||
private:
|
||||
event_group_id_type event_group_id;
|
||||
};
|
||||
|
||||
class event_update_status_evaluator : public evaluator<event_update_status_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef event_update_status_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const event_update_status_operation& o );
|
||||
void_result do_apply( const event_update_status_operation& o );
|
||||
private:
|
||||
const event_object* _event_to_update = nullptr;
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class event_group_create_evaluator : public evaluator<event_group_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef event_group_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const event_group_create_operation& o );
|
||||
object_id_type do_apply( const event_group_create_operation& o );
|
||||
|
||||
private:
|
||||
sport_id_type sport_id;
|
||||
};
|
||||
|
||||
class event_group_update_evaluator : public evaluator<event_group_update_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef event_group_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const event_group_update_operation& o );
|
||||
void_result do_apply( const event_group_update_operation& o );
|
||||
|
||||
private:
|
||||
sport_id_type sport_id;
|
||||
};
|
||||
} } // graphene::chain
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class database;
|
||||
|
||||
struct by_sport_id;
|
||||
|
||||
class event_group_object : public graphene::db::abstract_object< event_group_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = event_group_object_type;
|
||||
|
||||
internationalized_string_type name;
|
||||
sport_id_type sport_id;
|
||||
};
|
||||
|
||||
typedef multi_index_container<
|
||||
event_group_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag<by_sport_id>, member< event_group_object, sport_id_type, &event_group_object::sport_id > > >
|
||||
> event_group_object_multi_index_type;
|
||||
|
||||
typedef generic_index<event_group_object, event_group_object_multi_index_type> event_group_object_index;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::event_group_object, (graphene::db::object), (name)(sport_id) )
|
||||
155
libraries/chain/include/graphene/chain/event_object.hpp
Normal file
155
libraries/chain/include/graphene/chain/event_object.hpp
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <graphene/chain/protocol/event.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
class event_object;
|
||||
} }
|
||||
|
||||
namespace fc {
|
||||
void to_variant(const graphene::chain::event_object& event_obj, fc::variant& v);
|
||||
void from_variant(const fc::variant& v, graphene::chain::event_object& event_obj);
|
||||
} //end namespace fc
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class database;
|
||||
|
||||
class event_object : public graphene::db::abstract_object< event_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = event_object_type;
|
||||
|
||||
event_object();
|
||||
event_object(const event_object& rhs);
|
||||
~event_object();
|
||||
event_object& operator=(const event_object& rhs);
|
||||
|
||||
internationalized_string_type name;
|
||||
|
||||
internationalized_string_type season;
|
||||
|
||||
optional<time_point_sec> start_time;
|
||||
|
||||
event_group_id_type event_group_id;
|
||||
|
||||
bool at_least_one_betting_market_group_settled;
|
||||
|
||||
event_status get_status() const;
|
||||
vector<string> scores;
|
||||
|
||||
// serialization functions:
|
||||
// for serializing to raw, go through a temporary sstream object to avoid
|
||||
// having to implement serialization in the header file
|
||||
template<typename Stream>
|
||||
friend Stream& operator<<( Stream& s, const event_object& event_obj );
|
||||
|
||||
template<typename Stream>
|
||||
friend Stream& operator>>( Stream& s, event_object& event_obj );
|
||||
|
||||
friend void ::fc::to_variant(const graphene::chain::event_object& event_obj, fc::variant& v);
|
||||
friend void ::fc::from_variant(const fc::variant& v, graphene::chain::event_object& event_obj);
|
||||
|
||||
void pack_impl(std::ostream& stream) const;
|
||||
void unpack_impl(std::istream& stream);
|
||||
|
||||
void on_upcoming_event(database& db);
|
||||
void on_in_progress_event(database& db);
|
||||
void on_frozen_event(database& db);
|
||||
void on_finished_event(database& db);
|
||||
void on_canceled_event(database& db);
|
||||
void on_settled_event(database& db);
|
||||
void on_betting_market_group_resolved(database& db, betting_market_group_id_type resolved_group, bool was_canceled);
|
||||
void on_betting_market_group_closed(database& db, betting_market_group_id_type closed_group);
|
||||
void dispatch_new_status(database& db, event_status new_status);
|
||||
private:
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
struct by_event_group_id;
|
||||
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_non_unique< tag<by_event_group_id>, member< event_object, event_group_id_type, &event_object::event_group_id > >,
|
||||
ordered_non_unique< tag<by_event_status>, const_mem_fun< event_object, event_status, &event_object::get_status > > > > 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))
|
||||
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class database;
|
||||
|
||||
class global_betting_statistics_object : public graphene::db::abstract_object< global_betting_statistics_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_global_betting_statistics_object_type;
|
||||
|
||||
uint32_t number_of_active_events;
|
||||
map<asset_id_type, share_type> total_amount_staked;
|
||||
};
|
||||
|
||||
typedef multi_index_container<
|
||||
global_betting_statistics_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > > > > global_betting_statistics_object_multi_index_type;
|
||||
typedef generic_index<global_betting_statistics_object, global_betting_statistics_object_multi_index_type> global_betting_statistics_object_index;
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::global_betting_statistics_object, (graphene::db::object), (number_of_active_events)(total_amount_staked) )
|
||||
|
|
@ -120,6 +120,13 @@ class call_order_object : public abstract_object<call_order_object>
|
|||
share_type collateral; ///< call_price.base.asset_id, access via get_collateral
|
||||
share_type debt; ///< call_price.quote.asset_id, access via get_collateral
|
||||
price call_price; ///< Debt / Collateral
|
||||
|
||||
pair<asset_id_type,asset_id_type> get_market()const
|
||||
{
|
||||
auto tmp = std::make_pair( call_price.base.asset_id, call_price.quote.asset_id );
|
||||
if( tmp.first > tmp.second ) std::swap( tmp.first, tmp.second );
|
||||
return tmp;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ namespace graphene { namespace chain {
|
|||
struct by_id;
|
||||
struct by_seq;
|
||||
struct by_op;
|
||||
struct by_opid;
|
||||
typedef multi_index_container<
|
||||
account_transaction_history_object,
|
||||
indexed_by<
|
||||
|
|
@ -117,6 +118,9 @@ typedef multi_index_container<
|
|||
member< account_transaction_history_object, account_id_type, &account_transaction_history_object::account>,
|
||||
member< account_transaction_history_object, operation_history_id_type, &account_transaction_history_object::operation_id>
|
||||
>
|
||||
>,
|
||||
ordered_non_unique< tag<by_opid>,
|
||||
member< account_transaction_history_object, operation_history_id_type, &account_transaction_history_object::operation_id>
|
||||
>
|
||||
>
|
||||
> account_transaction_history_multi_index_type;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ class proposal_object : public abstract_object<proposal_object>
|
|||
flat_set<account_id_type> required_owner_approvals;
|
||||
flat_set<account_id_type> available_owner_approvals;
|
||||
flat_set<public_key_type> available_key_approvals;
|
||||
account_id_type proposer;
|
||||
|
||||
bool is_authorized_to_execute(database& db)const;
|
||||
};
|
||||
|
|
@ -93,4 +94,4 @@ typedef generic_index<proposal_object, proposal_multi_index_container> proposal_
|
|||
FC_REFLECT_DERIVED( graphene::chain::proposal_object, (graphene::chain::object),
|
||||
(expiration_time)(review_period_time)(proposed_transaction)(required_active_approvals)
|
||||
(available_active_approvals)(required_owner_approvals)(available_owner_approvals)
|
||||
(available_key_approvals) )
|
||||
(available_key_approvals)(proposer) )
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ namespace graphene { namespace chain {
|
|||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::account_options, (memo_key)(voting_account)(num_witness)(num_committee)(votes)(extensions))
|
||||
FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing)
|
||||
// FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing)
|
||||
FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing,
|
||||
(no_listing)(white_listed)(black_listed)(white_and_black_listed))
|
||||
|
||||
|
|
|
|||
|
|
@ -291,7 +291,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)
|
||||
|
|
@ -323,7 +323,7 @@ namespace graphene { namespace chain {
|
|||
account_id_type account_id;
|
||||
|
||||
/// The amounts received
|
||||
flat_set<asset> amounts;
|
||||
vector<asset> amounts;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
|
|
|
|||
|
|
@ -132,5 +132,5 @@ void add_authority_accounts(
|
|||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::authority, (weight_threshold)(account_auths)(key_auths)(address_auths) )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::authority::classification )
|
||||
// FC_REFLECT_TYPENAME( graphene::chain::authority::classification )
|
||||
FC_REFLECT_ENUM( graphene::chain::authority::classification, (owner)(active)(key) )
|
||||
|
|
|
|||
|
|
@ -0,0 +1,493 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct betting_market_rules_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
/**
|
||||
* A short name for the rules, like "Premier League Rules 1.0", probably not
|
||||
* displayed to the user
|
||||
*/
|
||||
internationalized_string_type name;
|
||||
|
||||
/**
|
||||
* The full text of the rules to be displayed to the user. As yet, there is
|
||||
* no standard format (html, markdown, etc)
|
||||
*/
|
||||
internationalized_string_type description;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
|
||||
struct betting_market_rules_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
betting_market_rules_id_type betting_market_rules_id;
|
||||
|
||||
fc::optional<internationalized_string_type> new_name;
|
||||
|
||||
fc::optional<internationalized_string_type> new_description;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
enum class betting_market_status
|
||||
{
|
||||
unresolved, /// no grading has been published for this betting market
|
||||
frozen, /// bets are suspended, no bets allowed
|
||||
graded, /// grading of win or not_win has been published
|
||||
canceled, /// the betting market is canceled, no further bets are allowed
|
||||
settled, /// the betting market has been paid out
|
||||
BETTING_MARKET_STATUS_COUNT
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The status of a betting market group. This controls the behavior of the betting
|
||||
* markets in the group.
|
||||
*/
|
||||
enum class betting_market_group_status
|
||||
{
|
||||
upcoming, /// betting markets are accepting bets, will never go "in_play"
|
||||
in_play, /// betting markets are delaying bets
|
||||
closed, /// betting markets are no longer accepting bets
|
||||
graded, /// witnesses have published win/not win for the betting markets
|
||||
re_grading, /// initial win/not win grading has been challenged
|
||||
settled, /// paid out
|
||||
frozen, /// betting markets are not accepting bets
|
||||
canceled, /// canceled
|
||||
BETTING_MARKET_GROUP_STATUS_COUNT
|
||||
};
|
||||
|
||||
|
||||
struct betting_market_group_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
/**
|
||||
* A description of the betting market, like "Moneyline", "Over/Under 180",
|
||||
* used for display
|
||||
*/
|
||||
internationalized_string_type description;
|
||||
|
||||
/**
|
||||
* This can be a event_id_type, or a
|
||||
* relative object id that resolves to a event_id_type
|
||||
*/
|
||||
object_id_type event_id;
|
||||
|
||||
/**
|
||||
* This can be a betting_market_rules_id_type, or a
|
||||
* relative object id that resolves to a betting_market_rules_id_type
|
||||
*/
|
||||
object_id_type rules_id;
|
||||
|
||||
/**
|
||||
* The asset used to place bets for all betting markets in this group
|
||||
*/
|
||||
asset_id_type asset_id;
|
||||
|
||||
/**
|
||||
* If true, this market will never go "in-play"
|
||||
*/
|
||||
bool never_in_play;
|
||||
|
||||
/**
|
||||
* After a grading has been published, the blockchain will wait this many
|
||||
* seconds before settling (paying the winners).
|
||||
* If the published grading is flagged (challenged) during this period,
|
||||
* settling will be delayed indefinitely until the betting market
|
||||
* group is re-graded (not implemented)
|
||||
*/
|
||||
uint32_t delay_before_settling;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct betting_market_group_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
betting_market_group_id_type betting_market_group_id;
|
||||
|
||||
optional<internationalized_string_type> new_description;
|
||||
|
||||
optional<object_id_type> new_rules_id;
|
||||
|
||||
optional<betting_market_group_status> status;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct betting_market_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
/**
|
||||
* This can be a betting_market_group_id_type, or a
|
||||
* relative object id that resolves to a betting_market_group_id_type
|
||||
*/
|
||||
object_id_type group_id;
|
||||
|
||||
internationalized_string_type description;
|
||||
|
||||
internationalized_string_type payout_condition;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct betting_market_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
betting_market_id_type betting_market_id;
|
||||
|
||||
optional<object_id_type> new_group_id;
|
||||
|
||||
optional<internationalized_string_type> new_description;
|
||||
|
||||
optional<internationalized_string_type> new_payout_condition;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
enum class betting_market_resolution_type {
|
||||
win,
|
||||
not_win,
|
||||
cancel,
|
||||
BETTING_MARKET_RESOLUTION_COUNT
|
||||
};
|
||||
|
||||
struct betting_market_group_resolve_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
betting_market_group_id_type betting_market_group_id;
|
||||
|
||||
std::map<betting_market_id_type, betting_market_resolution_type> resolutions;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct betting_market_group_resolved_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {};
|
||||
|
||||
account_id_type bettor_id;
|
||||
betting_market_group_id_type betting_market_group_id;
|
||||
std::map<betting_market_id_type, betting_market_resolution_type> resolutions;
|
||||
|
||||
share_type winnings; // always the asset type of the betting market group
|
||||
share_type fees_paid; // always the asset type of the betting market group
|
||||
|
||||
asset fee; // unused in a virtual operation
|
||||
|
||||
betting_market_group_resolved_operation() {}
|
||||
betting_market_group_resolved_operation(account_id_type bettor_id,
|
||||
betting_market_group_id_type betting_market_group_id,
|
||||
const std::map<betting_market_id_type, betting_market_resolution_type>& resolutions,
|
||||
share_type winnings,
|
||||
share_type fees_paid) :
|
||||
bettor_id(bettor_id),
|
||||
betting_market_group_id(betting_market_group_id),
|
||||
resolutions(resolutions),
|
||||
winnings(winnings),
|
||||
fees_paid(fees_paid)
|
||||
{
|
||||
}
|
||||
|
||||
account_id_type fee_payer()const { return bettor_id; }
|
||||
void validate()const { FC_ASSERT(false, "virtual operation"); }
|
||||
/// This is a virtual operation; there is no fee
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct betting_market_group_cancel_unmatched_bets_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
betting_market_group_id_type betting_market_group_id;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
enum class bet_type { back, lay };
|
||||
|
||||
struct bet_place_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type
|
||||
{
|
||||
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; // fixed fee charged upon placing the bet
|
||||
};
|
||||
asset fee;
|
||||
|
||||
account_id_type bettor_id;
|
||||
|
||||
betting_market_id_type betting_market_id;
|
||||
|
||||
/// the bettor's stake
|
||||
asset amount_to_bet;
|
||||
|
||||
// decimal odds as seen by the backer, even if this is a lay bet.
|
||||
// this is a fixed-precision number scaled by GRAPHENE_BETTING_ODDS_PRECISION.
|
||||
//
|
||||
// For example, an even 1/1 bet would be decimal odds 2.0, so backer_multiplier
|
||||
// would be 2 * GRAPHENE_BETTING_ODDS_PRECISION.
|
||||
bet_multiplier_type backer_multiplier;
|
||||
|
||||
bet_type back_or_lay;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return bettor_id; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* virtual op generated when a bet is matched
|
||||
*/
|
||||
struct bet_matched_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {};
|
||||
|
||||
bet_matched_operation(){}
|
||||
bet_matched_operation(account_id_type bettor_id, bet_id_type bet_id,
|
||||
asset amount_bet,
|
||||
bet_multiplier_type backer_multiplier,
|
||||
share_type guaranteed_winnings_returned) :
|
||||
bettor_id(bettor_id),
|
||||
bet_id(bet_id),
|
||||
amount_bet(amount_bet),
|
||||
backer_multiplier(backer_multiplier),
|
||||
guaranteed_winnings_returned(guaranteed_winnings_returned)
|
||||
{}
|
||||
|
||||
account_id_type bettor_id;
|
||||
bet_id_type bet_id;
|
||||
asset amount_bet;
|
||||
bet_multiplier_type backer_multiplier; // the actual odds received
|
||||
share_type guaranteed_winnings_returned; // same asset type as amount_bet
|
||||
asset fee; // unimportant for a virtual op
|
||||
|
||||
account_id_type fee_payer()const { return bettor_id; }
|
||||
void validate()const { FC_ASSERT(false, "virtual operation"); }
|
||||
|
||||
/// This is a virtual operation; there is no fee
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct bet_cancel_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type
|
||||
{
|
||||
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
//uint64_t fee = 0;
|
||||
};
|
||||
asset fee;
|
||||
|
||||
/// the bettor who is cancelling the bet
|
||||
account_id_type bettor_id;
|
||||
/// the bet being canceled
|
||||
bet_id_type bet_to_cancel;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return bettor_id; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* virtual op generated when a bet is canceled
|
||||
*/
|
||||
struct bet_canceled_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {};
|
||||
|
||||
bet_canceled_operation(){}
|
||||
bet_canceled_operation(account_id_type bettor_id, bet_id_type bet_id,
|
||||
asset stake_returned) :
|
||||
bettor_id(bettor_id),
|
||||
bet_id(bet_id),
|
||||
stake_returned(stake_returned)
|
||||
{}
|
||||
|
||||
account_id_type bettor_id;
|
||||
bet_id_type bet_id;
|
||||
asset stake_returned;
|
||||
asset fee; // unimportant for a virtual op
|
||||
|
||||
account_id_type fee_payer()const { return bettor_id; }
|
||||
void validate()const { FC_ASSERT(false, "virtual operation"); }
|
||||
|
||||
/// This is a virtual operation; there is no fee
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* virtual op generated when a bet amount is rounded down to an amount that can
|
||||
* match evenly at a given odds (the blockchain does this automatically at the time
|
||||
* the bet is placed on the order books). (note: there is no way a user can adjust their bet
|
||||
* after placing it, aside from canceling the bet and placing a new one)
|
||||
*/
|
||||
struct bet_adjusted_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {};
|
||||
|
||||
bet_adjusted_operation(){}
|
||||
bet_adjusted_operation(account_id_type bettor_id, bet_id_type bet_id,
|
||||
asset stake_returned) :
|
||||
bettor_id(bettor_id),
|
||||
bet_id(bet_id),
|
||||
stake_returned(stake_returned)
|
||||
{}
|
||||
|
||||
account_id_type bettor_id;
|
||||
bet_id_type bet_id;
|
||||
asset stake_returned;
|
||||
asset fee; // unimportant for a virtual op
|
||||
|
||||
account_id_type fee_payer()const { return bettor_id; }
|
||||
void validate()const { FC_ASSERT(false, "virtual operation"); }
|
||||
|
||||
/// This is a virtual operation; there is no fee
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_rules_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_rules_create_operation,
|
||||
(fee)(name)(description)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_rules_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_rules_update_operation,
|
||||
(fee)(new_name)(new_description)(extensions)(betting_market_rules_id) )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::betting_market_status,
|
||||
(unresolved)
|
||||
(frozen)
|
||||
(graded)
|
||||
(canceled)
|
||||
(settled)
|
||||
(BETTING_MARKET_STATUS_COUNT) )
|
||||
FC_REFLECT_ENUM( graphene::chain::betting_market_group_status,
|
||||
(upcoming)
|
||||
(in_play)
|
||||
(closed)
|
||||
(graded)
|
||||
(re_grading)
|
||||
(settled)
|
||||
(frozen)
|
||||
(canceled)
|
||||
(BETTING_MARKET_GROUP_STATUS_COUNT) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_group_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_group_create_operation,
|
||||
(fee)(description)(event_id)(rules_id)(asset_id)
|
||||
(never_in_play)(delay_before_settling)
|
||||
(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_group_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_group_update_operation,
|
||||
(fee)(betting_market_group_id)(new_description)(new_rules_id)(status)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_create_operation,
|
||||
(fee)(group_id)(description)(payout_condition)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_update_operation,
|
||||
(fee)(betting_market_id)(new_group_id)(new_description)(new_payout_condition)(extensions) )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::betting_market_resolution_type, (win)(not_win)(cancel)(BETTING_MARKET_RESOLUTION_COUNT) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_group_resolve_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_group_resolve_operation,
|
||||
(fee)(betting_market_group_id)(resolutions)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_group_resolved_operation::fee_parameters_type, )
|
||||
FC_REFLECT( graphene::chain::betting_market_group_resolved_operation,
|
||||
(bettor_id)(betting_market_group_id)(resolutions)(winnings)(fees_paid)(fee) )
|
||||
|
||||
FC_REFLECT( graphene::chain::betting_market_group_cancel_unmatched_bets_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::betting_market_group_cancel_unmatched_bets_operation,
|
||||
(fee)(betting_market_group_id)(extensions) )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::bet_type, (back)(lay) )
|
||||
FC_REFLECT( graphene::chain::bet_place_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::bet_place_operation,
|
||||
(fee)(bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(back_or_lay)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bet_matched_operation::fee_parameters_type, )
|
||||
FC_REFLECT( graphene::chain::bet_matched_operation, (bettor_id)(bet_id)(amount_bet)(backer_multiplier)(guaranteed_winnings_returned) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bet_cancel_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::bet_cancel_operation, (fee) (bettor_id) (bet_to_cancel) (extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bet_canceled_operation::fee_parameters_type, )
|
||||
FC_REFLECT( graphene::chain::bet_canceled_operation, (bettor_id)(bet_id)(stake_returned) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bet_adjusted_operation::fee_parameters_type, )
|
||||
FC_REFLECT( graphene::chain::bet_adjusted_operation, (bettor_id)(bet_id)(stake_returned) )
|
||||
|
|
@ -69,7 +69,12 @@ 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
|
||||
uint16_t betting_rake_fee_percentage = GRAPHENE_DEFAULT_RAKE_FEE_PERCENTAGE; ///< part of prize paid into the dividend account for the core token holders
|
||||
bet_multiplier_type min_bet_multiplier = GRAPHENE_DEFAULT_MIN_BET_MULTIPLIER;
|
||||
bet_multiplier_type max_bet_multiplier = GRAPHENE_DEFAULT_MAX_BET_MULTIPLIER;
|
||||
flat_map<bet_multiplier_type, bet_multiplier_type> permitted_betting_odds_increments = GRAPHENE_DEFAULT_PERMITTED_BETTING_ODDS_INCREMENTS;
|
||||
uint16_t live_betting_delay_time = GRAPHENE_DEFAULT_LIVE_BETTING_DELAY_TIME; ///< delayed bets
|
||||
//uint8_t witness_schedule_algorithm = GRAPHENE_WITNESS_SHUFFLED_ALGORITHM; ///< 0 shuffled, 1 scheduled
|
||||
uint8_t witness_schedule_algorithm = GRAPHENE_WITNESS_SCHEDULED_ALGORITHM; ///< 0 shuffled, 1 scheduled
|
||||
/* rps tournament parameters constraints */
|
||||
uint32_t min_round_delay = TOURNAMENT_MIN_ROUND_DELAY; ///< miniaml delay between games
|
||||
|
|
@ -122,7 +127,12 @@ FC_REFLECT( graphene::chain::chain_parameters,
|
|||
(accounts_per_fee_scale)
|
||||
(account_fee_scale_bitshifts)
|
||||
(max_authority_depth)
|
||||
(min_bet_multiplier)
|
||||
(max_bet_multiplier)
|
||||
(betting_rake_fee_percentage)
|
||||
(permitted_betting_odds_increments)
|
||||
(witness_schedule_algorithm)
|
||||
(live_betting_delay_time)
|
||||
(min_round_delay)
|
||||
(max_round_delay)
|
||||
(min_time_per_commit_move)
|
||||
|
|
|
|||
147
libraries/chain/include/graphene/chain/protocol/event.hpp
Normal file
147
libraries/chain/include/graphene/chain/protocol/event.hpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct event_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
/**
|
||||
* The name of the event
|
||||
*/
|
||||
internationalized_string_type name;
|
||||
|
||||
internationalized_string_type season;
|
||||
|
||||
optional<time_point_sec> start_time;
|
||||
|
||||
/**
|
||||
* This can be a event_group_id_type, or a
|
||||
* relative object id that resolves to a event_group_id_type
|
||||
*/
|
||||
object_id_type event_group_id;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* The status of an event. This is used to display in the UI, and setting
|
||||
* the event's status to certain values will propagate down to the
|
||||
* betting market groups in the event:
|
||||
* - when set to `in_progress`, all betting market groups are set to `in_play`
|
||||
* - when set to `completed`, all betting market groups are set to `closed`
|
||||
* - when set to `frozen`, all betting market groups are set to `frozen`
|
||||
* - when set to `canceled`, all betting market groups are set to `canceled`
|
||||
*/
|
||||
enum class event_status
|
||||
{
|
||||
upcoming, /// Event has not started yet, betting is allowed
|
||||
in_progress, /// Event is in progress, if "in-play" betting is enabled, bets will be delayed
|
||||
frozen, /// Betting is temporarily disabled
|
||||
finished, /// Event has finished, no more betting allowed
|
||||
canceled, /// Event has been canceled, all betting markets have been canceled
|
||||
settled, /// All betting markets have been paid out
|
||||
STATUS_COUNT
|
||||
};
|
||||
|
||||
struct event_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
event_id_type event_id;
|
||||
|
||||
optional<object_id_type> new_event_group_id;
|
||||
|
||||
optional<internationalized_string_type> new_name;
|
||||
|
||||
optional<internationalized_string_type> new_season;
|
||||
|
||||
optional<time_point_sec> new_start_time;
|
||||
|
||||
optional<event_status> new_status;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* The current (or final) score of an event.
|
||||
* This is only used for display to the user, witnesses must resolve each
|
||||
* betting market explicitly.
|
||||
* These are free-form strings that we assume will make sense to the user.
|
||||
* For a game like football, this may be a score like "3". For races,
|
||||
* it could be a time like "1:53.4".
|
||||
*/
|
||||
struct event_update_status_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
/// the id of the event to update
|
||||
event_id_type event_id;
|
||||
|
||||
/**
|
||||
* the new status of the event (if the status hasn't changed, the creator
|
||||
* of this operation must still set `status` to the event's current status)
|
||||
*/
|
||||
event_status status;
|
||||
|
||||
/*
|
||||
* scores for each competitor stored in same order as competitors in event_object
|
||||
*/
|
||||
vector<string> scores;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::event_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::event_create_operation,
|
||||
(fee)(name)(season)(start_time)(event_group_id)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::event_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::event_update_operation,
|
||||
(fee)(event_id)(new_event_group_id)(new_name)(new_season)(new_start_time)(new_status)(extensions) )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::event_status, (upcoming)(in_progress)(frozen)(finished)(canceled)(settled)(STATUS_COUNT) )
|
||||
FC_REFLECT( graphene::chain::event_update_status_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::event_update_status_operation,
|
||||
(fee)(event_id)(status)(scores)(extensions) )
|
||||
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct event_group_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
/**
|
||||
* The name of the event_group
|
||||
*/
|
||||
internationalized_string_type name;
|
||||
|
||||
/**
|
||||
* This can be a sport_id_type, or a
|
||||
* relative object id that resolves to a sport_id_type
|
||||
*/
|
||||
object_id_type sport_id;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct event_group_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
event_group_id_type event_group_id;
|
||||
|
||||
/**
|
||||
* This can be a sport_id_type, or a
|
||||
* relative object id that resolves to a sport_id_type
|
||||
*/
|
||||
optional<object_id_type> new_sport_id;
|
||||
|
||||
optional<internationalized_string_type> new_name;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::event_group_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::event_group_create_operation,
|
||||
(fee)(name)(sport_id)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::event_group_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::event_group_update_operation,
|
||||
(fee)(new_sport_id)(new_name)(event_group_id)(extensions) )
|
||||
|
|
@ -38,6 +38,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 {
|
||||
|
|
@ -92,14 +96,35 @@ namespace graphene { namespace chain {
|
|||
transfer_from_blind_operation,
|
||||
asset_settle_cancel_operation, // VIRTUAL
|
||||
asset_claim_fees_operation,
|
||||
fba_distribute_operation, // VIRTUAL
|
||||
fba_distribute_operation, // VIRTUAL
|
||||
tournament_create_operation,
|
||||
tournament_join_operation,
|
||||
game_move_operation,
|
||||
asset_update_dividend_operation,
|
||||
asset_dividend_distribution_operation, // VIRTUAL
|
||||
tournament_payout_operation, // VIRTUAL
|
||||
tournament_leave_operation
|
||||
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
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
70
libraries/chain/include/graphene/chain/protocol/sport.hpp
Normal file
70
libraries/chain/include/graphene/chain/protocol/sport.hpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct sport_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
/**
|
||||
* The name of the sport
|
||||
*/
|
||||
internationalized_string_type name;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
struct sport_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
|
||||
asset fee;
|
||||
|
||||
sport_id_type sport_id;
|
||||
|
||||
optional<internationalized_string_type> new_name;
|
||||
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_WITNESS_ACCOUNT; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::sport_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::sport_create_operation,
|
||||
(fee)(name)(extensions) )
|
||||
|
||||
FC_REFLECT( graphene::chain::sport_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::sport_update_operation,
|
||||
(fee)(sport_id)(new_name)(extensions) )
|
||||
|
|
@ -134,6 +134,13 @@ namespace graphene { namespace chain {
|
|||
vesting_balance_object_type,
|
||||
worker_object_type,
|
||||
balance_object_type,
|
||||
sport_object_type,
|
||||
event_group_object_type,
|
||||
event_object_type,
|
||||
betting_market_rules_object_type,
|
||||
betting_market_group_object_type,
|
||||
betting_market_object_type,
|
||||
bet_object_type,
|
||||
tournament_object_type,
|
||||
tournament_details_object_type,
|
||||
match_object_type,
|
||||
|
|
@ -160,6 +167,8 @@ namespace graphene { namespace chain {
|
|||
impl_special_authority_object_type,
|
||||
impl_buyback_object_type,
|
||||
impl_fba_accumulator_object_type,
|
||||
impl_betting_market_position_object_type,
|
||||
impl_global_betting_statistics_object_type,
|
||||
impl_asset_dividend_data_type,
|
||||
impl_pending_dividend_payout_balance_for_holder_object_type,
|
||||
impl_distributed_dividend_balance_data_type
|
||||
|
|
@ -182,6 +191,13 @@ namespace graphene { namespace chain {
|
|||
class worker_object;
|
||||
class balance_object;
|
||||
class blinded_balance_object;
|
||||
class sport_object;
|
||||
class event_group_object;
|
||||
class event_object;
|
||||
class betting_market_rules_object;
|
||||
class betting_market_group_object;
|
||||
class betting_market_object;
|
||||
class bet_object;
|
||||
class tournament_object;
|
||||
class tournament_details_object;
|
||||
class match_object;
|
||||
|
|
@ -201,6 +217,13 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type;
|
||||
typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type;
|
||||
typedef object_id< protocol_ids, balance_object_type, balance_object> balance_id_type;
|
||||
typedef object_id< protocol_ids, sport_object_type, sport_object> sport_id_type;
|
||||
typedef object_id< protocol_ids, event_group_object_type, event_group_object> event_group_id_type;
|
||||
typedef object_id< protocol_ids, event_object_type, event_object> event_id_type;
|
||||
typedef object_id< protocol_ids, betting_market_rules_object_type, betting_market_rules_object> betting_market_rules_id_type;
|
||||
typedef object_id< protocol_ids, betting_market_group_object_type, betting_market_group_object> betting_market_group_id_type;
|
||||
typedef object_id< protocol_ids, betting_market_object_type, betting_market_object> betting_market_id_type;
|
||||
typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type;
|
||||
typedef object_id< protocol_ids, tournament_object_type, tournament_object> tournament_id_type;
|
||||
typedef object_id< protocol_ids, tournament_details_object_type, tournament_details_object> tournament_details_id_type;
|
||||
typedef object_id< protocol_ids, match_object_type, match_object> match_id_type;
|
||||
|
|
@ -222,6 +245,8 @@ namespace graphene { namespace chain {
|
|||
class special_authority_object;
|
||||
class buyback_object;
|
||||
class fba_accumulator_object;
|
||||
class betting_market_position_object;
|
||||
class global_betting_statistics_object;
|
||||
class tournament_details_object;
|
||||
class asset_dividend_data_object;
|
||||
class pending_dividend_payout_balance_for_holder_object;
|
||||
|
|
@ -247,6 +272,8 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< implementation_ids, impl_special_authority_object_type, special_authority_object > special_authority_id_type;
|
||||
typedef object_id< implementation_ids, impl_buyback_object_type, buyback_object > buyback_id_type;
|
||||
typedef object_id< implementation_ids, impl_fba_accumulator_object_type, fba_accumulator_object > fba_accumulator_id_type;
|
||||
typedef object_id< implementation_ids, impl_betting_market_position_object_type, betting_market_position_object > betting_market_position_id_type;
|
||||
typedef object_id< implementation_ids, impl_global_betting_statistics_object_type, global_betting_statistics_object > global_betting_statistics_id_type;
|
||||
|
||||
typedef fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
|
||||
typedef fc::ripemd160 block_id_type;
|
||||
|
|
@ -325,6 +352,10 @@ namespace graphene { namespace chain {
|
|||
friend bool operator == ( const extended_private_key_type& p1, const extended_private_key_type& p2);
|
||||
friend bool operator != ( const extended_private_key_type& p1, const extended_private_key_type& p2);
|
||||
};
|
||||
|
||||
typedef flat_map<std::string, std::string> internationalized_string_type;
|
||||
|
||||
typedef uint32_t bet_multiplier_type;
|
||||
} } // graphene::chain
|
||||
|
||||
namespace fc
|
||||
|
|
@ -361,6 +392,13 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
|
|||
(vesting_balance_object_type)
|
||||
(worker_object_type)
|
||||
(balance_object_type)
|
||||
(sport_object_type)
|
||||
(event_group_object_type)
|
||||
(event_object_type)
|
||||
(betting_market_rules_object_type)
|
||||
(betting_market_group_object_type)
|
||||
(betting_market_object_type)
|
||||
(bet_object_type)
|
||||
(tournament_object_type)
|
||||
(tournament_details_object_type)
|
||||
(match_object_type)
|
||||
|
|
@ -385,6 +423,8 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
|||
(impl_special_authority_object_type)
|
||||
(impl_buyback_object_type)
|
||||
(impl_fba_accumulator_object_type)
|
||||
(impl_betting_market_position_object_type)
|
||||
(impl_global_betting_statistics_object_type)
|
||||
(impl_asset_dividend_data_type)
|
||||
(impl_pending_dividend_payout_balance_for_holder_object_type)
|
||||
(impl_distributed_dividend_balance_data_type)
|
||||
|
|
@ -406,6 +446,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 )
|
||||
|
|
@ -420,6 +467,8 @@ FC_REFLECT_TYPENAME( graphene::chain::budget_record_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::special_authority_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::buyback_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::fba_accumulator_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::betting_market_position_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::global_betting_statistics_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type )
|
||||
|
||||
FC_REFLECT( graphene::chain::void_t, )
|
||||
|
|
|
|||
|
|
@ -146,7 +146,6 @@ void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo );
|
|||
|
||||
} // fc
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::vote_id_type::vote_type )
|
||||
FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(VOTE_TYPE_COUNT) )
|
||||
|
|
|
|||
50
libraries/chain/include/graphene/chain/sport_evaluator.hpp
Normal file
50
libraries/chain/include/graphene/chain/sport_evaluator.hpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class sport_create_evaluator : public evaluator<sport_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sport_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const sport_create_operation& o );
|
||||
object_id_type do_apply( const sport_create_operation& o );
|
||||
};
|
||||
|
||||
class sport_update_evaluator : public evaluator<sport_update_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sport_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const sport_update_operation& o );
|
||||
void_result do_apply( const sport_update_operation& o );
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
51
libraries/chain/include/graphene/chain/sport_object.hpp
Normal file
51
libraries/chain/include/graphene/chain/sport_object.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class database;
|
||||
|
||||
class sport_object : public graphene::db::abstract_object< sport_object >
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = sport_object_type;
|
||||
|
||||
internationalized_string_type name;
|
||||
};
|
||||
|
||||
typedef multi_index_container<
|
||||
sport_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > > > > sport_object_multi_index_type;
|
||||
|
||||
typedef generic_index<sport_object, sport_object_multi_index_type> sport_object_index;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::sport_object, (graphene::db::object), (name) )
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -85,6 +85,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;
|
||||
|
|
|
|||
81
libraries/chain/protocol/betting_market.cpp
Normal file
81
libraries/chain/protocol/betting_market.cpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/protocol/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
|
||||
|
||||
|
|
@ -21,10 +21,15 @@
|
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/p2p/peer_connection.hpp>
|
||||
#include <graphene/chain/protocol/competitor.hpp>
|
||||
|
||||
namespace graphene { namespace p2p {
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
} } //graphene::p2p
|
||||
void competitor_create_operation::validate() const
|
||||
{
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
}
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
44
libraries/chain/protocol/event.cpp
Normal file
44
libraries/chain/protocol/event.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/protocol/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
|
||||
|
||||
39
libraries/chain/protocol/event_group.cpp
Normal file
39
libraries/chain/protocol/event_group.cpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#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 );
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
|
|
@ -189,6 +189,11 @@ 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( min_bet_multiplier >= GRAPHENE_BETTING_MIN_MULTIPLIER &&
|
||||
min_bet_multiplier <= GRAPHENE_BETTING_MAX_MULTIPLIER );
|
||||
FC_ASSERT( max_bet_multiplier >= GRAPHENE_BETTING_MIN_MULTIPLIER &&
|
||||
max_bet_multiplier <= GRAPHENE_BETTING_MAX_MULTIPLIER );
|
||||
FC_ASSERT( min_bet_multiplier < max_bet_multiplier );
|
||||
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,
|
||||
|
|
|
|||
40
libraries/chain/protocol/sport.cpp
Normal file
40
libraries/chain/protocol/sport.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#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 );
|
||||
}
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ struct sign_state
|
|||
if( approved_by.find(a.first) == approved_by.end() )
|
||||
{
|
||||
if( depth == max_recursion )
|
||||
return false;
|
||||
continue;
|
||||
if( check_authority( get_active( a.first ), depth+1 ) )
|
||||
{
|
||||
approved_by.insert( a.first );
|
||||
|
|
|
|||
74
libraries/chain/sport_evaluator.cpp
Normal file
74
libraries/chain/sport_evaluator.cpp
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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/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) ) }
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -304,53 +304,51 @@ 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;
|
||||
}
|
||||
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;
|
||||
|
||||
// check whether the core asset pays dividends. If so, we transfer the rake fee
|
||||
// to the core asset's dividend account
|
||||
const asset_object& core_asset_obj = asset_id_type()(event.db);
|
||||
optional<asset_dividend_data_id_type> dividend_id = core_asset_obj.dividend_data_id;
|
||||
|
||||
share_type rake_amount = 0;
|
||||
|
||||
const asset_object & asset_obj = tournament_obj.options.buy_in.asset_id(event.db);
|
||||
optional<asset_dividend_data_id_type> dividend_id = asset_obj.dividend_data_id;
|
||||
|
||||
if (dividend_id.valid())
|
||||
{
|
||||
rake_amount = (fc::uint128_t(tournament_obj.prize_pool.value) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100).to_uint64();
|
||||
}
|
||||
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;
|
||||
|
||||
if (won_prize.amount.value)
|
||||
{
|
||||
// Adjusting balance of winner
|
||||
event.db.adjust_balance(winner, won_prize);
|
||||
// Adjusting balance of winner
|
||||
event.db.adjust_balance(winner, won_prize);
|
||||
|
||||
// Generating a virtual operation that shows the payment
|
||||
op.tournament_id = tournament_obj.id;
|
||||
op.payout_amount = won_prize;
|
||||
op.payout_account_id = winner;
|
||||
op.type = payout_type::prize_award;
|
||||
event.db.push_applied_operation(op);
|
||||
// Generating a virtual operation that shows the payment
|
||||
op.tournament_id = tournament_obj.id;
|
||||
op.payout_amount = won_prize;
|
||||
op.payout_account_id = winner;
|
||||
op.type = payout_type::prize_award;
|
||||
event.db.push_applied_operation(op);
|
||||
}
|
||||
|
||||
if (dividend_id.valid() && rake_amount.value)
|
||||
if (dividend_id && rake_amount.value)
|
||||
{
|
||||
// Adjusting balance of dividend_distribution_account
|
||||
const asset_dividend_data_id_type& asset_dividend_data_id_= *dividend_id;
|
||||
const asset_dividend_data_object& dividend_obj = asset_dividend_data_id_(event.db);
|
||||
const account_id_type& rake_account_id = dividend_obj.dividend_distribution_account;
|
||||
asset rake(rake_amount, tournament_obj.options.buy_in.asset_id);
|
||||
event.db.adjust_balance(rake_account_id, rake);
|
||||
// Adjusting balance of dividend_distribution_account
|
||||
const asset_dividend_data_id_type& asset_dividend_data_id_= *dividend_id;
|
||||
const asset_dividend_data_object& dividend_obj = asset_dividend_data_id_(event.db);
|
||||
const account_id_type& rake_account_id = dividend_obj.dividend_distribution_account;
|
||||
asset rake(rake_amount, tournament_obj.options.buy_in.asset_id);
|
||||
event.db.adjust_balance(rake_account_id, rake);
|
||||
|
||||
// Generating a virtual operation that shows the payment
|
||||
op.payout_amount = rake;
|
||||
op.payout_account_id = rake_account_id;
|
||||
op.type = payout_type::rake_fee;
|
||||
event.db.push_applied_operation(op);
|
||||
// Generating a virtual operation that shows the payment
|
||||
op.payout_amount = rake;
|
||||
op.payout_account_id = rake_account_id;
|
||||
op.type = payout_type::rake_fee;
|
||||
event.db.push_applied_operation(op);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ asset linear_vesting_policy::get_allowed_withdraw( const vesting_policy_context&
|
|||
}
|
||||
}
|
||||
|
||||
return asset( allowed_withdraw, ctx.amount.asset_id );
|
||||
return asset( allowed_withdraw, ctx.balance.asset_id );
|
||||
}
|
||||
|
||||
void linear_vesting_policy::on_deposit(const vesting_policy_context& ctx)
|
||||
|
|
|
|||
|
|
@ -10,3 +10,4 @@ install( TARGETS
|
|||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
install( FILES ${HEADERS} DESTINATION "include/graphene/db" )
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ namespace graphene { namespace chain {
|
|||
|
||||
virtual const object* find( object_id_type id )const override
|
||||
{
|
||||
static_assert(std::is_same<typename MultiIndexType::key_type, object_id_type>::value,
|
||||
"First index of MultiIndexType MUST be object_id_type!");
|
||||
auto itr = _indices.find( id );
|
||||
if( itr == _indices.end() ) return nullptr;
|
||||
return &*itr;
|
||||
|
|
|
|||
|
|
@ -165,9 +165,10 @@ namespace graphene { namespace db {
|
|||
void on_modify( const object& obj );
|
||||
|
||||
template<typename T>
|
||||
void add_secondary_index()
|
||||
T* add_secondary_index()
|
||||
{
|
||||
_sindex.emplace_back( new T() );
|
||||
return static_cast<T*>(_sindex.back().get());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
|
|
|||
|
|
@ -25,3 +25,4 @@ install( TARGETS
|
|||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
install( FILES ${headers} DESTINATION "include/graphene/deterministic_openssl_rand" )
|
||||
|
|
|
|||
|
|
@ -32,3 +32,4 @@ install( TARGETS
|
|||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
install( FILES ${HEADERS} DESTINATION "include/graphene/net" )
|
||||
|
|
|
|||
|
|
@ -49,6 +49,9 @@
|
|||
|
||||
#define GRAPHENE_NET_PEER_DISCONNECT_TIMEOUT 20
|
||||
|
||||
/* uncomment next line to use testnet seed ip and port */
|
||||
//#define GRAPHENE_TEST_NETWORK 1
|
||||
|
||||
#define GRAPHENE_NET_TEST_SEED_IP "104.236.44.210" // autogenerated
|
||||
#define GRAPHENE_NET_TEST_P2P_PORT 1700
|
||||
#define GRAPHENE_NET_DEFAULT_P2P_PORT 2776
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ namespace graphene { namespace net {
|
|||
*/
|
||||
struct message_header
|
||||
{
|
||||
uint32_t size; // number of bytes in message, capped at MAX_MESSAGE_SIZE
|
||||
uint32_t msg_type; // every channel gets a 16 bit message type specifier
|
||||
uint32_t size = 0; // number of bytes in message, capped at MAX_MESSAGE_SIZE
|
||||
uint32_t msg_type = 0; // every channel gets a 16 bit message type specifier
|
||||
};
|
||||
|
||||
typedef fc::uint160_t message_hash_type;
|
||||
|
|
|
|||
|
|
@ -158,9 +158,6 @@ namespace graphene { namespace net {
|
|||
*/
|
||||
virtual fc::time_point_sec get_block_time(const item_hash_t& block_id) = 0;
|
||||
|
||||
/** returns graphene::blockchain::now() */
|
||||
virtual fc::time_point_sec get_blockchain_now() = 0;
|
||||
|
||||
virtual item_hash_t get_head_block_id() const = 0;
|
||||
|
||||
virtual uint32_t estimate_last_known_fork_from_git_revision_timestamp(uint32_t unix_timestamp) const = 0;
|
||||
|
|
|
|||
|
|
@ -166,25 +166,25 @@ namespace graphene { namespace net
|
|||
};
|
||||
|
||||
|
||||
size_t _total_queued_messages_size;
|
||||
size_t _total_queued_messages_size = 0;
|
||||
std::queue<std::unique_ptr<queued_message>, std::list<std::unique_ptr<queued_message> > > _queued_messages;
|
||||
fc::future<void> _send_queued_messages_done;
|
||||
public:
|
||||
fc::time_point connection_initiation_time;
|
||||
fc::time_point connection_closed_time;
|
||||
fc::time_point connection_terminated_time;
|
||||
peer_connection_direction direction;
|
||||
peer_connection_direction direction = peer_connection_direction::unknown;
|
||||
//connection_state state;
|
||||
firewalled_state is_firewalled;
|
||||
firewalled_state is_firewalled = firewalled_state::unknown;
|
||||
fc::microseconds clock_offset;
|
||||
fc::microseconds round_trip_delay;
|
||||
|
||||
our_connection_state our_state;
|
||||
bool they_have_requested_close;
|
||||
their_connection_state their_state;
|
||||
bool we_have_requested_close;
|
||||
our_connection_state our_state = our_connection_state::disconnected;
|
||||
bool they_have_requested_close = false;
|
||||
their_connection_state their_state = their_connection_state::disconnected;
|
||||
bool we_have_requested_close = false;
|
||||
|
||||
connection_negotiation_status negotiation_status;
|
||||
connection_negotiation_status negotiation_status = connection_negotiation_status::disconnected;
|
||||
fc::oexception connection_closed_error;
|
||||
|
||||
fc::time_point get_connection_time()const { return _message_connection.get_connection_time(); }
|
||||
|
|
@ -199,7 +199,7 @@ namespace graphene { namespace net
|
|||
* from the user_data field of the hello, or if none is present it will be filled with a
|
||||
* copy of node_public_key */
|
||||
node_id_t node_id;
|
||||
uint32_t core_protocol_version;
|
||||
uint32_t core_protocol_version = 0;
|
||||
std::string user_agent;
|
||||
fc::optional<std::string> graphene_git_revision_sha;
|
||||
fc::optional<fc::time_point_sec> graphene_git_revision_unix_timestamp;
|
||||
|
|
@ -212,8 +212,8 @@ namespace graphene { namespace net
|
|||
// its hello message. For outbound, they record what we sent the peer
|
||||
// in our hello message
|
||||
fc::ip::address inbound_address;
|
||||
uint16_t inbound_port;
|
||||
uint16_t outbound_port;
|
||||
uint16_t inbound_port = 0;
|
||||
uint16_t outbound_port = 0;
|
||||
/// @}
|
||||
|
||||
typedef std::unordered_map<item_id, fc::time_point> item_to_time_map_type;
|
||||
|
|
@ -222,14 +222,14 @@ namespace graphene { namespace net
|
|||
/// @{
|
||||
boost::container::deque<item_hash_t> ids_of_items_to_get; /// id of items in the blockchain that this peer has told us about
|
||||
std::set<item_hash_t> ids_of_items_being_processed; /// list of all items this peer has offered use that we've already handed to the client but the client hasn't finished processing
|
||||
uint32_t number_of_unfetched_item_ids; /// number of items in the blockchain that follow ids_of_items_to_get but the peer hasn't yet told us their ids
|
||||
bool peer_needs_sync_items_from_us;
|
||||
bool we_need_sync_items_from_peer;
|
||||
uint32_t number_of_unfetched_item_ids = 0; /// number of items in the blockchain that follow ids_of_items_to_get but the peer hasn't yet told us their ids
|
||||
bool peer_needs_sync_items_from_us = false;
|
||||
bool we_need_sync_items_from_peer = false;
|
||||
fc::optional<boost::tuple<std::vector<item_hash_t>, fc::time_point> > item_ids_requested_from_peer; /// we check this to detect a timed-out request and in busy()
|
||||
item_to_time_map_type sync_items_requested_from_peer; /// ids of blocks we've requested from this peer during sync. fetch from another peer if this peer disconnects
|
||||
item_hash_t last_block_delegate_has_seen; /// the hash of the last block this peer has told us about that the peer knows
|
||||
fc::time_point_sec last_block_time_delegate_has_seen;
|
||||
bool inhibit_fetching_sync_blocks;
|
||||
bool inhibit_fetching_sync_blocks = false;
|
||||
/// @}
|
||||
|
||||
/// non-synchronization state data
|
||||
|
|
@ -259,16 +259,17 @@ namespace graphene { namespace net
|
|||
// blockchain catch up
|
||||
fc::time_point transaction_fetching_inhibited_until;
|
||||
|
||||
uint32_t last_known_fork_block_number;
|
||||
uint32_t last_known_fork_block_number = 0;
|
||||
|
||||
fc::future<void> accept_or_connect_task_done;
|
||||
|
||||
firewall_check_state_data *firewall_check_state;
|
||||
firewall_check_state_data *firewall_check_state = nullptr;
|
||||
#ifndef NDEBUG
|
||||
private:
|
||||
fc::thread* _thread;
|
||||
unsigned _send_message_queue_tasks_running; // temporary debugging
|
||||
fc::thread* _thread = nullptr;
|
||||
unsigned _send_message_queue_tasks_running = 0; // temporary debugging
|
||||
#endif
|
||||
bool _currently_handling_message = false; // true while we're in the middle of handling a message from the remote system
|
||||
private:
|
||||
peer_connection(peer_connection_delegate* delegate);
|
||||
void destroy();
|
||||
|
|
@ -299,8 +300,9 @@ namespace graphene { namespace net
|
|||
fc::ip::endpoint get_local_endpoint();
|
||||
void set_remote_endpoint(fc::optional<fc::ip::endpoint> new_remote_endpoint);
|
||||
|
||||
bool busy();
|
||||
bool idle();
|
||||
bool busy() const;
|
||||
bool idle() const;
|
||||
bool is_currently_handling_message() const;
|
||||
|
||||
bool is_transaction_fetching_inhibited() const;
|
||||
fc::sha512 get_shared_secret() const;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
#include <graphene/net/stcp_socket.hpp>
|
||||
#include <graphene/net/config.hpp>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#ifdef DEFAULT_LOGGER
|
||||
# undef DEFAULT_LOGGER
|
||||
#endif
|
||||
|
|
@ -61,7 +63,6 @@ namespace graphene { namespace net {
|
|||
fc::time_point _last_message_sent_time;
|
||||
|
||||
bool _send_message_in_progress;
|
||||
|
||||
#ifndef NDEBUG
|
||||
fc::thread* _thread;
|
||||
#endif
|
||||
|
|
@ -137,7 +138,6 @@ namespace graphene { namespace net {
|
|||
_sock.bind(local_endpoint);
|
||||
}
|
||||
|
||||
|
||||
void message_oriented_connection_impl::read_loop()
|
||||
{
|
||||
VERIFY_CORRECT_THREAD();
|
||||
|
|
@ -261,8 +261,13 @@ namespace graphene { namespace net {
|
|||
//pad the message we send to a multiple of 16 bytes
|
||||
size_t size_with_padding = 16 * ((size_of_message_and_header + 15) / 16);
|
||||
std::unique_ptr<char[]> padded_message(new char[size_with_padding]);
|
||||
|
||||
memcpy(padded_message.get(), (char*)&message_to_send, sizeof(message_header));
|
||||
memcpy(padded_message.get() + sizeof(message_header), message_to_send.data.data(), message_to_send.size );
|
||||
char* paddingSpace = padded_message.get() + sizeof(message_header) + message_to_send.size;
|
||||
size_t toClean = size_with_padding - size_of_message_and_header;
|
||||
memset(paddingSpace, 0, toClean);
|
||||
|
||||
_sock.write(padded_message.get(), size_with_padding);
|
||||
_sock.flush();
|
||||
_bytes_sent += size_with_padding;
|
||||
|
|
|
|||
|
|
@ -396,7 +396,6 @@ namespace graphene { namespace net { namespace detail {
|
|||
void connection_count_changed( uint32_t c ) override;
|
||||
uint32_t get_block_number(const item_hash_t& block_id) override;
|
||||
fc::time_point_sec get_block_time(const item_hash_t& block_id) override;
|
||||
fc::time_point_sec get_blockchain_now() override;
|
||||
item_hash_t get_head_block_id() const override;
|
||||
uint32_t estimate_last_known_fork_from_git_revision_timestamp(uint32_t unix_timestamp) const override;
|
||||
void error_encountered(const std::string& message, const fc::oexception& error) override;
|
||||
|
|
@ -975,10 +974,7 @@ namespace graphene { namespace net { namespace detail {
|
|||
{
|
||||
throw;
|
||||
}
|
||||
catch (const fc::exception& e)
|
||||
{
|
||||
elog("${e}", ("e", e));
|
||||
}
|
||||
FC_CAPTURE_AND_LOG( (0) )
|
||||
}// while(!canceled)
|
||||
}
|
||||
|
||||
|
|
@ -1425,6 +1421,19 @@ namespace graphene { namespace net { namespace detail {
|
|||
wlog( "Sending a keepalive message to peer ${peer} who hasn't sent us any messages in the last ${timeout} seconds",
|
||||
( "peer", active_peer->get_remote_endpoint() )("timeout", active_send_keepalive_timeout ) );
|
||||
peers_to_send_keep_alive.push_back(active_peer);
|
||||
}
|
||||
else if (active_peer->we_need_sync_items_from_peer &&
|
||||
!active_peer->is_currently_handling_message() &&
|
||||
!active_peer->item_ids_requested_from_peer &&
|
||||
active_peer->ids_of_items_to_get.empty())
|
||||
{
|
||||
// This is a state we should never get into in the first place, but if we do, we should disconnect the peer
|
||||
// to re-establish the connection.
|
||||
fc_wlog(fc::logger::get("sync"), "Disconnecting peer ${peer} because we think we need blocks from them but sync has stalled.",
|
||||
("peer", active_peer->get_remote_endpoint()));
|
||||
wlog("Disconnecting peer ${peer} because we think we need blocks from them but sync has stalled.",
|
||||
("peer", active_peer->get_remote_endpoint()));
|
||||
peers_to_disconnect_forcibly.push_back(active_peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2456,24 +2465,24 @@ namespace graphene { namespace net { namespace detail {
|
|||
uint32_t expected_num = first_block_number_in_reponse + i;
|
||||
if (actual_num != expected_num)
|
||||
{
|
||||
wlog("Invalid response from peer ${peer_endpoint}. The list of blocks they provided is not sequential, "
|
||||
"the ${position}th block in their reply was block number ${actual_num}, "
|
||||
"but it should have been number ${expected_num}",
|
||||
("peer_endpoint", originating_peer->get_remote_endpoint())
|
||||
("position", i)
|
||||
("actual_num", actual_num)
|
||||
("expected_num", expected_num));
|
||||
fc::exception error_for_peer(FC_LOG_MESSAGE(error,
|
||||
"You gave an invalid response to my request for sync blocks. The list of blocks you provided is not sequential, "
|
||||
"the ${position}th block in their reply was block number ${actual_num}, "
|
||||
"but it should have been number ${expected_num}",
|
||||
("position", i)
|
||||
("actual_num", actual_num)
|
||||
("expected_num", expected_num)));
|
||||
disconnect_from_peer(originating_peer,
|
||||
"You gave an invalid response to my request for sync blocks",
|
||||
true, error_for_peer);
|
||||
return;
|
||||
wlog("Invalid response from peer ${peer_endpoint}. The list of blocks they provided is not sequential, "
|
||||
"the ${position}th block in their reply was block number ${actual_num}, "
|
||||
"but it should have been number ${expected_num}",
|
||||
("peer_endpoint", originating_peer->get_remote_endpoint())
|
||||
("position", i)
|
||||
("actual_num", actual_num)
|
||||
("expected_num", expected_num));
|
||||
fc::exception error_for_peer(FC_LOG_MESSAGE(error,
|
||||
"You gave an invalid response to my request for sync blocks. The list of blocks you provided is not sequential, "
|
||||
"the ${position}th block in their reply was block number ${actual_num}, "
|
||||
"but it should have been number ${expected_num}",
|
||||
("position", i)
|
||||
("actual_num", actual_num)
|
||||
("expected_num", expected_num)));
|
||||
disconnect_from_peer(originating_peer,
|
||||
"You gave an invalid response to my request for sync blocks",
|
||||
true, error_for_peer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2520,180 +2529,205 @@ namespace graphene { namespace net { namespace detail {
|
|||
}
|
||||
originating_peer->item_ids_requested_from_peer.reset();
|
||||
|
||||
dlog( "sync: received a list of ${count} available items from ${peer_endpoint}",
|
||||
( "count", blockchain_item_ids_inventory_message_received.item_hashes_available.size() )
|
||||
( "peer_endpoint", originating_peer->get_remote_endpoint() ) );
|
||||
//for( const item_hash_t& item_hash : blockchain_item_ids_inventory_message_received.item_hashes_available )
|
||||
//{
|
||||
// dlog( "sync: ${hash}", ("hash", item_hash ) );
|
||||
//}
|
||||
|
||||
// if the peer doesn't have any items after the one we asked for
|
||||
if( blockchain_item_ids_inventory_message_received.total_remaining_item_count == 0 &&
|
||||
( blockchain_item_ids_inventory_message_received.item_hashes_available.empty() || // there are no items in the peer's blockchain. this should only happen if our blockchain was empty when we requested, might want to verify that.
|
||||
( blockchain_item_ids_inventory_message_received.item_hashes_available.size() == 1 &&
|
||||
_delegate->has_item( item_id(blockchain_item_ids_inventory_message_received.item_type,
|
||||
blockchain_item_ids_inventory_message_received.item_hashes_available.front() ) ) ) ) && // we've already seen the last item in the peer's blockchain
|
||||
originating_peer->ids_of_items_to_get.empty() &&
|
||||
originating_peer->number_of_unfetched_item_ids == 0 ) // <-- is the last check necessary?
|
||||
// if exceptions are throw after clearing the item_ids_requested_from_peer (above),
|
||||
// it could leave our sync in a stalled state. Wrap a try/catch around the rest
|
||||
// of the function so we can log if this ever happens.
|
||||
try
|
||||
{
|
||||
dlog( "sync: peer said we're up-to-date, entering normal operation with this peer" );
|
||||
originating_peer->we_need_sync_items_from_peer = false;
|
||||
dlog( "sync: received a list of ${count} available items from ${peer_endpoint}",
|
||||
( "count", blockchain_item_ids_inventory_message_received.item_hashes_available.size() )
|
||||
( "peer_endpoint", originating_peer->get_remote_endpoint() ) );
|
||||
//for( const item_hash_t& item_hash : blockchain_item_ids_inventory_message_received.item_hashes_available )
|
||||
//{
|
||||
// dlog( "sync: ${hash}", ("hash", item_hash ) );
|
||||
//}
|
||||
|
||||
uint32_t new_number_of_unfetched_items = calculate_unsynced_block_count_from_all_peers();
|
||||
_total_number_of_unfetched_items = new_number_of_unfetched_items;
|
||||
if( new_number_of_unfetched_items == 0 )
|
||||
_delegate->sync_status( blockchain_item_ids_inventory_message_received.item_type, 0 );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::deque<item_hash_t> item_hashes_received( blockchain_item_ids_inventory_message_received.item_hashes_available.begin(),
|
||||
blockchain_item_ids_inventory_message_received.item_hashes_available.end() );
|
||||
originating_peer->number_of_unfetched_item_ids = blockchain_item_ids_inventory_message_received.total_remaining_item_count;
|
||||
// flush any items this peer sent us that we've already received and processed from another peer
|
||||
if (!item_hashes_received.empty() &&
|
||||
originating_peer->ids_of_items_to_get.empty())
|
||||
{
|
||||
bool is_first_item_for_other_peer = false;
|
||||
for (const peer_connection_ptr& peer : _active_connections)
|
||||
if (peer != originating_peer->shared_from_this() &&
|
||||
!peer->ids_of_items_to_get.empty() &&
|
||||
peer->ids_of_items_to_get.front() == blockchain_item_ids_inventory_message_received.item_hashes_available.front())
|
||||
{
|
||||
dlog("The item ${newitem} is the first item for peer ${peer}",
|
||||
("newitem", blockchain_item_ids_inventory_message_received.item_hashes_available.front())
|
||||
("peer", peer->get_remote_endpoint()));
|
||||
is_first_item_for_other_peer = true;
|
||||
break;
|
||||
}
|
||||
dlog("is_first_item_for_other_peer: ${is_first}. item_hashes_received.size() = ${size}",
|
||||
("is_first", is_first_item_for_other_peer)("size", item_hashes_received.size()));
|
||||
if (!is_first_item_for_other_peer)
|
||||
// if the peer doesn't have any items after the one we asked for
|
||||
if( blockchain_item_ids_inventory_message_received.total_remaining_item_count == 0 &&
|
||||
( blockchain_item_ids_inventory_message_received.item_hashes_available.empty() || // there are no items in the peer's blockchain. this should only happen if our blockchain was empty when we requested, might want to verify that.
|
||||
( blockchain_item_ids_inventory_message_received.item_hashes_available.size() == 1 &&
|
||||
_delegate->has_item( item_id(blockchain_item_ids_inventory_message_received.item_type,
|
||||
blockchain_item_ids_inventory_message_received.item_hashes_available.front() ) ) ) ) && // we've already seen the last item in the peer's blockchain
|
||||
originating_peer->ids_of_items_to_get.empty() &&
|
||||
originating_peer->number_of_unfetched_item_ids == 0 ) // <-- is the last check necessary?
|
||||
{
|
||||
while (!item_hashes_received.empty() &&
|
||||
_delegate->has_item(item_id(blockchain_item_ids_inventory_message_received.item_type,
|
||||
item_hashes_received.front())))
|
||||
dlog( "sync: peer said we're up-to-date, entering normal operation with this peer" );
|
||||
originating_peer->we_need_sync_items_from_peer = false;
|
||||
|
||||
uint32_t new_number_of_unfetched_items = calculate_unsynced_block_count_from_all_peers();
|
||||
_total_number_of_unfetched_items = new_number_of_unfetched_items;
|
||||
if( new_number_of_unfetched_items == 0 )
|
||||
_delegate->sync_status( blockchain_item_ids_inventory_message_received.item_type, 0 );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::deque<item_hash_t> item_hashes_received( blockchain_item_ids_inventory_message_received.item_hashes_available.begin(),
|
||||
blockchain_item_ids_inventory_message_received.item_hashes_available.end() );
|
||||
originating_peer->number_of_unfetched_item_ids = blockchain_item_ids_inventory_message_received.total_remaining_item_count;
|
||||
// flush any items this peer sent us that we've already received and processed from another peer
|
||||
if (!item_hashes_received.empty() &&
|
||||
originating_peer->ids_of_items_to_get.empty())
|
||||
{
|
||||
bool is_first_item_for_other_peer = false;
|
||||
for (const peer_connection_ptr& peer : _active_connections)
|
||||
if (peer != originating_peer->shared_from_this() &&
|
||||
!peer->ids_of_items_to_get.empty() &&
|
||||
peer->ids_of_items_to_get.front() == blockchain_item_ids_inventory_message_received.item_hashes_available.front())
|
||||
{
|
||||
dlog("The item ${newitem} is the first item for peer ${peer}",
|
||||
("newitem", blockchain_item_ids_inventory_message_received.item_hashes_available.front())
|
||||
("peer", peer->get_remote_endpoint()));
|
||||
is_first_item_for_other_peer = true;
|
||||
break;
|
||||
}
|
||||
dlog("is_first_item_for_other_peer: ${is_first}. item_hashes_received.size() = ${size}",
|
||||
("is_first", is_first_item_for_other_peer)("size", item_hashes_received.size()));
|
||||
if (!is_first_item_for_other_peer)
|
||||
{
|
||||
assert(item_hashes_received.front() != item_hash_t());
|
||||
while (!item_hashes_received.empty() &&
|
||||
_delegate->has_item(item_id(blockchain_item_ids_inventory_message_received.item_type,
|
||||
item_hashes_received.front())))
|
||||
{
|
||||
assert(item_hashes_received.front() != item_hash_t());
|
||||
originating_peer->last_block_delegate_has_seen = item_hashes_received.front();
|
||||
originating_peer->last_block_time_delegate_has_seen = _delegate->get_block_time(item_hashes_received.front());
|
||||
dlog("popping item because delegate has already seen it. peer ${peer}'s last block the delegate has seen is now ${block_id} (actual block #${actual_block_num})",
|
||||
("peer", originating_peer->get_remote_endpoint())
|
||||
("block_id", originating_peer->last_block_delegate_has_seen)
|
||||
("actual_block_num", _delegate->get_block_number(item_hashes_received.front())));
|
||||
|
||||
item_hashes_received.pop_front();
|
||||
}
|
||||
dlog("after removing all items we have already seen, item_hashes_received.size() = ${size}", ("size", item_hashes_received.size()));
|
||||
}
|
||||
}
|
||||
else if (!item_hashes_received.empty())
|
||||
{
|
||||
// we received a list of items and we already have a list of items to fetch from this peer.
|
||||
// In the normal case, this list will immediately follow the existing list, meaning the
|
||||
// last hash of our existing list will match the first hash of the new list.
|
||||
|
||||
// In the much less likely case, we've received a partial list of items from the peer, then
|
||||
// the peer switched forks before sending us the remaining list. In this case, the first
|
||||
// hash in the new list may not be the last hash in the existing list (it may be earlier, or
|
||||
// it may not exist at all.
|
||||
|
||||
// In either case, pop items off the back of our existing list until we find our first
|
||||
// item, then append our list.
|
||||
while (!originating_peer->ids_of_items_to_get.empty())
|
||||
{
|
||||
if (item_hashes_received.front() != originating_peer->ids_of_items_to_get.back())
|
||||
originating_peer->ids_of_items_to_get.pop_back();
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (originating_peer->ids_of_items_to_get.empty())
|
||||
{
|
||||
// this happens when the peer has switched forks between the last inventory message and
|
||||
// this one, and there weren't any unfetched items in common
|
||||
// We don't know where in the blockchain the new front() actually falls, all we can
|
||||
// expect is that it is a block that we knew about because it should be one of the
|
||||
// blocks we sent in the initial synopsis.
|
||||
assert(_delegate->has_item(item_id(_sync_item_type, item_hashes_received.front())));
|
||||
originating_peer->last_block_delegate_has_seen = item_hashes_received.front();
|
||||
originating_peer->last_block_time_delegate_has_seen = _delegate->get_block_time(item_hashes_received.front());
|
||||
dlog("popping item because delegate has already seen it. peer ${peer}'s last block the delegate has seen is now ${block_id} (actual block #${actual_block_num})",
|
||||
("peer", originating_peer->get_remote_endpoint())
|
||||
("block_id", originating_peer->last_block_delegate_has_seen)
|
||||
("actual_block_num", _delegate->get_block_number(item_hashes_received.front())));
|
||||
|
||||
item_hashes_received.pop_front();
|
||||
}
|
||||
dlog("after removing all items we have already seen, item_hashes_received.size() = ${size}", ("size", item_hashes_received.size()));
|
||||
}
|
||||
}
|
||||
else if (!item_hashes_received.empty())
|
||||
{
|
||||
// we received a list of items and we already have a list of items to fetch from this peer.
|
||||
// In the normal case, this list will immediately follow the existing list, meaning the
|
||||
// last hash of our existing list will match the first hash of the new list.
|
||||
|
||||
// In the much less likely case, we've received a partial list of items from the peer, then
|
||||
// the peer switched forks before sending us the remaining list. In this case, the first
|
||||
// hash in the new list may not be the last hash in the existing list (it may be earlier, or
|
||||
// it may not exist at all.
|
||||
|
||||
// In either case, pop items off the back of our existing list until we find our first
|
||||
// item, then append our list.
|
||||
while (!originating_peer->ids_of_items_to_get.empty())
|
||||
{
|
||||
if (item_hashes_received.front() != originating_peer->ids_of_items_to_get.back())
|
||||
originating_peer->ids_of_items_to_get.pop_back();
|
||||
else
|
||||
break;
|
||||
{
|
||||
// the common simple case: the new list extends the old. pop off the duplicate element
|
||||
originating_peer->ids_of_items_to_get.pop_back();
|
||||
}
|
||||
}
|
||||
if (originating_peer->ids_of_items_to_get.empty())
|
||||
|
||||
if (!item_hashes_received.empty() && !originating_peer->ids_of_items_to_get.empty())
|
||||
assert(item_hashes_received.front() != originating_peer->ids_of_items_to_get.back());
|
||||
|
||||
// append the remaining items to the peer's list
|
||||
boost::push_back(originating_peer->ids_of_items_to_get, item_hashes_received);
|
||||
|
||||
originating_peer->number_of_unfetched_item_ids = blockchain_item_ids_inventory_message_received.total_remaining_item_count;
|
||||
|
||||
// at any given time, there's a maximum number of blocks that can possibly be out there
|
||||
// [(now - genesis time) / block interval]. If they offer us more blocks than that,
|
||||
// they must be an attacker or have a buggy client.
|
||||
fc::time_point_sec minimum_time_of_last_offered_block =
|
||||
originating_peer->last_block_time_delegate_has_seen + // timestamp of the block immediately before the first unfetched block
|
||||
originating_peer->number_of_unfetched_item_ids * GRAPHENE_MIN_BLOCK_INTERVAL;
|
||||
fc::time_point_sec now = fc::time_point::now();
|
||||
if (minimum_time_of_last_offered_block > now + GRAPHENE_NET_FUTURE_SYNC_BLOCKS_GRACE_PERIOD_SEC)
|
||||
{
|
||||
// this happens when the peer has switched forks between the last inventory message and
|
||||
// this one, and there weren't any unfetched items in common
|
||||
// We don't know where in the blockchain the new front() actually falls, all we can
|
||||
// expect is that it is a block that we knew about because it should be one of the
|
||||
// blocks we sent in the initial synopsis.
|
||||
assert(_delegate->has_item(item_id(_sync_item_type, item_hashes_received.front())));
|
||||
originating_peer->last_block_delegate_has_seen = item_hashes_received.front();
|
||||
originating_peer->last_block_time_delegate_has_seen = _delegate->get_block_time(item_hashes_received.front());
|
||||
item_hashes_received.pop_front();
|
||||
wlog("Disconnecting from peer ${peer} who offered us an implausible number of blocks, their last block would be in the future (${timestamp})",
|
||||
("peer", originating_peer->get_remote_endpoint())
|
||||
("timestamp", minimum_time_of_last_offered_block));
|
||||
fc::exception error_for_peer(FC_LOG_MESSAGE(error, "You offered me a list of more sync blocks than could possibly exist. Total blocks offered: ${blocks}, Minimum time of the last block you offered: ${minimum_time_of_last_offered_block}, Now: ${now}",
|
||||
("blocks", originating_peer->number_of_unfetched_item_ids)
|
||||
("minimum_time_of_last_offered_block", minimum_time_of_last_offered_block)
|
||||
("now", now)));
|
||||
disconnect_from_peer(originating_peer,
|
||||
"You offered me a list of more sync blocks than could possibly exist",
|
||||
true, error_for_peer);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t new_number_of_unfetched_items = calculate_unsynced_block_count_from_all_peers();
|
||||
if (new_number_of_unfetched_items != _total_number_of_unfetched_items)
|
||||
_delegate->sync_status(blockchain_item_ids_inventory_message_received.item_type,
|
||||
new_number_of_unfetched_items);
|
||||
_total_number_of_unfetched_items = new_number_of_unfetched_items;
|
||||
|
||||
if (blockchain_item_ids_inventory_message_received.total_remaining_item_count != 0)
|
||||
{
|
||||
// the peer hasn't sent us all the items it knows about.
|
||||
if (originating_peer->ids_of_items_to_get.size() > GRAPHENE_NET_MIN_BLOCK_IDS_TO_PREFETCH)
|
||||
{
|
||||
// we have a good number of item ids from this peer, start fetching blocks from it;
|
||||
// we'll switch back later to finish the job.
|
||||
trigger_fetch_sync_items_loop();
|
||||
}
|
||||
else
|
||||
{
|
||||
// keep fetching the peer's list of sync items until we get enough to switch into block-
|
||||
// fetchimg mode
|
||||
fetch_next_batch_of_item_ids_from_peer(originating_peer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the common simple case: the new list extends the old. pop off the duplicate element
|
||||
originating_peer->ids_of_items_to_get.pop_back();
|
||||
// the peer has told us about all of the items it knows
|
||||
if (!originating_peer->ids_of_items_to_get.empty())
|
||||
{
|
||||
// we now know about all of the items the peer knows about, and there are some items on the list
|
||||
// that we should try to fetch. Kick off the fetch loop.
|
||||
trigger_fetch_sync_items_loop();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we get here, the peer has sent us a non-empty list of items, but we have already
|
||||
// received all of the items from other peers. Send a new request to the peer to
|
||||
// see if we're really in sync
|
||||
fetch_next_batch_of_item_ids_from_peer(originating_peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!item_hashes_received.empty() && !originating_peer->ids_of_items_to_get.empty())
|
||||
assert(item_hashes_received.front() != originating_peer->ids_of_items_to_get.back());
|
||||
|
||||
// append the remaining items to the peer's list
|
||||
boost::push_back(originating_peer->ids_of_items_to_get, item_hashes_received);
|
||||
|
||||
originating_peer->number_of_unfetched_item_ids = blockchain_item_ids_inventory_message_received.total_remaining_item_count;
|
||||
|
||||
// at any given time, there's a maximum number of blocks that can possibly be out there
|
||||
// [(now - genesis time) / block interval]. If they offer us more blocks than that,
|
||||
// they must be an attacker or have a buggy client.
|
||||
fc::time_point_sec minimum_time_of_last_offered_block =
|
||||
originating_peer->last_block_time_delegate_has_seen + // timestamp of the block immediately before the first unfetched block
|
||||
originating_peer->number_of_unfetched_item_ids * GRAPHENE_MIN_BLOCK_INTERVAL;
|
||||
if (minimum_time_of_last_offered_block > _delegate->get_blockchain_now() + GRAPHENE_NET_FUTURE_SYNC_BLOCKS_GRACE_PERIOD_SEC)
|
||||
catch (const fc::canceled_exception&)
|
||||
{
|
||||
wlog("Disconnecting from peer ${peer} who offered us an implausible number of blocks, their last block would be in the future (${timestamp})",
|
||||
("peer", originating_peer->get_remote_endpoint())
|
||||
("timestamp", minimum_time_of_last_offered_block));
|
||||
fc::exception error_for_peer(FC_LOG_MESSAGE(error, "You offered me a list of more sync blocks than could possibly exist. Total blocks offered: ${blocks}, Minimum time of the last block you offered: ${minimum_time_of_last_offered_block}, Now: ${now}",
|
||||
("blocks", originating_peer->number_of_unfetched_item_ids)
|
||||
("minimum_time_of_last_offered_block", minimum_time_of_last_offered_block)
|
||||
("now", _delegate->get_blockchain_now())));
|
||||
disconnect_from_peer(originating_peer,
|
||||
"You offered me a list of more sync blocks than could possibly exist",
|
||||
true, error_for_peer);
|
||||
return;
|
||||
throw;
|
||||
}
|
||||
|
||||
uint32_t new_number_of_unfetched_items = calculate_unsynced_block_count_from_all_peers();
|
||||
if (new_number_of_unfetched_items != _total_number_of_unfetched_items)
|
||||
_delegate->sync_status(blockchain_item_ids_inventory_message_received.item_type,
|
||||
new_number_of_unfetched_items);
|
||||
_total_number_of_unfetched_items = new_number_of_unfetched_items;
|
||||
|
||||
if (blockchain_item_ids_inventory_message_received.total_remaining_item_count != 0)
|
||||
catch (const fc::exception& e)
|
||||
{
|
||||
// the peer hasn't sent us all the items it knows about.
|
||||
if (originating_peer->ids_of_items_to_get.size() > GRAPHENE_NET_MIN_BLOCK_IDS_TO_PREFETCH)
|
||||
{
|
||||
// we have a good number of item ids from this peer, start fetching blocks from it;
|
||||
// we'll switch back later to finish the job.
|
||||
trigger_fetch_sync_items_loop();
|
||||
}
|
||||
else
|
||||
{
|
||||
// keep fetching the peer's list of sync items until we get enough to switch into block-
|
||||
// fetchimg mode
|
||||
fetch_next_batch_of_item_ids_from_peer(originating_peer);
|
||||
}
|
||||
elog("Caught unexpected exception: ${e}", ("e", e));
|
||||
assert(false && "exceptions not expected here");
|
||||
}
|
||||
else
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// the peer has told us about all of the items it knows
|
||||
if (!originating_peer->ids_of_items_to_get.empty())
|
||||
{
|
||||
// we now know about all of the items the peer knows about, and there are some items on the list
|
||||
// that we should try to fetch. Kick off the fetch loop.
|
||||
trigger_fetch_sync_items_loop();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we get here, the peer has sent us a non-empty list of items, but we have already
|
||||
// received all of the items from other peers. Send a new request to the peer to
|
||||
// see if we're really in sync
|
||||
fetch_next_batch_of_item_ids_from_peer(originating_peer);
|
||||
}
|
||||
elog("Caught unexpected exception: ${e}", ("e", e.what()));
|
||||
assert(false && "exceptions not expected here");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
elog("Caught unexpected exception, could break sync operation");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -3270,7 +3304,33 @@ namespace graphene { namespace net { namespace detail {
|
|||
block_processed_this_iteration = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
dlog("Already received and accepted this block (presumably through normal inventory mechanism), treating it as accepted");
|
||||
std::vector< peer_connection_ptr > peers_needing_next_batch;
|
||||
for (const peer_connection_ptr& peer : _active_connections)
|
||||
{
|
||||
auto items_being_processed_iter = peer->ids_of_items_being_processed.find(received_block_iter->block_id);
|
||||
if (items_being_processed_iter != peer->ids_of_items_being_processed.end())
|
||||
{
|
||||
peer->ids_of_items_being_processed.erase(items_being_processed_iter);
|
||||
dlog("Removed item from ${endpoint}'s list of items being processed, still processing ${len} blocks",
|
||||
("endpoint", peer->get_remote_endpoint())("len", peer->ids_of_items_being_processed.size()));
|
||||
|
||||
// if we just processed the last item in our list from this peer, we will want to
|
||||
// send another request to find out if we are now in sync (this is normally handled in
|
||||
// send_sync_block_to_node_delegate)
|
||||
if (peer->ids_of_items_to_get.empty() &&
|
||||
peer->number_of_unfetched_item_ids == 0 &&
|
||||
peer->ids_of_items_being_processed.empty())
|
||||
{
|
||||
dlog("We received last item in our list for peer ${endpoint}, setup to do a sync check", ("endpoint", peer->get_remote_endpoint()));
|
||||
peers_needing_next_batch.push_back( peer );
|
||||
}
|
||||
}
|
||||
}
|
||||
for( const peer_connection_ptr& peer : peers_needing_next_batch )
|
||||
fetch_next_batch_of_item_ids_from_peer(peer.get());
|
||||
}
|
||||
|
||||
break; // start iterating _received_sync_items from the beginning
|
||||
} // end if potential_first_block
|
||||
|
|
@ -3486,19 +3546,43 @@ namespace graphene { namespace net { namespace detail {
|
|||
if (sync_item_iter != originating_peer->sync_items_requested_from_peer.end())
|
||||
{
|
||||
originating_peer->sync_items_requested_from_peer.erase(sync_item_iter);
|
||||
_active_sync_requests.erase(block_message_to_process.block_id);
|
||||
process_block_during_sync(originating_peer, block_message_to_process, message_hash);
|
||||
if (originating_peer->idle())
|
||||
// if exceptions are throw here after removing the sync item from the list (above),
|
||||
// it could leave our sync in a stalled state. Wrap a try/catch around the rest
|
||||
// of the function so we can log if this ever happens.
|
||||
try
|
||||
{
|
||||
// we have finished fetching a batch of items, so we either need to grab another batch of items
|
||||
// or we need to get another list of item ids.
|
||||
if (originating_peer->number_of_unfetched_item_ids > 0 &&
|
||||
originating_peer->ids_of_items_to_get.size() < GRAPHENE_NET_MIN_BLOCK_IDS_TO_PREFETCH)
|
||||
fetch_next_batch_of_item_ids_from_peer(originating_peer);
|
||||
else
|
||||
trigger_fetch_sync_items_loop();
|
||||
_active_sync_requests.erase(block_message_to_process.block_id);
|
||||
process_block_during_sync(originating_peer, block_message_to_process, message_hash);
|
||||
if (originating_peer->idle())
|
||||
{
|
||||
// we have finished fetching a batch of items, so we either need to grab another batch of items
|
||||
// or we need to get another list of item ids.
|
||||
if (originating_peer->number_of_unfetched_item_ids > 0 &&
|
||||
originating_peer->ids_of_items_to_get.size() < GRAPHENE_NET_MIN_BLOCK_IDS_TO_PREFETCH)
|
||||
fetch_next_batch_of_item_ids_from_peer(originating_peer);
|
||||
else
|
||||
trigger_fetch_sync_items_loop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
catch (const fc::canceled_exception& e)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (const fc::exception& e)
|
||||
{
|
||||
elog("Caught unexpected exception: ${e}", ("e", e));
|
||||
assert(false && "exceptions not expected here");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
elog("Caught unexpected exception: ${e}", ("e", e.what()));
|
||||
assert(false && "exceptions not expected here");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
elog("Caught unexpected exception, could break sync operation");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4192,7 +4276,7 @@ namespace graphene { namespace net { namespace detail {
|
|||
|
||||
// limit the rate at which we accept connections to mitigate DOS attacks
|
||||
fc::usleep( fc::milliseconds(10) );
|
||||
} FC_CAPTURE_AND_RETHROW()
|
||||
} FC_CAPTURE_AND_LOG( (0) )
|
||||
}
|
||||
} // accept_loop()
|
||||
|
||||
|
|
@ -4393,7 +4477,7 @@ namespace graphene { namespace net { namespace detail {
|
|||
_node_configuration = detail::node_configuration();
|
||||
|
||||
#ifdef GRAPHENE_TEST_NETWORK
|
||||
uint32_t port = GRAPHENE_NET_TEST_P2P_PORT + GRAPHENE_TEST_NETWORK_VERSION;
|
||||
uint32_t port = GRAPHENE_NET_TEST_P2P_PORT;
|
||||
#else
|
||||
uint32_t port = GRAPHENE_NET_DEFAULT_P2P_PORT;
|
||||
#endif
|
||||
|
|
@ -4489,7 +4573,8 @@ namespace graphene { namespace net { namespace detail {
|
|||
error_message_stream << "\nStill waiting for port " << listen_endpoint.port() << " to become available\n";
|
||||
}
|
||||
std::string error_message = error_message_stream.str();
|
||||
ulog(error_message);
|
||||
wlog(error_message);
|
||||
std::cout << "\033[31m" << error_message;
|
||||
_delegate->error_encountered( error_message, fc::oexception() );
|
||||
fc::usleep( fc::seconds(5 ) );
|
||||
}
|
||||
|
|
@ -5335,17 +5420,21 @@ namespace graphene { namespace net { namespace detail {
|
|||
# define INVOKE_AND_COLLECT_STATISTICS(method_name, ...) \
|
||||
try \
|
||||
{ \
|
||||
call_statistics_collector statistics_collector(#method_name, \
|
||||
&_ ## method_name ## _execution_accumulator, \
|
||||
&_ ## method_name ## _delay_before_accumulator, \
|
||||
&_ ## method_name ## _delay_after_accumulator); \
|
||||
if (_thread->is_current()) \
|
||||
{ \
|
||||
call_statistics_collector statistics_collector(#method_name, \
|
||||
&_ ## method_name ## _execution_accumulator, \
|
||||
&_ ## method_name ## _delay_before_accumulator, \
|
||||
&_ ## method_name ## _delay_after_accumulator); \
|
||||
call_statistics_collector::actual_execution_measurement_helper helper(statistics_collector); \
|
||||
return _node_delegate->method_name(__VA_ARGS__); \
|
||||
} \
|
||||
else \
|
||||
return _thread->async([&](){ \
|
||||
call_statistics_collector statistics_collector(#method_name, \
|
||||
&_ ## method_name ## _execution_accumulator, \
|
||||
&_ ## method_name ## _delay_before_accumulator, \
|
||||
&_ ## method_name ## _delay_after_accumulator); \
|
||||
call_statistics_collector::actual_execution_measurement_helper helper(statistics_collector); \
|
||||
return _node_delegate->method_name(__VA_ARGS__); \
|
||||
}, "invoke " BOOST_STRINGIZE(method_name)).wait(); \
|
||||
|
|
@ -5367,17 +5456,21 @@ namespace graphene { namespace net { namespace detail {
|
|||
}
|
||||
#else
|
||||
# define INVOKE_AND_COLLECT_STATISTICS(method_name, ...) \
|
||||
call_statistics_collector statistics_collector(#method_name, \
|
||||
&_ ## method_name ## _execution_accumulator, \
|
||||
&_ ## method_name ## _delay_before_accumulator, \
|
||||
&_ ## method_name ## _delay_after_accumulator); \
|
||||
if (_thread->is_current()) \
|
||||
{ \
|
||||
call_statistics_collector statistics_collector(#method_name, \
|
||||
&_ ## method_name ## _execution_accumulator, \
|
||||
&_ ## method_name ## _delay_before_accumulator, \
|
||||
&_ ## method_name ## _delay_after_accumulator); \
|
||||
call_statistics_collector::actual_execution_measurement_helper helper(statistics_collector); \
|
||||
return _node_delegate->method_name(__VA_ARGS__); \
|
||||
} \
|
||||
else \
|
||||
return _thread->async([&](){ \
|
||||
call_statistics_collector statistics_collector(#method_name, \
|
||||
&_ ## method_name ## _execution_accumulator, \
|
||||
&_ ## method_name ## _delay_before_accumulator, \
|
||||
&_ ## method_name ## _delay_after_accumulator); \
|
||||
call_statistics_collector::actual_execution_measurement_helper helper(statistics_collector); \
|
||||
return _node_delegate->method_name(__VA_ARGS__); \
|
||||
}, "invoke " BOOST_STRINGIZE(method_name)).wait()
|
||||
|
|
@ -5447,14 +5540,6 @@ namespace graphene { namespace net { namespace detail {
|
|||
INVOKE_AND_COLLECT_STATISTICS(get_block_time, block_id);
|
||||
}
|
||||
|
||||
/** returns graphene::blockchain::now() */
|
||||
fc::time_point_sec statistics_gathering_node_delegate_wrapper::get_blockchain_now()
|
||||
{
|
||||
// this function doesn't need to block,
|
||||
ASSERT_TASK_NOT_PREEMPTED();
|
||||
return _node_delegate->get_blockchain_now();
|
||||
}
|
||||
|
||||
item_hash_t statistics_gathering_node_delegate_wrapper::get_head_block_id() const
|
||||
{
|
||||
INVOKE_AND_COLLECT_STATISTICS(get_head_block_id);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
|
||||
#include <fc/thread/thread.hpp>
|
||||
|
||||
#include <boost/scope_exit.hpp>
|
||||
|
||||
#ifdef DEFAULT_LOGGER
|
||||
# undef DEFAULT_LOGGER
|
||||
#endif
|
||||
|
|
@ -86,11 +88,12 @@ namespace graphene { namespace net
|
|||
inhibit_fetching_sync_blocks(false),
|
||||
transaction_fetching_inhibited_until(fc::time_point::min()),
|
||||
last_known_fork_block_number(0),
|
||||
firewall_check_state(nullptr)
|
||||
firewall_check_state(nullptr),
|
||||
#ifndef NDEBUG
|
||||
,_thread(&fc::thread::current()),
|
||||
_send_message_queue_tasks_running(0)
|
||||
_thread(&fc::thread::current()),
|
||||
_send_message_queue_tasks_running(0),
|
||||
#endif
|
||||
_currently_handling_message(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -265,6 +268,10 @@ namespace graphene { namespace net
|
|||
void peer_connection::on_message( message_oriented_connection* originating_connection, const message& received_message )
|
||||
{
|
||||
VERIFY_CORRECT_THREAD();
|
||||
_currently_handling_message = true;
|
||||
BOOST_SCOPE_EXIT(this_) {
|
||||
this_->_currently_handling_message = false;
|
||||
} BOOST_SCOPE_EXIT_END
|
||||
_node->on_message( this, received_message );
|
||||
}
|
||||
|
||||
|
|
@ -438,18 +445,24 @@ namespace graphene { namespace net
|
|||
_remote_endpoint = new_remote_endpoint;
|
||||
}
|
||||
|
||||
bool peer_connection::busy()
|
||||
bool peer_connection::busy() const
|
||||
{
|
||||
VERIFY_CORRECT_THREAD();
|
||||
return !items_requested_from_peer.empty() || !sync_items_requested_from_peer.empty() || item_ids_requested_from_peer;
|
||||
}
|
||||
|
||||
bool peer_connection::idle()
|
||||
bool peer_connection::idle() const
|
||||
{
|
||||
VERIFY_CORRECT_THREAD();
|
||||
return !busy();
|
||||
}
|
||||
|
||||
bool peer_connection::is_currently_handling_message() const
|
||||
{
|
||||
VERIFY_CORRECT_THREAD();
|
||||
return _currently_handling_message;
|
||||
}
|
||||
|
||||
bool peer_connection::is_transaction_fetching_inhibited() const
|
||||
{
|
||||
VERIFY_CORRECT_THREAD();
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
file(GLOB HEADERS "include/graphene/p2p/*.hpp")
|
||||
|
||||
set(SOURCES node.cpp
|
||||
stcp_socket.cpp
|
||||
peer_connection.cpp
|
||||
message_oriented_connection.cpp)
|
||||
|
||||
add_library( graphene_p2p ${SOURCES} ${HEADERS} )
|
||||
|
||||
target_link_libraries( graphene_p2p
|
||||
PUBLIC fc graphene_db )
|
||||
target_include_directories( graphene_p2p
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
PRIVATE "${CMAKE_SOURCE_DIR}/libraries/chain/include"
|
||||
)
|
||||
|
||||
#if(MSVC)
|
||||
# set_source_files_properties( node.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
|
||||
#endif(MSVC)
|
||||
|
||||
#if (USE_PCH)
|
||||
# set_target_properties(graphene_p2p PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
|
||||
# cotire(graphene_p2p )
|
||||
#endif(USE_PCH)
|
||||
|
||||
install( TARGETS
|
||||
graphene_p2p
|
||||
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
# Network Protocol 2
|
||||
|
||||
Building a low-latency network requires P2P nodes that have low-latency
|
||||
connections and a protocol designed to minimize latency. for the purpose
|
||||
of this document we will assume that two nodes are located on opposite
|
||||
sides of the globe with a ping time of 250ms.
|
||||
|
||||
|
||||
## Announce, Request, Send Protocol
|
||||
Under the prior network archtiecture, transactions and blocks were broadcast
|
||||
in a manner similar to the Bitcoin protocol: inventory messages notify peers of
|
||||
transactions and blocks, then peers fetch the transaction or block from one
|
||||
peer. After validating the item a node will broadcast an inventory message to
|
||||
its peers.
|
||||
|
||||
Under this model it will take 0.75 seconds for a peer to communicate a transaction
|
||||
or block to another peer even if their size was 0 and there was no processing overhead.
|
||||
This level of performance is unacceptable for a network attempting to produce one block
|
||||
every second.
|
||||
|
||||
This prior protocol also sent every transaction twice: initial broadcast, and again as
|
||||
part of a block.
|
||||
|
||||
|
||||
## Push Protocol
|
||||
To minimize latency each node needs to immediately broadcast the data it receives
|
||||
to its peers after validating it. Given the average transaction size is less than
|
||||
100 bytes, it is almost as effecient to send the transaction as it is to send
|
||||
the notice (assuming a 20 byte transaction id)
|
||||
|
||||
Each node implements the following protocol:
|
||||
|
||||
|
||||
onReceiveTransaction( from_peer, transaction )
|
||||
if( isKnown( transaction.id() ) )
|
||||
return
|
||||
|
||||
markKnown( transaction.id() )
|
||||
|
||||
if( !validate( transaction ) )
|
||||
return
|
||||
|
||||
for( peer : peers )
|
||||
if( peer != from_peer )
|
||||
send( peer, transaction )
|
||||
|
||||
|
||||
onReceiveBlock( from_peer, block_summary )
|
||||
if( isKnown( block_summary )
|
||||
return
|
||||
|
||||
full_block = reconstructFullBlcok( from_peer, block_summary )
|
||||
if( !full_block ) disconnect from_peer
|
||||
|
||||
markKnown( block_summary )
|
||||
|
||||
if( !pushBlock( full_block ) ) disconnect from_peer
|
||||
|
||||
for( peer : peers )
|
||||
if( peer != from_peer )
|
||||
send( peer, block_summary )
|
||||
|
||||
|
||||
onHello( new_peer, new_peer_head_block_num )
|
||||
|
||||
replyHello( new_peer ) // ack the hello message with our timestamp to measure latency
|
||||
|
||||
if( peers.size() >= max_peers )
|
||||
send( new_peer, peers )
|
||||
disconnect( new_peer )
|
||||
return
|
||||
|
||||
while( new_peer_head_block_num < our_head_block_num )
|
||||
sendFullBlock( new_peer, ++new_peer_head_block_num )
|
||||
|
||||
new_peer.synced = true
|
||||
for( peer : peers )
|
||||
send( peer, new_peer )
|
||||
|
||||
onHelloReply( from_peer, hello_reply )
|
||||
update_latency_measure, disconnect if too slow
|
||||
|
||||
onReceivePeers( from_peer, peers )
|
||||
addToPotentialPeers( peers )
|
||||
|
||||
onUpdateConnectionsTimer
|
||||
if( peers.size() < desired_peers )
|
||||
connect( random_potential_peer )
|
||||
|
||||
onFullBlock( from_peer, full_block )
|
||||
if( !pushBlock( full_block ) ) disconnect from_peer
|
||||
|
||||
onStartup
|
||||
init_potential_peers from config
|
||||
start onUpdateConnectionsTimer
|
||||
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
#include <fc/array.hpp>
|
||||
#include <fc/io/varint.hpp>
|
||||
#include <fc/network/ip.hpp>
|
||||
#include <fc/io/raw.hpp>
|
||||
#include <fc/crypto/ripemd160.hpp>
|
||||
#include <fc/reflect/variant.hpp>
|
||||
|
||||
namespace graphene { namespace p2p {
|
||||
using namespace graphene::chain;
|
||||
|
||||
struct message_header
|
||||
{
|
||||
uint32_t size; // number of bytes in message, capped at MAX_MESSAGE_SIZE
|
||||
uint32_t msg_type;
|
||||
};
|
||||
|
||||
typedef fc::uint160_t message_hash_type;
|
||||
|
||||
/**
|
||||
* Abstracts the process of packing/unpacking a message for a
|
||||
* particular channel.
|
||||
*/
|
||||
struct message : public message_header
|
||||
{
|
||||
std::vector<char> data;
|
||||
|
||||
message(){}
|
||||
|
||||
message( message&& m )
|
||||
:message_header(m),data( std::move(m.data) ){}
|
||||
|
||||
message( const message& m )
|
||||
:message_header(m),data( m.data ){}
|
||||
|
||||
/**
|
||||
* Assumes that T::type specifies the message type
|
||||
*/
|
||||
template<typename T>
|
||||
message( const T& m )
|
||||
{
|
||||
msg_type = T::type;
|
||||
data = fc::raw::pack(m);
|
||||
size = (uint32_t)data.size();
|
||||
}
|
||||
|
||||
fc::uint160_t id()const
|
||||
{
|
||||
return fc::ripemd160::hash( data.data(), (uint32_t)data.size() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically checks the type and deserializes T in the
|
||||
* opposite process from the constructor.
|
||||
*/
|
||||
template<typename T>
|
||||
T as()const
|
||||
{
|
||||
try {
|
||||
FC_ASSERT( msg_type == T::type );
|
||||
T tmp;
|
||||
if( data.size() )
|
||||
{
|
||||
fc::datastream<const char*> ds( data.data(), data.size() );
|
||||
fc::raw::unpack( ds, tmp );
|
||||
}
|
||||
else
|
||||
{
|
||||
// just to make sure that tmp shouldn't have any data
|
||||
fc::datastream<const char*> ds( nullptr, 0 );
|
||||
fc::raw::unpack( ds, tmp );
|
||||
}
|
||||
return tmp;
|
||||
} FC_RETHROW_EXCEPTIONS( warn,
|
||||
"error unpacking network message as a '${type}' ${x} !=? ${msg_type}",
|
||||
("type", fc::get_typename<T>::name() )
|
||||
("x", T::type)
|
||||
("msg_type", msg_type)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
enum core_message_type_enum {
|
||||
hello_message_type = 1000,
|
||||
transaction_message_type = 1001,
|
||||
block_message_type = 1002,
|
||||
peer_message_type = 1003,
|
||||
error_message_type = 1004
|
||||
};
|
||||
|
||||
struct hello_message
|
||||
{
|
||||
static const core_message_type_enum type;
|
||||
|
||||
std::string user_agent;
|
||||
uint16_t version;
|
||||
fc::time_point timestamp;
|
||||
|
||||
fc::ip::address inbound_address;
|
||||
uint16_t inbound_port;
|
||||
uint16_t outbound_port;
|
||||
public_key_type node_public_key;
|
||||
fc::sha256 chain_id;
|
||||
fc::variant_object user_data;
|
||||
block_id_type head_block;
|
||||
};
|
||||
|
||||
struct hello_reply_message
|
||||
{
|
||||
static const core_message_type_enum type;
|
||||
|
||||
fc::time_point hello_timestamp;
|
||||
fc::time_point reply_timestamp;
|
||||
};
|
||||
|
||||
struct transaction_message
|
||||
{
|
||||
static const core_message_type_enum type;
|
||||
signed_transaction trx;
|
||||
};
|
||||
|
||||
struct block_summary_message
|
||||
{
|
||||
static const core_message_type_enum type;
|
||||
|
||||
signed_block_header header;
|
||||
vector<transaction_id_type> transaction_ids;
|
||||
};
|
||||
|
||||
struct full_block_message
|
||||
{
|
||||
static const core_message_type_enum type;
|
||||
signed_block block;
|
||||
};
|
||||
|
||||
struct peers_message
|
||||
{
|
||||
static const core_message_type_enum type;
|
||||
|
||||
vector<fc::ip::endpoint> peers;
|
||||
};
|
||||
|
||||
struct error_message
|
||||
{
|
||||
static const core_message_type_enum type;
|
||||
string message;
|
||||
};
|
||||
|
||||
|
||||
} } // graphene::p2p
|
||||
|
||||
FC_REFLECT( graphene::p2p::message_header, (size)(msg_type) )
|
||||
FC_REFLECT_DERIVED( graphene::p2p::message, (graphene::p2p::message_header), (data) )
|
||||
FC_REFLECT_ENUM( graphene::p2p::core_message_type_enum,
|
||||
(hello_message_type)
|
||||
(transaction_message_type)
|
||||
(block_message_type)
|
||||
(peer_message_type)
|
||||
(error_message_type)
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue