Merge remote-tracking branch 'origin/merge_baxter_with_github' into beatrice

This commit is contained in:
Fabian Schuh 2018-10-11 13:50:53 +02:00
commit 8fb62ed768
No known key found for this signature in database
GPG key ID: F2538A4B282D6238
75 changed files with 218022 additions and 221355 deletions

7
.gitignore vendored
View file

@ -8,8 +8,6 @@ Makefile
compile_commands.json
moc_*
*.moc
genesis.json
hardfork.hpp
libraries/utilities/git_revision.cpp
@ -41,6 +39,5 @@ object_database/*
*.pyc
*.pyo
.romek
.*
.vscode
.DS_Store

View file

@ -1,51 +0,0 @@
# Tries to find Gperftools.
#
# Usage of this module as follows:
#
# find_package(Gperftools)
#
# Variables used by this module, they can change the default behaviour and need
# to be set before calling find_package:
#
# Gperftools_ROOT_DIR Set this variable to the root installation of
# Gperftools if the module has problems finding
# the proper installation path.
#
# Variables defined by this module:
#
# GPERFTOOLS_FOUND System has Gperftools libs/headers
# GPERFTOOLS_LIBRARIES The Gperftools libraries (tcmalloc & profiler)
# GPERFTOOLS_INCLUDE_DIR The location of Gperftools headers
find_library(GPERFTOOLS_TCMALLOC
NAMES tcmalloc
HINTS ${Gperftools_ROOT_DIR}/lib)
find_library(GPERFTOOLS_PROFILER
NAMES profiler
HINTS ${Gperftools_ROOT_DIR}/lib)
find_library(GPERFTOOLS_TCMALLOC_AND_PROFILER
NAMES tcmalloc_and_profiler
HINTS ${Gperftools_ROOT_DIR}/lib)
find_path(GPERFTOOLS_INCLUDE_DIR
NAMES gperftools/heap-profiler.h
HINTS ${Gperftools_ROOT_DIR}/include)
set(GPERFTOOLS_LIBRARIES ${GPERFTOOLS_TCMALLOC_AND_PROFILER})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
Gperftools
DEFAULT_MSG
GPERFTOOLS_LIBRARIES
GPERFTOOLS_INCLUDE_DIR)
mark_as_advanced(
Gperftools_ROOT_DIR
GPERFTOOLS_TCMALLOC
GPERFTOOLS_PROFILER
GPERFTOOLS_TCMALLOC_AND_PROFILER
GPERFTOOLS_LIBRARIES
GPERFTOOLS_INCLUDE_DIR)

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
Copyright (c) 2015-2016 Cryptonomex Inc. <contact@cryptonomex.com>
Copyright (c) 2015-2017 contributors <CONTRIBUTORS.txt>
Copyright (c) 2017 - 2018 Peerplays Blockchain Standards Association, and contributors.
The MIT License

345
README.md
View file

@ -1,231 +1,212 @@
BitShares Core
==============
* [Getting Started](#getting-started)
* [Support](#support)
* [Using the API](#using-the-api)
* [Accessing restricted API's](#accessing-restricted-apis)
* [FAQ](#faq)
* [License](#license)
Intro for new developers and witnesses
------------------------
BitShares Core is the BitShares blockchain implementation and command-line interface.
The web wallet is [BitShares UI](https://github.com/bitshares/bitshares-ui).
This is a quick introduction to get new developers and witnesses up to speed on Peerplays blockchain. It is intended for witnesses plannig to join a live, already deployed blockchain.
Visit [BitShares.org](https://bitshares.org/) to learn about BitShares and join the community at [BitSharesTalk.org](https://bitsharestalk.org/).
Starting A Peerplays Node
-----------------
**NOTE:** The official BitShares git repository location, default branch, and submodule remotes were recently changed. Existing
repositories can be updated with the following steps:
For Ubuntu 14.04 LTS and up users, see this link first:
https://github.com/cryptonomex/graphene/wiki/build-ubuntu
git remote set-url origin https://github.com/bitshares/bitshares-core.git
git checkout master
git remote set-head origin --auto
git pull
git submodule sync --recursive
and then proceed with:
git clone https://github.com/pbsa/peerplays.git
cd peerplays
git submodule update --init --recursive
Getting Started
---------------
Build instructions and additional documentation are available in the
[wiki](https://github.com/bitshares/bitshares-core/wiki).
We recommend building on Ubuntu 16.04 LTS, and the build dependencies may be installed with:
sudo apt-get update
sudo apt-get install autoconf cmake git libboost-all-dev libssl-dev
To build after all dependencies are installed:
git clone https://github.com/bitshares/bitshares-core.git
cd bitshares-core
git checkout <LATEST_RELEASE_TAG>
git submodule update --init --recursive
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .
cmake -DBOOST_ROOT="$BOOST_ROOT" -DCMAKE_BUILD_TYPE=Release .
make
./programs/witness_node/witness_node
Launching the witness creates required directories. Next, **stop the witness** and continue.
**NOTE:** BitShares requires an [OpenSSL](https://www.openssl.org/) version in the 1.0.x series. OpenSSL 1.1.0 and newer are NOT supported. If your system OpenSSL version is newer, then you will need to manually provide an older version of OpenSSL and specify it to CMake using `-DOPENSSL_INCLUDE_DIR`, `-DOPENSSL_SSL_LIBRARY`, and `-DOPENSSL_CRYPTO_LIBRARY`.
**NOTE:** BitShares requires a [Boost](http://www.boost.org/) version in the range [1.57, 1.60]. Versions earlier than
1.57 or newer than 1.60 are NOT supported. If your system Boost version is newer, then you will need to manually build
an older version of Boost and specify it to CMake using `DBOOST_ROOT`.
After building, the witness node can be launched with:
vi witness_node_data_dir/config.ini
p2p-endpoint = 0.0.0.0:9777
rpc-endpoint = 127.0.0.1:8090
seed-node = 213.184.225.234:59500
Start the witness back up
./programs/witness_node/witness_node
The node will automatically create a data directory including a config file. It may take several hours to fully synchronize
the blockchain. After syncing, you can exit the node using Ctrl+C and setup the command-line wallet by editing
`witness_node_data_dir/config.ini` as follows:
Upgrading A Peerplays Node
-----------------
To minimize downtime of your peerplays node when upgrading, one upgrade idea was written in this steemit article: https://steemit.com/peerplays/@joseph/peerplays-update-setting-a-backup-witness-server-switching-servers
rpc-endpoint = 127.0.0.1:8090
After starting the witness node again, in a separate terminal you can run:
Wallet Setup
-----------------
Then, in a separate terminal window, start the command-line wallet `cli_wallet`:
./programs/cli_wallet/cli_wallet
Set your inital password:
To set your initial password to 'password' use:
>>> set_password <PASSWORD>
>>> unlock <PASSWORD>
>>> set_password password
>>> unlock password
To import your initial balance:
A list of CLI wallet commands is available
[here](https://github.com/PBSA/peerplays/blob/master/libraries/wallet/include/graphene/wallet/wallet.hpp).
>>> import_balance <ACCOUNT NAME> [<WIF_KEY>] true
If you send private keys over this connection, `rpc-endpoint` should be bound to localhost for security.
Testnet
----------------------
- chain-id - 5b37954aa0d33a8e0d57b084995c262a7c13dbc0693d3e96654e63ff45a9ceec
Use `help` to see all available wallet commands. Source definition and listing of all commands is available
[here](https://github.com/bitshares/bitshares-core/blob/master/libraries/wallet/include/graphene/wallet/wallet.hpp).
Register your username at the faucet address
---------------------------
https://595-dev.pixelplex.by/
Support
-------
Technical support is available in the [BitSharesTalk technical support subforum](https://bitsharestalk.org/index.php?board=45.0).
BitShares Core bugs can be reported directly to the [issue tracker](https://github.com/bitshares/bitshares-core/issues).
Use the get_private_key_from_password command
---------------------------------
You will to generate owner and active keys
BitShares UI bugs should be reported to the [UI issue tracker](https://github.com/bitshares/bitshares-ui/issues)
```
get_private_key_from_password your_witness_username active the_key_you_received_from_the_faucet
```
This will reveal an array for your active key `["PPYxxx", "xxxx"]`
Using the API
-------------
import_keys into your cli_wallet
-------------------------------
- use the second value in the array returned from the previous step for the private key
- be sure to wrap your username in quotes
- import the key with this command
```
import_key "your_witness_username" xxxx
```
We provide several different API's. Each API has its own ID.
When running `witness_node`, initially two API's are available:
API 0 provides read-only access to the database, while API 1 is
used to login and gain access to additional, restricted API's.
Upgrade your account to lifetime membership
--------------------------------
```
upgrade_account your_witness_username true
```
Here is an example using `wscat` package from `npm` for websockets:
Create your witness (substitute the url for your witness information)
-------------------------------
- place quotes around url
```
create_witness your_witness_username "url" true
```
**Be sure to take note of the block_signing_key**
$ npm install -g wscat
$ wscat -c ws://127.0.0.1:8090
> {"id":1, "method":"call", "params":[0,"get_accounts",[["1.2.0"]]]}
< {"id":1,"result":[{"id":"1.2.0","annotations":[],"membership_expiration_date":"1969-12-31T23:59:59","registrar":"1.2.0","referrer":"1.2.0","lifetime_referrer":"1.2.0","network_fee_percentage":2000,"lifetime_referrer_fee_percentage":8000,"referrer_rewards_percentage":0,"name":"committee-account","owner":{"weight_threshold":1,"account_auths":[],"key_auths":[],"address_auths":[]},"active":{"weight_threshold":6,"account_auths":[["1.2.5",1],["1.2.6",1],["1.2.7",1],["1.2.8",1],["1.2.9",1],["1.2.10",1],["1.2.11",1],["1.2.12",1],["1.2.13",1],["1.2.14",1]],"key_auths":[],"address_auths":[]},"options":{"memo_key":"GPH1111111111111111111111111111111114T1Anm","voting_account":"1.2.0","num_witness":0,"num_committee":0,"votes":[],"extensions":[]},"statistics":"2.7.0","whitelisting_accounts":[],"blacklisting_accounts":[]}]}
IMPORTANT (issue below command using block_signing_key just obtained)
```
get_private_key block_signing_key
```
Compare this result to
We can do the same thing using an HTTP client such as `curl` for API's which do not require login or other session state:
```
dump_private_keys
```
You should see 3 pairs of keys. One of the pairs should match your block_signing_key and this is the one you will use in the next step!
$ curl --data '{"jsonrpc": "2.0", "method": "call", "params": [0, "get_accounts", [["1.2.0"]]], "id": 1}' http://127.0.0.1:8090/rpc
{"id":1,"result":[{"id":"1.2.0","annotations":[],"membership_expiration_date":"1969-12-31T23:59:59","registrar":"1.2.0","referrer":"1.2.0","lifetime_referrer":"1.2.0","network_fee_percentage":2000,"lifetime_referrer_fee_percentage":8000,"referrer_rewards_percentage":0,"name":"committee-account","owner":{"weight_threshold":1,"account_auths":[],"key_auths":[],"address_auths":[]},"active":{"weight_threshold":6,"account_auths":[["1.2.5",1],["1.2.6",1],["1.2.7",1],["1.2.8",1],["1.2.9",1],["1.2.10",1],["1.2.11",1],["1.2.12",1],["1.2.13",1],["1.2.14",1]],"key_auths":[],"address_auths":[]},"options":{"memo_key":"GPH1111111111111111111111111111111114T1Anm","voting_account":"1.2.0","num_witness":0,"num_committee":0,"votes":[],"extensions":[]},"statistics":"2.7.0","whitelisting_accounts":[],"blacklisting_accounts":[]}]}
Get your witness id
-----------------
```
get_witness username (note the "id" for your config)
```
API 0 is accessible using regular JSON-RPC:
Modify your witness_node config.ini to include **your** witness id and private key pair.
-------------------------
Comment out the existing private-key before adding yours
```
vim witness_node_data_dir/config.ini
$ curl --data '{"jsonrpc": "2.0", "method": "get_accounts", "params": [["1.2.0"]], "id": 1}' http://127.0.0.1:8090/rpc
witness-id = "1.6.x"
private-key = ["block_signing_key","private_key_for_your_block_signing_key"]
```
Accessing restricted API's
--------------------------
start your witness back up
------------------
```
./programs/witness_node/witness_node
```
You can restrict API's to particular users by specifying an `apiaccess` file in `config.ini`. Here is an example `apiaccess` file which allows
user `bytemaster` with password `supersecret` to access four different API's, while allowing any other user to access the three public API's
necessary to use the wallet:
If it fails to start, try with these flags (not for permanent use)
{
"permission_map" :
[
[
"bytemaster",
{
"password_hash_b64" : "9e9GF7ooXVb9k4BoSfNIPTelXeGOZ5DrgOYMj94elaY=",
"password_salt_b64" : "INDdM6iCi/8=",
"allowed_apis" : ["database_api", "network_broadcast_api", "history_api", "network_node_api"]
}
],
[
"*",
{
"password_hash_b64" : "*",
"password_salt_b64" : "*",
"allowed_apis" : ["database_api", "network_broadcast_api", "history_api"]
}
]
]
}
```
./programs/witness_node/witness_node --resync --replay
```
Passwords are stored in `base64` as salted `sha256` hashes. A simple Python script, `saltpass.py` is avaliable to obtain hash and salt values from a password.
A single asterisk `"*"` may be specified as username or password hash to accept any value.
Vote for yourself
--------------
```
vote_for_witness your_witness_account your_witness_account true true
```
With the above configuration, here is an example of how to call `add_node` from the `network_node` API:
Ask to be voted in!
--------------
{"id":1, "method":"call", "params":[1,"login",["bytemaster", "supersecret"]]}
{"id":2, "method":"call", "params":[1,"network_node",[]]}
{"id":3, "method":"call", "params":[2,"add_node",["127.0.0.1:9090"]]}
Join @Peerplays Telegram group to find information about the witness group.
http://t.me/@peerplayswitness
Note, the call to `network_node` is necessary to obtain the correct API identifier for the network API. It is not guaranteed that the network API identifier will always be `2`.
You will get logs that look like this:
Since the `network_node` API requires login, it is only accessible over the websocket RPC. Our `doxygen` documentation contains the most up-to-date information
about API's for the [witness node](https://bitshares.github.io/doxygen/namespacegraphene_1_1app.html) and the
[wallet](https://bitshares.github.io/doxygen/classgraphene_1_1wallet_1_1wallet__api.html).
If you want information which is not available from an API, it might be available
from the [database](https://bitshares.github.io/doxygen/classgraphene_1_1chain_1_1database.html);
it is fairly simple to write API methods to expose database methods.
```
2070264ms th_a application.cpp:506 handle_block ] Got block: #87913 time: 2017-05-27T16:34:30 latency: 264 ms from: bhuz-witness irreversible: 87903 (-10)
2071000ms th_a witness.cpp:204 block_production_loo ] Not producing block because slot has not yet arrived
2072000ms th_a witness.cpp:204 block_production_loo ] Not producing block because slot has not yet arrived
2073000ms th_a witness.cpp:201 block_production_loo ] Not producing block because it isn't my turn
```
FAQ
---
Assuming you've received votes, you will start producing as a witness at the next maintenance interval (once per hour). You can check your votes with.
- Is there a way to generate help with parameter names and method descriptions?
```
get_witness your_witness_account
```
Yes. Documentation of the code base, including APIs, can be generated using Doxygen. Simply run `doxygen` in this directory.
systemd
----------------
It's important for your witness to start when your system boots up. The filepaths here assume that you installed your witness into `/home/ubuntu/peerplays`
If both Doxygen and perl are available in your build environment, the CLI wallet's `help` and `gethelp`
commands will display help generated from the doxygen documentation.
Create a logfile to hold your stdout/err logging
```bash
sudo touch /var/log/peerplays.log
```
If your CLI wallet's `help` command displays descriptions without parameter names like
`signed_transaction transfer(string, string, string, string, string, bool)`
it means CMake was unable to find Doxygen or perl during configuration. If found, the
output should look like this:
`signed_transaction transfer(string from, string to, string amount, string asset_symbol, string memo, bool broadcast)`
Save this file in your peerplays directory. `vi /home/ubuntu/peerplays/start.sh`
```bash
#!/bin/bash
- Is there a way to allow external program to drive `cli_wallet` via websocket, JSONRPC, or HTTP?
cd /home/ubuntu/peerplays
./programs/witness_node/witness_node &> /var/log/peerplays.log
```
Make it executable
```bash
chmod 744 /home/ubuntu/peerplays/start.sh
```
Create this file: `sudo vi /etc/systemd/system/peerplays.service`
Note the path for start.sh. Change it to match where your start.sh file is if necessary.
```
[Unit]
Description=Peerplays Witness
After=network.target
Yes. External programs may connect to the CLI wallet and make its calls over a websockets API. To do this, run the wallet in
server mode, i.e. `cli_wallet -s "127.0.0.1:9999"` and then have the external program connect to it over the specified port
(in this example, port 9999).
[Service]
ExecStart=/home/ubuntu/peerplays/start.sh
- Is there a way to access methods which require login over HTTP?
[Install]
WantedBy = multi-user.target
```
Enable the service
```bash
sudo systemctl enable peerplays.service
```
Make sure you don't get any errors
```bash
sudo systemctl status peerplays.service
```
Stop your witness if it is currently running from previous steps, then start it with the service.
```bash
sudo systemctl start peerplays.service
```
Check your logfile for entries
```bash
tail -f /var/log/peerplays.log
```
No. Login is inherently a stateful process (logging in changes what the server will do for certain requests, that's kind
of the point of having it). If you need to track state across HTTP RPC calls, you must maintain a session across multiple
connections. This is a famous source of security vulnerabilities for HTTP applications. Additionally, HTTP is not really
designed for "server push" notifications, and we would have to figure out a way to queue notifications for a polling client.
Websockets solves all these problems. If you need to access Graphene's stateful methods, you need to use Websockets.
Running specific tests
----------------------
- What is the meaning of `a.b.c` numbers?
The first number specifies the *space*. Space 1 is for protocol objects, 2 is for implementation objects.
Protocol space objects can appear on the wire, for example in the binary form of transactions.
Implementation space objects cannot appear on the wire and solely exist for implementation
purposes, such as optimization or internal bookkeeping.
The second number specifies the *type*. The type of the object determines what fields it has. For a
complete list of type ID's, see `enum object_type` and `enum impl_object_type` in
[types.hpp](https://github.com/bitshares/bitshares-2/blob/bitshares/libraries/chain/include/graphene/chain/protocol/types.hpp).
The third number specifies the *instance*. The instance of the object is different for each individual
object.
- The answer to the previous question was really confusing. Can you make it clearer?
All account ID's are of the form `1.2.x`. If you were the 9735th account to be registered,
your account's ID will be `1.2.9735`. Account `0` is special (it's the "committee account,"
which is controlled by the committee members and has a few abilities and restrictions other accounts
do not).
All asset ID's are of the form `1.3.x`. If you were the 29th asset to be registered,
your asset's ID will be `1.3.29`. Asset `0` is special (it's BTS, which is considered the "core asset").
The first and second number together identify the kind of thing you're talking about (`1.2` for accounts,
`1.3` for assets). The third number identifies the particular thing.
- How do I get the `network_add_nodes` command to work? Why is it so complicated?
You need to follow the instructions in the "Accessing restricted API's" section to
allow a username/password access to the `network_node` API. Then you need
to pass the username/password to the `cli_wallet` on the command line or in a config file.
It's set up this way so that the default configuration is secure even if the RPC port is
publicly accessible. It's fine if your `witness_node` allows the general public to query
the database or broadcast transactions (in fact, this is how the hosted web UI works). It's
less fine if your `witness_node` allows the general public to control which p2p nodes it's
connecting to. Therefore the API to add p2p connections needs to be set up with proper access
controls.
License
-------
BitShares Core is under the MIT license. See [LICENSE](https://github.com/bitshares/bitshares-core/blob/master/LICENSE.txt)
for more information.
- `tests/chain_tests -t block_tests/name_of_test`

View file

@ -1,161 +0,0 @@
### 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

View file

@ -1,165 +0,0 @@
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

2
docs

@ -1 +1 @@
Subproject commit bd792d02c70e7686da2b27197eba4fd6df30477c
Subproject commit 8d8b69d82482101279460fa02f814d0e4030966f

216097
genesis.json

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -160,10 +160,44 @@ namespace detail {
}
else
{
// https://bitsharestalk.org/index.php/topic,23715.0.html
// t.me/peerplays #seednodes
vector<string> seeds = {
"peerplays-dev.blocktrades.info:2776"
"seed.ppy.blckchnd.com:6112", // blckchnd
"ppy.esteem.ws:7777", // good-karma
"peerplays.bitcoiner.me:9777", // bitcoiner
"peerplays.roelandp.nl:9777", // roelandp
"ppyseed.bacchist.me:42420", // bacchist-witness
"5.9.18.213:18828", // pfunk
"31.171.244.121:7777", // taconator
"seed.peerplaysdb.com:9777", // jesta
"ppy-seed.xeldal.com:19777", // xeldal
"seed.ppy.altcap.io:61388", // winner.winner.chicken.dinner
"seed.peerplaysnodes.com:9777", // wackou
"peerplays-seed.privex.io:7777", // someguy123/privex
"51.15.78.16:9777", // agoric.systems
"212.71.253.163:9777", // xtar
"51.15.35.96:9777", // lafona
"anyx.ca:9777", // anyx
"82.223.108.91:7777", // hiltos-witness
"seed.ppy.nuevax.com:19777", // nuevax
"peerplays.butler.net:9777", // billbutler-witness
"peerplays.bitcoiner.me:9777", // bitcoiner
"ppyseed.bacchist.me:42420", // bacchist-witness
"peerplays.bhuz.info:9777", // bhuz
"node.peerblock.trade:9777", // bitcoinsig
"peerplays.crypto.fans:9777", // sc-steemit
"54.38.193.20:9777", // royal-flush
"ppy001.bts-nodes.net:7777", // baxters-sports-witness
"ppy002.bts-nodes.net:7777", // baxters-sports-witness
"ppy003.bts-nodes.net:7777", // baxters-sports-witness
"ppy004.bts-nodes.net:7777", // baxters-sports-witness
"ppy.proxyhosts.info:7777", // baxters-sports-witness
"ppyseed.spacemx.tech:9777", // spacecrypt-witness
"peerplaysblockchain.net:9777", // houdini-witness
"54.37.235.164:7777", // melea-trust
"peerplays-seed.lukestokes.info:7777" // lukestokes-witness
};
for( const string& endpoint_string : seeds )
{
try {
@ -512,8 +546,8 @@ namespace detail {
virtual bool handle_block(const graphene::net::block_message& blk_msg, bool sync_mode,
std::vector<fc::uint160_t>& contained_transaction_message_ids) override
{ try {
auto latency = 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);
@ -823,7 +857,7 @@ namespace detail {
if (high_block_num == 0)
return synopsis; // we have no blocks
}
if( low_block_num == 0)
low_block_num = 1;

View file

@ -68,6 +68,7 @@ void verify_account_votes( const database& db, const account_options& options )
"Voted for more witnesses than currently allowed (${c})", ("c", chain_params.maximum_witness_count) );
FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count,
"Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) );
FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." );
uint32_t max_vote_id = gpo.next_available_vote_id;
bool has_worker_votes = false;
@ -108,7 +109,6 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
FC_ASSERT( !op.extensions.value.buyback_options.valid() );
}
FC_ASSERT( d.find_object(op.options.voting_account), "Invalid proxy account specified." );
FC_ASSERT( fee_paying_account->is_lifetime_member(), "Only Lifetime members may register an account." );
FC_ASSERT( op.referrer(d).is_member(d.head_block_time()), "The referrer must be either a lifetime or annual subscriber." );

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*
@ -36,6 +36,7 @@ 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) ) }
@ -52,6 +53,7 @@ object_id_type betting_market_rules_create_evaluator::do_apply(const betting_mar
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");
@ -72,6 +74,7 @@ void_result betting_market_rules_update_evaluator::do_apply(const betting_market
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,
@ -119,6 +122,7 @@ object_id_type betting_market_group_create_evaluator::do_apply(const betting_mar
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);
@ -192,6 +196,7 @@ void_result betting_market_group_update_evaluator::do_apply(const betting_market
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,
@ -223,6 +228,7 @@ object_id_type betting_market_create_evaluator::do_apply(const betting_market_cr
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");
@ -261,7 +267,7 @@ void_result betting_market_update_evaluator::do_apply(const betting_market_updat
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);
@ -271,6 +277,14 @@ void_result bet_place_evaluator::do_evaluate(const bet_place_operation& op)
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 ) );
@ -278,15 +292,15 @@ void_result bet_place_evaluator::do_evaluate(const bet_place_operation& op)
_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,
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())
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;
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));
@ -294,10 +308,6 @@ void_result bet_place_evaluator::do_evaluate(const bet_place_operation& op)
FC_ASSERT(op.amount_to_bet.amount > share_type(), "Cannot place a bet with zero amount");
// do they have enough in their account to place the bet
FC_ASSERT( d.get_balance( *fee_paying_account, *_asset ).amount >= op.amount_to_bet.amount, "insufficient balance",
("balance", d.get_balance(*fee_paying_account, *_asset))("amount_to_bet", op.amount_to_bet.amount) );
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
@ -314,24 +324,31 @@ object_id_type bet_place_evaluator::do_apply(const bet_place_operation& op)
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_obj.end_of_delay = d.head_block_time() + _current_params->block_interval + _current_params->live_betting_delay_time();
}
});
bet_id_type new_bet_id = new_bet.id; // save the bet id here, new_bet may be deleted during place_bet()
d.adjust_balance(fee_paying_account->id, -op.amount_to_bet);
ddump((_betting_market_group->bets_are_delayed())(_current_params->live_betting_delay_time));
if (!_betting_market_group->bets_are_delayed() || _current_params->live_betting_delay_time <= 0)
// 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" );
@ -347,6 +364,7 @@ void_result bet_cancel_evaluator::do_apply(const bet_cancel_operation& 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();
@ -360,6 +378,7 @@ void_result betting_market_group_resolve_evaluator::do_apply(const betting_marke
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) ) }

View file

@ -1,3 +1,27 @@
/*
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#define DEFAULT_LOGGER "betting"
#include <graphene/chain/betting_market_object.hpp>
#include <graphene/chain/event_object.hpp>
@ -373,10 +397,13 @@ namespace {
// this is an approximate test, the state name provided by typeinfo will be mangled, but should
// at least contain the string we're looking for
const char* fc_reflected_value_name = fc::reflector<betting_market_group_state>::to_string((betting_market_group_state)i);
if (!strcmp(fc_reflected_value_name, filled_state_names[i]))
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&)
{
@ -386,7 +413,10 @@ namespace {
++error_count;
}
}
dlog("Done checking constants");
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;
}

View file

@ -1,3 +1,27 @@
/*
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#define DEFAULT_LOGGER "betting"
#include <graphene/chain/betting_market_object.hpp>
#include <graphene/chain/database.hpp>
@ -14,8 +38,8 @@ namespace graphene { namespace chain {
unresolved,
frozen,
closed,
canceled,
graded,
canceled,
settled
};
} }
@ -23,8 +47,8 @@ FC_REFLECT_ENUM(graphene::chain::betting_market_state,
(unresolved)
(frozen)
(closed)
(canceled)
(graded)
(canceled)
(settled))
@ -252,6 +276,7 @@ betting_market_object::betting_market_object(const 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;
@ -265,6 +290,7 @@ betting_market_object& betting_market_object::operator=(const betting_market_obj
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;
@ -293,10 +319,14 @@ namespace {
// this is an approximate test, the state name provided by typeinfo will be mangled, but should
// at least contain the string we're looking for
const char* fc_reflected_value_name = fc::reflector<betting_market_state>::to_string((betting_market_state)i);
if (!strcmp(fc_reflected_value_name, filled_state_names[i]))
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}",
"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&)
{
@ -306,7 +336,10 @@ namespace {
++error_count;
}
}
dlog("Done checking constants");
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;
}

View file

@ -25,6 +25,7 @@
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <graphene/chain/protocol/vote.hpp>
#include <graphene/chain/transaction_evaluation_state.hpp>
@ -77,12 +78,28 @@ void_result committee_member_update_global_parameters_evaluator::do_evaluate(con
{ try {
FC_ASSERT(trx_state->_is_proposed_trx);
if( db().head_block_time() < HARDFORK_1000_TIME ) // TODO: remove after hf
FC_ASSERT( !o.new_parameters.extensions.value.min_bet_multiplier.valid()
&& !o.new_parameters.extensions.value.max_bet_multiplier.valid()
&& !o.new_parameters.extensions.value.betting_rake_fee_percentage.valid()
&& !o.new_parameters.extensions.value.permitted_betting_odds_increments.valid()
&& !o.new_parameters.extensions.value.live_betting_delay_time.valid(),
"Parameter extensions are not allowed yet!" );
dgpo = &db().get_global_properties();
if( o.new_parameters.extensions.value.min_bet_multiplier.valid()
&& !o.new_parameters.extensions.value.max_bet_multiplier.valid() )
FC_ASSERT( *o.new_parameters.extensions.value.min_bet_multiplier < dgpo->parameters.max_bet_multiplier() );
if( !o.new_parameters.extensions.value.min_bet_multiplier.valid()
&& o.new_parameters.extensions.value.max_bet_multiplier.valid() )
FC_ASSERT( dgpo->parameters.min_bet_multiplier() < *o.new_parameters.extensions.value.max_bet_multiplier );
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result committee_member_update_global_parameters_evaluator::do_apply(const committee_member_update_global_parameters_operation& o)
{ try {
db().modify(db().get_global_properties(), [&o](global_property_object& p) {
db().modify(*dgpo, [&o](global_property_object& p) {
p.pending_parameters = o.new_parameters;
});

View file

@ -1,4 +1,27 @@
#define DEFAULT_LOGGER "betting"
/*
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
@ -7,6 +30,7 @@
#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 {
@ -20,7 +44,7 @@ void database::cancel_bet( const bet_object& bet, bool create_virtual_op )
{
bet_canceled_operation bet_canceled_virtual_op(bet.bettor_id, bet.id,
bet.amount_to_bet);
//idump((bet_canceled_virtual_op));
//fc_idump(fc::logger::get("betting"), (bet_canceled_virtual_op));
push_applied_operation(std::move(bet_canceled_virtual_op));
}
remove(bet);
@ -132,7 +156,7 @@ void database::resolve_betting_market_group(const betting_market_group_object& b
void database::settle_betting_market_group(const betting_market_group_object& betting_market_group)
{
ilog("Settling betting market group ${id}", ("id", betting_market_group.id));
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;
@ -179,7 +203,7 @@ void database::settle_betting_market_group(const betting_market_group_object& be
// 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;
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;
@ -244,7 +268,7 @@ void database::settle_betting_market_group(const betting_market_group_object& be
// pay winning - rake
adjust_balance(bettor_id, asset(payout_amounts - rake_amount, betting_market_group.asset_id));
// [ROL]
//idump((payout_amounts)(net_profits.value)(rake_amount.value));
//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,
@ -268,23 +292,45 @@ void database::settle_betting_market_group(const betting_market_group_object& be
const betting_market_object& betting_market = *betting_market_itr;
++betting_market_itr;
dlog("removing betting market ${id}", ("id", betting_market.id));
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);
dlog("removing betting market group ${id}", ("id", betting_market_group.id));
fc_dlog(fc::logger::get("betting"), "removing betting market group ${id}", ("id", betting_market_group.id));
remove(betting_market_group);
}
if (event.get_status() == event_status::canceled ||
event.get_status() == event_status::settled) {
dlog("removing event ${id}", ("id", event.id));
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)
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);
@ -298,8 +344,8 @@ share_type adjust_betting_position(database& db, account_id_type bettor_id, bett
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.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;
@ -308,8 +354,8 @@ share_type adjust_betting_position(database& db, account_id_type bettor_id, bett
});
} else {
db.modify(*itr, [&](betting_market_position_object& position) {
assert(position.bettor_id == bettor_id);
assert(position.betting_market_id == betting_market_id);
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;
@ -339,7 +385,7 @@ bool bet_was_matched(database& db, const bet_object& bet,
asset_amount_bet,
actual_multiplier,
guaranteed_winnings_returned);
//edump((bet_matched_virtual_op));
//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
@ -376,13 +422,15 @@ bool bet_was_matched(database& db, const bet_object& bet,
*/
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 == bet_type::back ? taker_bet.backer_multiplier <= maker_bet.backer_multiplier :
taker_bet.backer_multiplier >= maker_bet.backer_multiplier);
assert(taker_bet.back_or_lay != maker_bet.back_or_lay);
int result = 0;
//idump((taker_bet)(maker_bet));
//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)
@ -393,17 +441,33 @@ int match_bet(database& db, const bet_object& taker_bet, const bet_object& maker
// and make some shortcuts to get to the maker's and taker's side of the ratio
const share_type& maker_odds_ratio = maker_bet.back_or_lay == bet_type::back ? back_odds_ratio : lay_odds_ratio;
const share_type& taker_odds_ratio = maker_bet.back_or_lay == bet_type::back ? lay_odds_ratio : back_odds_ratio;
//idump((back_odds_ratio)(lay_odds_ratio));
//idump((maker_odds_ratio)(taker_odds_ratio));
// 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_taker_factor = taker_bet.amount_to_bet.amount / taker_odds_ratio;
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;
//idump((maker_amount_to_match)(taker_amount_to_match));
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);
@ -414,20 +478,84 @@ int match_bet(database& db, const bet_object& taker_bet, const bet_object& maker
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);
// verify we're getting the odds we expect
fc::uint128_t payout_128 = maker_amount_to_match.value;
payout_128 += taker_amount_to_match.value;
payout_128 *= GRAPHENE_BETTING_ODDS_PRECISION;
payout_128 /= maker_bet.back_or_lay == bet_type::back ? maker_amount_to_match.value : taker_amount_to_match.value;
assert(payout_128.to_uint64() == maker_bet.backer_multiplier);
}
#endif
//idump((taker_amount_to_match)(maker_amount_to_match));
//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);
@ -441,17 +569,50 @@ int match_bet(database& db, const bet_object& taker_bet, const bet_object& maker
// 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));
// ilog("");
// ilog("------------ order book ------------------");
// fc_ilog(fc::logger::get("betting"), "");
// fc_ilog(fc::logger::get("betting"), "------------ order book ------------------");
// for (auto itr = book_itr; itr != book_end; ++itr)
// idump((*itr));
// ilog("------------ order book ------------------");
// fc_idump(fc::logger::get("betting"), (*itr));
// fc_ilog(fc::logger::get("betting"), "------------ order book ------------------");
int orders_matched_flags = 0;
bool finished = false;
@ -465,45 +626,12 @@ bool database::place_bet(const bet_object& new_bet_object)
// we continue if the maker bet was completely consumed AND the taker bet was not
finished = orders_matched_flags != 2;
}
if (!(orders_matched_flags & 1))
{
// if the new (taker) bet was not completely consumed, we need to put whatever remains
// of it on the books. But we only allow bets that can be exactly matched
// on the books, so round the amount down if necessary
share_type minimum_matchable_amount = new_bet_object.get_minimum_matchable_amount();
share_type scale_factor = new_bet_object.amount_to_bet.amount / minimum_matchable_amount;
share_type rounded_bet_amount = scale_factor * minimum_matchable_amount;
//idump((new_bet_object.amount_to_bet.amount)(rounded_bet_amount)(minimum_matchable_amount)(scale_factor));
fc_ddump(fc::logger::get("betting"), (new_bet_object));
if (rounded_bet_amount == share_type())
{
// the remainder of the bet was too small to match, cancel the bet
cancel_bet(new_bet_object, true);
return true;
}
else if (rounded_bet_amount != new_bet_object.amount_to_bet.amount)
{
asset stake_returned = new_bet_object.amount_to_bet;
stake_returned.amount -= rounded_bet_amount;
modify(new_bet_object, [&rounded_bet_amount](bet_object& modified_bet_object) {
modified_bet_object.amount_to_bet.amount = rounded_bet_amount;
});
adjust_balance(new_bet_object.bettor_id, stake_returned);
// TODO: update global statistics
bet_adjusted_operation bet_adjusted_op(new_bet_object.bettor_id, new_bet_object.id,
stake_returned);
// idump((bet_adjusted_op)(new_bet_object));
push_applied_operation(std::move(bet_adjusted_op));
return false;
}
else
return false;
}
else
return true;
// return true if the taker bet was completely consumed
return (orders_matched_flags & 1) != 0;
}
} }

View file

@ -395,7 +395,6 @@ void database::init_genesis(const genesis_state_type& genesis_state)
}).get_id() == GRAPHENE_PROXY_TO_SELF_ACCOUNT);
FC_ASSERT(create<account_object>([this](account_object& a) {
a.name = "default-dividend-distribution";
//a.name = "test-dividend-distribution";
a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id;
a.owner.weight_threshold = 1;
a.active.weight_threshold = 1;
@ -404,7 +403,6 @@ void database::init_genesis(const genesis_state_type& genesis_state)
a.network_fee_percentage = 0;
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT;
}).get_id() == GRAPHENE_RAKE_FEE_ACCOUNT_ID);
//}).get_id() == TOURNAMENT_RAKE_FEE_ACCOUNT_ID);
// Create more special accounts
while( true )
{
@ -435,12 +433,9 @@ 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::hours(1);
a.options.payout_interval = 7*24*60*60;
a.options.next_payout_time = genesis_state.initial_timestamp + fc::days(1);
a.options.payout_interval = 30*24*60*60;
a.dividend_distribution_account = GRAPHENE_RAKE_FEE_ACCOUNT_ID;
//a.options.next_payout_time = genesis_state.initial_timestamp + fc::days(1);
//a.options.payout_interval = 30*24*60*60;
//a.dividend_distribution_account = TOURNAMENT_RAKE_FEE_ACCOUNT_ID;
});
const asset_object& core_asset =
@ -474,20 +469,17 @@ void database::init_genesis(const genesis_state_type& genesis_state)
a.options.next_payout_time = genesis_state.initial_timestamp + fc::hours(1);
a.options.payout_interval = 7*24*60*60;
a.dividend_distribution_account = GRAPHENE_RAKE_FEE_ACCOUNT_ID;
//a.dividend_distribution_account = TOURNAMENT_RAKE_FEE_ACCOUNT_ID;
});
const asset_object& default_asset =
create<asset_object>( [&]( asset_object& a ) {
a.symbol = "DEF";
//a.symbol = "DEFAULT";
a.options.max_market_fee =
a.options.max_supply = genesis_state.max_core_supply;
a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS;
a.options.flags = 0;
a.options.issuer_permissions = 79;
a.issuer = GRAPHENE_RAKE_FEE_ACCOUNT_ID;
//a.issuer = TOURNAMENT_RAKE_FEE_ACCOUNT_ID;
a.options.core_exchange_rate.base.amount = 1;
a.options.core_exchange_rate.base.asset_id = asset_id_type(0);
a.options.core_exchange_rate.quote.amount = 1;
@ -831,7 +823,10 @@ void database::init_genesis(const genesis_state_type& genesis_state)
}
++it;
}
// @romek
#if 0
FC_ASSERT( !has_imbalanced_assets );
#endif
// Save tallied supplies
for( const auto& item : total_supplies )

View file

@ -228,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 );
}
} );
@ -310,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 );
}
} );
@ -737,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 =
@ -770,9 +770,9 @@ void schedule_pending_dividend_balances(database& db,
for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end))
{
vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount;
dlog("Vesting balance for account: ${owner}, amount: ${amount}",
("owner", vesting_balance_obj.owner(db).name)
("amount", vesting_balance_obj.balance.amount));
//dlog("Vesting balance for account: ${owner}, amount: ${amount}",
// ("owner", vesting_balance_obj.owner(db).name)
// ("amount", vesting_balance_obj.balance.amount));
}
#else
// get only once a collection of accounts that hold nonzero vesting balances of the dividend asset
@ -1059,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>();
@ -1084,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);
@ -1250,9 +1253,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end))
{
vesting_amounts[vesting_balance_obj.owner] += vesting_balance_obj.balance.amount;
dlog("Vesting balance for account: ${owner}, amount: ${amount}",
("owner", vesting_balance_obj.owner(d).name)
("amount", vesting_balance_obj.balance.amount));
//dlog("Vesting balance for account: ${owner}, amount: ${amount}",
// ("owner", vesting_balance_obj.owner(d).name)
// ("amount", vesting_balance_obj.balance.amount));
}
#else
const auto& vesting_balances = vesting_index.indices().get<by_id>();
@ -1275,10 +1278,15 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
// There may be a difference between the account whose stake is voting and the one specifying opinions.
// Usually they're the same, but if the stake account has specified a voting_account, that account is the one
// specifying the opinions.
const account_object& opinion_account =
const account_object* opinion_account_ptr =
(stake_account.options.voting_account ==
GRAPHENE_PROXY_TO_SELF_ACCOUNT)? stake_account
: d.get(stake_account.options.voting_account);
GRAPHENE_PROXY_TO_SELF_ACCOUNT)? &stake_account
: d.find(stake_account.options.voting_account);
if( !opinion_account_ptr ) // skip non-exist account
return;
const account_object& opinion_account = *opinion_account_ptr;
const auto& stats = stake_account.statistics(d);
uint64_t voting_stake = stats.total_core_in_orders.value
@ -1363,6 +1371,16 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
if( p.pending_parameters )
{
if( !p.pending_parameters->extensions.value.min_bet_multiplier.valid() )
p.pending_parameters->extensions.value.min_bet_multiplier = p.parameters.extensions.value.min_bet_multiplier;
if( !p.pending_parameters->extensions.value.max_bet_multiplier.valid() )
p.pending_parameters->extensions.value.max_bet_multiplier = p.parameters.extensions.value.max_bet_multiplier;
if( !p.pending_parameters->extensions.value.betting_rake_fee_percentage.valid() )
p.pending_parameters->extensions.value.betting_rake_fee_percentage = p.parameters.extensions.value.betting_rake_fee_percentage;
if( !p.pending_parameters->extensions.value.permitted_betting_odds_increments.valid() )
p.pending_parameters->extensions.value.permitted_betting_odds_increments = p.parameters.extensions.value.permitted_betting_odds_increments;
if( !p.pending_parameters->extensions.value.live_betting_delay_time.valid() )
p.pending_parameters->extensions.value.live_betting_delay_time = p.parameters.extensions.value.live_betting_delay_time;
p.parameters = std::move(*p.pending_parameters);
p.pending_parameters.reset();
}
@ -1416,6 +1434,6 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
// process_budget needs to run at the bottom because
// it needs to know the next_maintenance_time
process_budget();
}
} FC_CAPTURE_AND_RETHROW() }
} }

View file

@ -1,3 +1,27 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <fc/container/flat.hpp>
#include <graphene/chain/protocol/authority.hpp>

View file

@ -677,6 +677,7 @@ void process_settled_betting_markets(database& db, fc::time_point_sec current_bl
void database::update_betting_markets(fc::time_point_sec current_block_time)
{
process_settled_betting_markets(*this, current_block_time);
remove_completed_events();
}
} }

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*
@ -34,6 +34,7 @@ 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();
@ -72,6 +73,7 @@ object_id_type event_create_evaluator::do_apply(const event_create_operation& 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");
@ -115,6 +117,7 @@ void_result event_update_status_evaluator::do_evaluate(const event_update_status
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);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*
@ -33,6 +33,7 @@ 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,
@ -62,6 +63,7 @@ object_id_type event_group_create_evaluator::do_apply(const event_group_create_o
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() )

View file

@ -1,3 +1,27 @@
/*
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#define DEFAULT_LOGGER "betting"
#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
#define BOOST_MPL_LIMIT_VECTOR_SIZE 30
@ -392,10 +416,13 @@ namespace graphene { namespace chain {
// this is an approximate test, the state name provided by typeinfo will be mangled, but should
// at least contain the string we're looking for
const char* fc_reflected_value_name = fc::reflector<event_state>::to_string((event_state)i);
if (!strcmp(fc_reflected_value_name, filled_state_names[i]))
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&)
{
@ -405,7 +432,10 @@ namespace graphene { namespace chain {
++error_count;
}
}
dlog("Done checking constants");
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;
}

View 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( 1577750400 )) // 2019-12-31
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -50,6 +50,8 @@ namespace graphene { namespace chain {
public:
typedef committee_member_update_global_parameters_operation operation_type;
const global_property_object* dgpo;
void_result do_evaluate( const committee_member_update_global_parameters_operation& o );
void_result do_apply( const committee_member_update_global_parameters_operation& o );
};

View file

@ -151,7 +151,7 @@
#define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4
#define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3
#define GRAPHENE_CURRENT_DB_VERSION "BTS2.9"
#define GRAPHENE_CURRENT_DB_VERSION "PPY1.11"
#define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT)
@ -173,7 +173,6 @@
#define GRAPHENE_PROXY_TO_SELF_ACCOUNT (graphene::chain::account_id_type(5))
///
#define GRAPHENE_RAKE_FEE_ACCOUNT_ID (graphene::chain::account_id_type(6))
#define TOURNAMENT_RAKE_FEE_ACCOUNT_ID (graphene::chain::account_id_type(6))
/// Sentinel value used in the scheduler.
#define GRAPHENE_NULL_WITNESS (graphene::chain::witness_id_type(0))
///@}

View file

@ -396,6 +396,7 @@ namespace graphene { namespace chain {
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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*
@ -95,11 +95,13 @@ class event_object : public graphene::db::abstract_object< event_object >
};
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 > > > > event_object_multi_index_type;
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;

View file

@ -1,3 +1,27 @@
/*
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/chain/match_object.hpp>
#include <graphene/chain/rock_paper_scissors.hpp>

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -23,20 +23,22 @@
*/
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/ext.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <fc/smart_ref_fwd.hpp>
namespace graphene { namespace chain { struct fee_schedule; } }
/*
namespace fc {
template<typename Stream, typename T> inline void pack( Stream& s, const graphene::chain::fee_schedule& value );
template<typename Stream, typename T> inline void unpack( Stream& s, graphene::chain::fee_schedule& value );
} // namespace fc
*/
namespace graphene { namespace chain {
struct parameter_extension
{
optional< bet_multiplier_type > min_bet_multiplier;
optional< bet_multiplier_type > max_bet_multiplier;
optional< uint16_t > betting_rake_fee_percentage;
optional< flat_map<bet_multiplier_type, bet_multiplier_type> > permitted_betting_odds_increments;
optional< uint16_t > live_betting_delay_time;
};
typedef static_variant<> parameter_extension;
struct chain_parameters
{
/** using a smart ref breaks the circular dependency created between operations and the fee schedule */
@ -69,12 +71,6 @@ namespace graphene { namespace chain {
uint16_t accounts_per_fee_scale = GRAPHENE_DEFAULT_ACCOUNTS_PER_FEE_SCALE; ///< number of accounts between fee scalings
uint8_t account_fee_scale_bitshifts = GRAPHENE_DEFAULT_ACCOUNT_FEE_SCALE_BITSHIFTS; ///< number of times to left bitshift account registration fee at each scaling
uint8_t max_authority_depth = GRAPHENE_MAX_SIG_CHECK_DEPTH;
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
@ -90,14 +86,38 @@ namespace graphene { namespace chain {
uint32_t maximum_tournament_start_time_in_future = TOURNAMENT_MAX_START_TIME_IN_FUTURE;
uint32_t maximum_tournament_start_delay = TOURNAMENT_MAX_START_DELAY;
uint16_t maximum_tournament_number_of_wins = TOURNAMENT_MAX_NUMBER_OF_WINS;
extensions_type extensions;
extension<parameter_extension> extensions;
/** defined in fee_schedule.cpp */
void validate()const;
inline bet_multiplier_type min_bet_multiplier()const {
return extensions.value.min_bet_multiplier.valid() ? *extensions.value.min_bet_multiplier : GRAPHENE_DEFAULT_MIN_BET_MULTIPLIER;
}
inline bet_multiplier_type max_bet_multiplier()const {
return extensions.value.max_bet_multiplier.valid() ? *extensions.value.max_bet_multiplier : GRAPHENE_DEFAULT_MAX_BET_MULTIPLIER;
}
inline uint16_t betting_rake_fee_percentage()const {
return extensions.value.betting_rake_fee_percentage.valid() ? *extensions.value.betting_rake_fee_percentage : GRAPHENE_DEFAULT_RAKE_FEE_PERCENTAGE;
}
inline const flat_map<bet_multiplier_type, bet_multiplier_type>& permitted_betting_odds_increments()const {
static const flat_map<bet_multiplier_type, bet_multiplier_type> _default = GRAPHENE_DEFAULT_PERMITTED_BETTING_ODDS_INCREMENTS;
return extensions.value.permitted_betting_odds_increments.valid() ? *extensions.value.permitted_betting_odds_increments : _default;
}
inline uint16_t live_betting_delay_time()const {
return extensions.value.live_betting_delay_time.valid() ? *extensions.value.live_betting_delay_time : GRAPHENE_DEFAULT_LIVE_BETTING_DELAY_TIME;
}
};
} } // graphene::chain
FC_REFLECT( graphene::chain::parameter_extension,
(min_bet_multiplier)
(max_bet_multiplier)
(betting_rake_fee_percentage)
(permitted_betting_odds_increments)
(live_betting_delay_time)
)
FC_REFLECT( graphene::chain::chain_parameters,
(current_fees)
(block_interval)
@ -127,12 +147,7 @@ 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)

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -97,8 +97,13 @@ namespace graphene { namespace chain {
asset_settle_cancel_operation, // VIRTUAL
asset_claim_fees_operation,
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,
sport_create_operation,
sport_update_operation,
event_group_create_operation,
@ -117,11 +122,6 @@ namespace graphene { namespace chain {
bet_matched_operation, // VIRTUAL
bet_cancel_operation,
bet_canceled_operation, // VIRTUAL
tournament_create_operation,
tournament_join_operation,
game_move_operation,
tournament_payout_operation, // VIRTUAL
tournament_leave_operation,
betting_market_group_update_operation,
betting_market_update_operation,
event_update_status_operation

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -134,6 +134,10 @@ namespace graphene { namespace chain {
vesting_balance_object_type,
worker_object_type,
balance_object_type,
tournament_object_type,
tournament_details_object_type,
match_object_type,
game_object_type,
sport_object_type,
event_group_object_type,
event_object_type,
@ -141,10 +145,6 @@ namespace graphene { namespace chain {
betting_market_group_object_type,
betting_market_object_type,
bet_object_type,
tournament_object_type,
tournament_details_object_type,
match_object_type,
game_object_type,
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
};
@ -167,11 +167,11 @@ 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
impl_distributed_dividend_balance_data_type,
impl_betting_market_position_object_type,
impl_global_betting_statistics_object_type
};
//typedef fc::unsigned_int object_id_type;
@ -191,6 +191,10 @@ namespace graphene { namespace chain {
class worker_object;
class balance_object;
class blinded_balance_object;
class tournament_object;
class tournament_details_object;
class match_object;
class game_object;
class sport_object;
class event_group_object;
class event_object;
@ -198,10 +202,6 @@ namespace graphene { namespace chain {
class betting_market_group_object;
class betting_market_object;
class bet_object;
class tournament_object;
class tournament_details_object;
class match_object;
class game_object;
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
@ -217,6 +217,10 @@ 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, tournament_object_type, tournament_object> tournament_id_type;
typedef object_id< protocol_ids, tournament_details_object_type, tournament_details_object> tournament_details_id_type;
typedef object_id< protocol_ids, match_object_type, match_object> match_id_type;
typedef object_id< protocol_ids, game_object_type, game_object> game_id_type;
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;
@ -224,10 +228,6 @@ namespace graphene { namespace chain {
typedef object_id< protocol_ids, betting_market_group_object_type, betting_market_group_object> betting_market_group_id_type;
typedef object_id< protocol_ids, betting_market_object_type, betting_market_object> betting_market_id_type;
typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type;
typedef object_id< protocol_ids, tournament_object_type, tournament_object> tournament_id_type;
typedef object_id< protocol_ids, tournament_details_object_type, tournament_details_object> tournament_details_id_type;
typedef object_id< protocol_ids, match_object_type, match_object> match_id_type;
typedef object_id< protocol_ids, game_object_type, game_object> game_id_type;
// implementation types
class global_property_object;
@ -245,11 +245,10 @@ 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;
class betting_market_position_object;
class global_betting_statistics_object;
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
@ -392,6 +391,10 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
(vesting_balance_object_type)
(worker_object_type)
(balance_object_type)
(tournament_object_type)
(tournament_details_object_type)
(match_object_type)
(game_object_type)
(sport_object_type)
(event_group_object_type)
(event_object_type)
@ -399,10 +402,6 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
(betting_market_group_object_type)
(betting_market_object_type)
(bet_object_type)
(tournament_object_type)
(tournament_details_object_type)
(match_object_type)
(game_object_type)
(OBJECT_TYPE_COUNT)
)
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
@ -423,11 +422,11 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
(impl_special_authority_object_type)
(impl_buyback_object_type)
(impl_fba_accumulator_object_type)
(impl_betting_market_position_object_type)
(impl_global_betting_statistics_object_type)
(impl_asset_dividend_data_type)
(impl_pending_dividend_payout_balance_for_holder_object_type)
(impl_distributed_dividend_balance_data_type)
(impl_betting_market_position_object_type)
(impl_global_betting_statistics_object_type)
)
FC_REFLECT_TYPENAME( graphene::chain::share_type )

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -181,7 +181,6 @@ namespace graphene { namespace chain {
ordered_non_unique< tag<by_account>,
member<vesting_balance_object, account_id_type, &vesting_balance_object::owner>
>,
//ordered_unique< tag<by_asset_balance>,
ordered_non_unique< tag<by_asset_balance>,
composite_key<
vesting_balance_object,

View file

@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/proposal_evaluator.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/account_object.hpp>
@ -31,9 +32,108 @@
namespace graphene { namespace chain {
struct proposal_operation_hardfork_visitor
{
typedef void result_type;
const fc::time_point_sec block_time;
proposal_operation_hardfork_visitor( const fc::time_point_sec bt ) : block_time(bt) {}
template<typename T>
void operator()(const T &v) const {}
void operator()(const committee_member_update_global_parameters_operation &op) const {
if( block_time < HARDFORK_1000_TIME ) // TODO: remove after hf
FC_ASSERT( !op.new_parameters.extensions.value.min_bet_multiplier.valid()
&& !op.new_parameters.extensions.value.max_bet_multiplier.valid()
&& !op.new_parameters.extensions.value.betting_rake_fee_percentage.valid()
&& !op.new_parameters.extensions.value.permitted_betting_odds_increments.valid()
&& !op.new_parameters.extensions.value.live_betting_delay_time.valid(),
"Parameter extensions are not allowed yet!" );
}
void operator()(const sport_create_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "sport_create_operation not allowed yet!" );
}
void operator()(const sport_update_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "sport_update_operation not allowed yet!" );
}
void operator()(const event_group_create_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_group_create_operation not allowed yet!" );
}
void operator()(const event_group_update_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_group_update_operation not allowed yet!" );
}
void operator()(const event_create_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_create_operation not allowed yet!" );
}
void operator()(const event_update_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_update_operation not allowed yet!" );
}
void operator()(const betting_market_rules_create_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_rules_create_operation not allowed yet!" );
}
void operator()(const betting_market_rules_update_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_rules_update_operation not allowed yet!" );
}
void operator()(const betting_market_group_create_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_group_create_operation not allowed yet!" );
}
void operator()(const betting_market_create_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_create_operation not allowed yet!" );
}
void operator()(const bet_place_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "bet_place_operation not allowed yet!" );
}
void operator()(const betting_market_group_resolve_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_group_resolve_operation not allowed yet!" );
}
void operator()(const betting_market_group_cancel_unmatched_bets_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_group_cancel_unmatched_bets_operation not allowed yet!" );
}
void operator()(const bet_cancel_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_group_resolve_operation not allowed yet!" );
}
void operator()(const betting_market_group_update_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_group_update_operation not allowed yet!" );
}
void operator()(const betting_market_update_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "betting_market_update_operation not allowed yet!" );
}
void operator()(const event_update_status_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_1000_TIME, "event_update_status_operation not allowed yet!" );
}
// loop and self visit in proposals
void operator()(const proposal_create_operation &v) const {
for (const op_wrapper &op : v.proposed_ops)
op.op.visit(*this);
}
};
void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o)
{ try {
const database& d = db();
proposal_operation_hardfork_visitor vtor( d.head_block_time() );
vtor( o );
const auto& global_parameters = d.get_global_properties().parameters;
FC_ASSERT( o.expiration_time > d.head_block_time(), "Proposal has already expired on creation." );

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -189,16 +189,22 @@ namespace graphene { namespace chain {
FC_ASSERT( maximum_proposal_lifetime - committee_proposal_review_period > block_interval,
"Committee proposal review period must be less than the maximum proposal lifetime" );
FC_ASSERT( 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,
"Rake fee percentage must not be greater than ${max}", ("max", TOURNAMENT_MAXIMAL_RAKE_FEE_PERCENTAGE));
if( extensions.value.min_bet_multiplier.valid() )
FC_ASSERT( *extensions.value.min_bet_multiplier >= GRAPHENE_BETTING_MIN_MULTIPLIER &&
*extensions.value.min_bet_multiplier <= GRAPHENE_BETTING_MAX_MULTIPLIER );
if( extensions.value.max_bet_multiplier.valid() )
FC_ASSERT( *extensions.value.max_bet_multiplier >= GRAPHENE_BETTING_MIN_MULTIPLIER &&
*extensions.value.max_bet_multiplier <= GRAPHENE_BETTING_MAX_MULTIPLIER );
if( extensions.value.min_bet_multiplier.valid() && extensions.value.max_bet_multiplier.valid() )
FC_ASSERT( *extensions.value.min_bet_multiplier < *extensions.value.max_bet_multiplier );
if( extensions.value.betting_rake_fee_percentage.valid() )
{
FC_ASSERT( *extensions.value.betting_rake_fee_percentage >= TOURNAMENT_MINIMAL_RAKE_FEE_PERCENTAGE,
"Rake fee percentage must not be less than ${min}", ("min",TOURNAMENT_MINIMAL_RAKE_FEE_PERCENTAGE));
FC_ASSERT( *extensions.value.betting_rake_fee_percentage <= TOURNAMENT_MAXIMAL_RAKE_FEE_PERCENTAGE,
"Rake fee percentage must not be greater than ${max}", ("max", TOURNAMENT_MAXIMAL_RAKE_FEE_PERCENTAGE));
}
}
} } // graphene::chain

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -33,6 +33,7 @@ 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();
@ -50,6 +51,7 @@ object_id_type sport_create_evaluator::do_apply(const sport_create_operation& 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();

View file

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

@ -1 +1 @@
Subproject commit 8df97c6b3543c6d555377ceddc884cf2011c68e4
Subproject commit c8c05254b1285fdfb1ea345da8342e4575426995

View file

@ -7,3 +7,4 @@ add_subdirectory( bookie )
add_subdirectory( generate_genesis )
add_subdirectory( generate_uia_sharedrop_genesis )
add_subdirectory( debug_witness )
add_subdirectory( snapshot )

View file

@ -1,3 +1,26 @@
/*
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <fc/filesystem.hpp>
#include <fc/optional.hpp>
#include <fc/variant_object.hpp>
@ -61,8 +84,6 @@ binned_order_book bookie_api_impl::get_binned_order_book(graphene::chain::bettin
binned_order_book result;
// use a bet_object here for convenience. we really only use it to track the amount, odds, and back_or_lay
// note, the current bin is accumulating the matching bets, so it will be of type 'lay' when we're
// binning 'back' bets, and vice versa
fc::optional<bet_object> current_bin;
auto flush_current_bin = [&current_bin, &result]()
@ -72,9 +93,9 @@ binned_order_book bookie_api_impl::get_binned_order_book(graphene::chain::bettin
order_bin current_order_bin;
current_order_bin.backer_multiplier = current_bin->backer_multiplier;
current_order_bin.amount_to_bet = current_bin->get_approximate_matching_amount(true /* round up */);
current_order_bin.amount_to_bet = current_bin->amount_to_bet.amount;
//idump((*current_bin)(current_order_bin));
if (current_bin->back_or_lay == bet_type::lay)
if (current_bin->back_or_lay == bet_type::back)
result.aggregated_back_bets.emplace_back(std::move(current_order_bin));
else // current_bin is aggregating back positions
result.aggregated_lay_bets.emplace_back(std::move(current_order_bin));
@ -89,7 +110,7 @@ binned_order_book bookie_api_impl::get_binned_order_book(graphene::chain::bettin
++bet_odds_iter)
{
if (current_bin &&
(bet_odds_iter->back_or_lay == current_bin->back_or_lay /* we have switched from back to lay bets */ ||
(bet_odds_iter->back_or_lay != current_bin->back_or_lay /* we have switched from back to lay bets */ ||
(bet_odds_iter->back_or_lay == bet_type::back ? bet_odds_iter->backer_multiplier > current_bin->backer_multiplier :
bet_odds_iter->backer_multiplier < current_bin->backer_multiplier)))
flush_current_bin();
@ -104,20 +125,20 @@ binned_order_book bookie_api_impl::get_binned_order_book(graphene::chain::bettin
if (bet_odds_iter->back_or_lay == bet_type::back)
{
current_bin->backer_multiplier = (bet_odds_iter->backer_multiplier + bin_size - 1) / bin_size * bin_size;
current_bin->backer_multiplier = std::min<graphene::chain::bet_multiplier_type>(current_bin->backer_multiplier, current_params.max_bet_multiplier);
current_bin->back_or_lay = bet_type::lay;
current_bin->backer_multiplier = std::min<graphene::chain::bet_multiplier_type>(current_bin->backer_multiplier, current_params.max_bet_multiplier());
current_bin->back_or_lay = bet_type::back;
}
else
{
current_bin->backer_multiplier = bet_odds_iter->backer_multiplier / bin_size * bin_size;
current_bin->backer_multiplier = std::max<graphene::chain::bet_multiplier_type>(current_bin->backer_multiplier, current_params.min_bet_multiplier);
current_bin->back_or_lay = bet_type::back;
current_bin->backer_multiplier = std::max<graphene::chain::bet_multiplier_type>(current_bin->backer_multiplier, current_params.min_bet_multiplier());
current_bin->back_or_lay = bet_type::lay;
}
current_bin->amount_to_bet.amount = 0;
}
current_bin->amount_to_bet.amount += bet_odds_iter->get_exact_matching_amount();
current_bin->amount_to_bet.amount += bet_odds_iter->amount_to_bet.amount;
}
if (current_bin)
flush_current_bin();

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*
@ -21,7 +21,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/bookie/bookie_plugin.hpp>
#include <graphene/bookie/bookie_objects.hpp>

View file

@ -1,3 +1,26 @@
/*
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <memory>

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*

View file

@ -0,0 +1,17 @@
file(GLOB HEADERS "include/graphene/snapshot/*.hpp")
add_library( graphene_snapshot
snapshot.cpp
)
target_link_libraries( graphene_snapshot graphene_chain graphene_app )
target_include_directories( graphene_snapshot
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
install( TARGETS
graphene_snapshot
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2017 Peter Conrad, and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <graphene/app/plugin.hpp>
#include <graphene/chain/database.hpp>
#include <fc/time.hpp>
namespace graphene { namespace snapshot_plugin {
class snapshot_plugin : public graphene::app::plugin {
public:
~snapshot_plugin() {}
std::string plugin_name()const override;
virtual void plugin_set_program_options(
boost::program_options::options_description &command_line_options,
boost::program_options::options_description &config_file_options
) override;
virtual void plugin_initialize( const boost::program_options::variables_map& options ) override;
virtual void plugin_startup() override;
virtual void plugin_shutdown() override;
private:
void check_snapshot( const graphene::chain::signed_block& b);
uint32_t snapshot_block = -1, last_block = 0;
fc::time_point_sec snapshot_time = fc::time_point_sec::maximum(), last_time = fc::time_point_sec(1);
fc::path dest;
};
} } //graphene::snapshot_plugin

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2017 Peter Conrad, 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/snapshot/snapshot.hpp>
#include <graphene/chain/database.hpp>
#include <fc/io/fstream.hpp>
using namespace graphene::snapshot_plugin;
using std::string;
using std::vector;
namespace bpo = boost::program_options;
static const char* OPT_BLOCK_NUM = "snapshot-at-block";
static const char* OPT_BLOCK_TIME = "snapshot-at-time";
static const char* OPT_DEST = "snapshot-to";
void snapshot_plugin::plugin_set_program_options(
boost::program_options::options_description& command_line_options,
boost::program_options::options_description& config_file_options)
{
command_line_options.add_options()
(OPT_BLOCK_NUM, bpo::value<uint32_t>(), "Block number after which to do a snapshot")
(OPT_BLOCK_TIME, bpo::value<string>(), "Block time (ISO format) after which to do a snapshot")
(OPT_DEST, bpo::value<string>(), "Pathname of JSON file where to store the snapshot")
;
config_file_options.add(command_line_options);
}
std::string snapshot_plugin::plugin_name()const
{
return "snapshot";
}
void snapshot_plugin::plugin_initialize(const boost::program_options::variables_map& options)
{ try {
ilog("snapshot plugin: plugin_initialize() begin");
if( options.count(OPT_BLOCK_NUM) || options.count(OPT_BLOCK_TIME) )
{
FC_ASSERT( options.count(OPT_DEST), "Must specify snapshot-to in addition to snapshot-at-block or snapshot-at-time!" );
dest = options[OPT_DEST].as<std::string>();
if( options.count(OPT_BLOCK_NUM) )
snapshot_block = options[OPT_BLOCK_NUM].as<uint32_t>();
if( options.count(OPT_BLOCK_TIME) )
snapshot_time = fc::time_point_sec::from_iso_string( options[OPT_BLOCK_TIME].as<std::string>() );
database().applied_block.connect( [&]( const graphene::chain::signed_block& b ) {
check_snapshot( b );
});
}
else
FC_ASSERT( !options.count("snapshot-to"), "Must specify snapshot-at-block or snapshot-at-time in addition to snapshot-to!" );
ilog("snapshot plugin: plugin_initialize() end");
} FC_LOG_AND_RETHROW() }
void snapshot_plugin::plugin_startup() {}
void snapshot_plugin::plugin_shutdown() {}
static void create_snapshot( const graphene::chain::database& db, const fc::path& dest )
{
ilog("snapshot plugin: creating snapshot");
fc::ofstream out;
try
{
out.open( dest );
}
catch ( fc::exception& e )
{
wlog( "Failed to open snapshot destination: ${ex}", ("ex",e) );
return;
}
for( uint32_t space_id = 0; space_id < 256; space_id++ )
for( uint32_t type_id = 0; type_id < 256; type_id++ )
{
try
{
db.get_index( (uint8_t)space_id, (uint8_t)type_id );
}
catch (fc::assert_exception& e)
{
continue;
}
auto& index = db.get_index( (uint8_t)space_id, (uint8_t)type_id );
index.inspect_all_objects( [&out]( const graphene::db::object& o ) {
out << fc::json::to_string( o.to_variant() ) << '\n';
});
}
out.close();
ilog("snapshot plugin: created snapshot");
}
void snapshot_plugin::check_snapshot( const graphene::chain::signed_block& b )
{ try {
uint32_t current_block = b.block_num();
if( (last_block < snapshot_block && snapshot_block <= current_block)
|| (last_time < snapshot_time && snapshot_time <= b.timestamp) )
create_snapshot( database(), dest );
last_block = current_block;
last_time = b.timestamp;
} FC_LOG_AND_RETHROW() }

View file

@ -198,7 +198,6 @@ block_production_condition::block_production_condition_enum witness_plugin::bloc
//ilog("Not producing block because it isn't my turn");
break;
case block_production_condition::not_time_yet:
//ilog("Not producing block because slot has not yet arrived");
dlog("Not producing block because slot has not yet arrived");
break;
case block_production_condition::no_private_key:

View file

@ -632,6 +632,7 @@ class wallet_api
* @return Whether a public key is known
*/
bool is_public_key_registered(string public_key) const;
/**
* @param role - active | owner | memo
*/

View file

@ -11,7 +11,7 @@ endif()
# We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246
target_link_libraries( witness_node
PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full graphene_snapshot fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
# also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins
install( TARGETS

View file

@ -31,6 +31,7 @@
//#include <graphene/generate_uia_sharedrop_genesis/generate_uia_sharedrop_genesis.hpp>
#include <graphene/bookie/bookie_plugin.hpp>
#include <graphene/utilities/git_revision.hpp>
#include <graphene/snapshot/snapshot.hpp>
#include <fc/exception/exception.hpp>
#include <fc/thread/thread.hpp>
@ -84,6 +85,7 @@ int main(int argc, char** argv) {
//auto generate_uia_sharedrop_genesis_plug = node->register_plugin<generate_uia_sharedrop_genesis::generate_uia_sharedrop_genesis_plugin>();
auto list_plug = node->register_plugin<accounts_list::accounts_list_plugin>();
auto bookie_plug = node->register_plugin<bookie::bookie_plugin>();
auto snapshot_plug = node->register_plugin<snapshot_plugin::snapshot_plugin>();
try
{

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* Copyright (c) 2018 Peerplays Blockchain Standards Association, and contributors.
*
* The MIT License
*
@ -32,12 +32,14 @@
#include <boost/test/unit_test.hpp>
#include <fc/crypto/openssl.hpp>
#include <fc/log/appender.hpp>
#include <openssl/rand.h>
#include <graphene/utilities/tempdir.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/is_authorized_asset.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/sport_object.hpp>
#include <graphene/chain/event_object.hpp>
@ -48,10 +50,91 @@
#include <graphene/bookie/bookie_api.hpp>
//#include <boost/algorithm/string/replace.hpp>
struct enable_betting_logging_config {
enable_betting_logging_config()
{
fc::logger::get("betting").add_appender(fc::appender::get("stdout"));
fc::logger::get("betting").set_log_level(fc::log_level::debug);
}
~enable_betting_logging_config() {
fc::logger::get("betting").remove_appender(fc::appender::get("stdout"));
}
};
BOOST_GLOBAL_FIXTURE( enable_betting_logging_config );
using namespace graphene::chain;
using namespace graphene::chain::test;
using namespace graphene::chain::keywords;
// While the bets are placed, stored, and sorted using the decimal form of their odds, matching
// uses the ratios to ensure no rounding takes place.
// The allowed odds are defined by rules that can be changed at runtime.
// For reference when designing/debugging tests, here is the list of allowed decimal odds and their
// corresponding ratios as set in the genesis block.
//
// decimal ratio | decimal ratio | decimal ratio | decimal ratio | decimal ratio | decimal ratio
// ------------------+-----------------+-------------------+-------------------+-------------------+-----------------
// 1.01 100:1 | 1.6 5:3 | 2.38 50:69 | 4.8 5:19 | 26 1:25 | 440 1:439
// 1.02 50:1 | 1.61 100:61 | 2.4 5:7 | 4.9 10:39 | 27 1:26 | 450 1:449
// 1.03 100:3 | 1.62 50:31 | 2.42 50:71 | 5 1:4 | 28 1:27 | 460 1:459
// 1.04 25:1 | 1.63 100:63 | 2.44 25:36 | 5.1 10:41 | 29 1:28 | 470 1:469
// 1.05 20:1 | 1.64 25:16 | 2.46 50:73 | 5.2 5:21 | 30 1:29 | 480 1:479
// 1.06 50:3 | 1.65 20:13 | 2.48 25:37 | 5.3 10:43 | 32 1:31 | 490 1:489
// 1.07 100:7 | 1.66 50:33 | 2.5 2:3 | 5.4 5:22 | 34 1:33 | 500 1:499
// 1.08 25:2 | 1.67 100:67 | 2.52 25:38 | 5.5 2:9 | 36 1:35 | 510 1:509
// 1.09 100:9 | 1.68 25:17 | 2.54 50:77 | 5.6 5:23 | 38 1:37 | 520 1:519
// 1.1 10:1 | 1.69 100:69 | 2.56 25:39 | 5.7 10:47 | 40 1:39 | 530 1:529
// 1.11 100:11 | 1.7 10:7 | 2.58 50:79 | 5.8 5:24 | 42 1:41 | 540 1:539
// 1.12 25:3 | 1.71 100:71 | 2.6 5:8 | 5.9 10:49 | 44 1:43 | 550 1:549
// 1.13 100:13 | 1.72 25:18 | 2.62 50:81 | 6 1:5 | 46 1:45 | 560 1:559
// 1.14 50:7 | 1.73 100:73 | 2.64 25:41 | 6.2 5:26 | 48 1:47 | 570 1:569
// 1.15 20:3 | 1.74 50:37 | 2.66 50:83 | 6.4 5:27 | 50 1:49 | 580 1:579
// 1.16 25:4 | 1.75 4:3 | 2.68 25:42 | 6.6 5:28 | 55 1:54 | 590 1:589
// 1.17 100:17 | 1.76 25:19 | 2.7 10:17 | 6.8 5:29 | 60 1:59 | 600 1:599
// 1.18 50:9 | 1.77 100:77 | 2.72 25:43 | 7 1:6 | 65 1:64 | 610 1:609
// 1.19 100:19 | 1.78 50:39 | 2.74 50:87 | 7.2 5:31 | 70 1:69 | 620 1:619
// 1.2 5:1 | 1.79 100:79 | 2.76 25:44 | 7.4 5:32 | 75 1:74 | 630 1:629
// 1.21 100:21 | 1.8 5:4 | 2.78 50:89 | 7.6 5:33 | 80 1:79 | 640 1:639
// 1.22 50:11 | 1.81 100:81 | 2.8 5:9 | 7.8 5:34 | 85 1:84 | 650 1:649
// 1.23 100:23 | 1.82 50:41 | 2.82 50:91 | 8 1:7 | 90 1:89 | 660 1:659
// 1.24 25:6 | 1.83 100:83 | 2.84 25:46 | 8.2 5:36 | 95 1:94 | 670 1:669
// 1.25 4:1 | 1.84 25:21 | 2.86 50:93 | 8.4 5:37 | 100 1:99 | 680 1:679
// 1.26 50:13 | 1.85 20:17 | 2.88 25:47 | 8.6 5:38 | 110 1:109 | 690 1:689
// 1.27 100:27 | 1.86 50:43 | 2.9 10:19 | 8.8 5:39 | 120 1:119 | 700 1:699
// 1.28 25:7 | 1.87 100:87 | 2.92 25:48 | 9 1:8 | 130 1:129 | 710 1:709
// 1.29 100:29 | 1.88 25:22 | 2.94 50:97 | 9.2 5:41 | 140 1:139 | 720 1:719
// 1.3 10:3 | 1.89 100:89 | 2.96 25:49 | 9.4 5:42 | 150 1:149 | 730 1:729
// 1.31 100:31 | 1.9 10:9 | 2.98 50:99 | 9.6 5:43 | 160 1:159 | 740 1:739
// 1.32 25:8 | 1.91 100:91 | 3 1:2 | 9.8 5:44 | 170 1:169 | 750 1:749
// 1.33 100:33 | 1.92 25:23 | 3.05 20:41 | 10 1:9 | 180 1:179 | 760 1:759
// 1.34 50:17 | 1.93 100:93 | 3.1 10:21 | 10.5 2:19 | 190 1:189 | 770 1:769
// 1.35 20:7 | 1.94 50:47 | 3.15 20:43 | 11 1:10 | 200 1:199 | 780 1:779
// 1.36 25:9 | 1.95 20:19 | 3.2 5:11 | 11.5 2:21 | 210 1:209 | 790 1:789
// 1.37 100:37 | 1.96 25:24 | 3.25 4:9 | 12 1:11 | 220 1:219 | 800 1:799
// 1.38 50:19 | 1.97 100:97 | 3.3 10:23 | 12.5 2:23 | 230 1:229 | 810 1:809
// 1.39 100:39 | 1.98 50:49 | 3.35 20:47 | 13 1:12 | 240 1:239 | 820 1:819
// 1.4 5:2 | 1.99 100:99 | 3.4 5:12 | 13.5 2:25 | 250 1:249 | 830 1:829
// 1.41 100:41 | 2 1:1 | 3.45 20:49 | 14 1:13 | 260 1:259 | 840 1:839
// 1.42 50:21 | 2.02 50:51 | 3.5 2:5 | 14.5 2:27 | 270 1:269 | 850 1:849
// 1.43 100:43 | 2.04 25:26 | 3.55 20:51 | 15 1:14 | 280 1:279 | 860 1:859
// 1.44 25:11 | 2.06 50:53 | 3.6 5:13 | 15.5 2:29 | 290 1:289 | 870 1:869
// 1.45 20:9 | 2.08 25:27 | 3.65 20:53 | 16 1:15 | 300 1:299 | 880 1:879
// 1.46 50:23 | 2.1 10:11 | 3.7 10:27 | 16.5 2:31 | 310 1:309 | 890 1:889
// 1.47 100:47 | 2.12 25:28 | 3.75 4:11 | 17 1:16 | 320 1:319 | 900 1:899
// 1.48 25:12 | 2.14 50:57 | 3.8 5:14 | 17.5 2:33 | 330 1:329 | 910 1:909
// 1.49 100:49 | 2.16 25:29 | 3.85 20:57 | 18 1:17 | 340 1:339 | 920 1:919
// 1.5 2:1 | 2.18 50:59 | 3.9 10:29 | 18.5 2:35 | 350 1:349 | 930 1:929
// 1.51 100:51 | 2.2 5:6 | 3.95 20:59 | 19 1:18 | 360 1:359 | 940 1:939
// 1.52 25:13 | 2.22 50:61 | 4 1:3 | 19.5 2:37 | 370 1:369 | 950 1:949
// 1.53 100:53 | 2.24 25:31 | 4.1 10:31 | 20 1:19 | 380 1:379 | 960 1:959
// 1.54 50:27 | 2.26 50:63 | 4.2 5:16 | 21 1:20 | 390 1:389 | 970 1:969
// 1.55 20:11 | 2.28 25:32 | 4.3 10:33 | 22 1:21 | 400 1:399 | 980 1:979
// 1.56 25:14 | 2.3 10:13 | 4.4 5:17 | 23 1:22 | 410 1:409 | 990 1:989
// 1.57 100:57 | 2.32 25:33 | 4.5 2:7 | 24 1:23 | 420 1:419 | 1000 1:999
// 1.58 50:29 | 2.34 50:67 | 4.6 5:18 | 25 1:24 | 430 1:429 |
// 1.59 100:59 | 2.36 25:34 | 4.7 10:37
#define CREATE_ICE_HOCKEY_BETTING_MARKET(never_in_play, delay_before_settling) \
create_sport({{"en", "Ice Hockey"}, {"zh_Hans", "冰球"}, {"ja", "アイスホッケー"}}); \
generate_blocks(1); \
@ -273,12 +356,14 @@ BOOST_AUTO_TEST_CASE(binned_order_books)
// the binned orders returned should be chosen so that we if we assume those orders are real and we place
// matching lay orders, we will completely consume the underlying orders and leave no orders on the books
//
// for the bets bob placed above, we should get: 200 @ 1.6, 300 @ 1.7
BOOST_CHECK_EQUAL(binned_orders_point_one.aggregated_back_bets.size(), 2u);
BOOST_CHECK_EQUAL(binned_orders_point_one.aggregated_lay_bets.size(), 0u);
for (const graphene::bookie::order_bin& binned_order : binned_orders_point_one.aggregated_back_bets)
{
// compute the matching lay order
share_type lay_amount = bet_object::get_approximate_matching_amount(binned_order.amount_to_bet, binned_order.backer_multiplier, bet_type::back, false /* round down */);
share_type lay_amount = bet_object::get_approximate_matching_amount(binned_order.amount_to_bet, binned_order.backer_multiplier, bet_type::back, true /* round up */);
ilog("Alice is laying with ${lay_amount} at odds ${odds} to match the binned back amount ${back_amount}", ("lay_amount", lay_amount)("odds", binned_order.backer_multiplier)("back_amount", binned_order.amount_to_bet));
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(lay_amount, asset_id_type()), binned_order.backer_multiplier);
}
@ -294,6 +379,7 @@ BOOST_AUTO_TEST_CASE(binned_order_books)
BOOST_CHECK(bet_odds_idx.lower_bound(std::make_tuple(capitals_win_market.id)) == bet_odds_idx.end());
// place lay bets at decimal odds of 1.55, 1.6, 1.65, 1.66, and 1.67
// these bets will get rounded down, actual amounts are 99, 99, 91, 99, and 67
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 155 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 16 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 165 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
@ -305,16 +391,28 @@ BOOST_AUTO_TEST_CASE(binned_order_books)
// the binned orders returned should be chosen so that we if we assume those orders are real and we place
// matching lay orders, we will completely consume the underlying orders and leave no orders on the books
//
// for the bets bob placed above, we shoudl get 356 @ 1.6, 99 @ 1.5
BOOST_CHECK_EQUAL(binned_orders_point_one.aggregated_back_bets.size(), 0u);
BOOST_CHECK_EQUAL(binned_orders_point_one.aggregated_lay_bets.size(), 2u);
for (const graphene::bookie::order_bin& binned_order : binned_orders_point_one.aggregated_lay_bets)
{
// compute the matching lay order
share_type back_amount = bet_object::get_approximate_matching_amount(binned_order.amount_to_bet, binned_order.backer_multiplier, bet_type::lay, false /* round down */);
share_type back_amount = bet_object::get_approximate_matching_amount(binned_order.amount_to_bet, binned_order.backer_multiplier, bet_type::lay, true /* round up */);
ilog("Alice is backing with ${back_amount} at odds ${odds} to match the binned lay amount ${lay_amount}", ("back_amount", back_amount)("odds", binned_order.backer_multiplier)("lay_amount", binned_order.amount_to_bet));
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(back_amount, asset_id_type()), binned_order.backer_multiplier);
ilog("After alice's bet, order book is:");
bet_iter = bet_odds_idx.lower_bound(std::make_tuple(capitals_win_market.id));
while (bet_iter != bet_odds_idx.end() &&
bet_iter->betting_market_id == capitals_win_market.id)
{
idump((*bet_iter));
++bet_iter;
}
}
bet_iter = bet_odds_idx.lower_bound(std::make_tuple(capitals_win_market.id));
while (bet_iter != bet_odds_idx.end() &&
bet_iter->betting_market_id == capitals_win_market.id)
@ -356,7 +454,7 @@ BOOST_AUTO_TEST_CASE( peerplays_sport_create_test )
generate_blocks(1);
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage();
uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value);
@ -396,6 +494,347 @@ BOOST_AUTO_TEST_CASE( cancel_unmatched_in_betting_group_test )
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(match_using_takers_expected_amounts)
{
try
{
generate_blocks(1);
ACTORS( (alice)(bob) );
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
transfer(account_id_type(), alice_id, asset(10000000));
share_type alice_expected_balance = 10000000;
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
// lay 46 at 1.94 odds (50:47) -- this is too small to be placed on the books and there's
// nothing for it to match, so it should be canceled
BOOST_TEST_MESSAGE("lay 46 at 1.94 odds (50:47) -- this is too small to be placed on the books and there's nothing for it to match, so it should be canceled");
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(46, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
BOOST_TEST_MESSAGE("alice's balance should be " << alice_expected_balance.value);
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
ilog("message");
// lay 47 at 1.94 odds (50:47) -- this is an exact amount, nothing surprising should happen here
BOOST_TEST_MESSAGE("alice lays 470 at 1.94 odds (50:47) -- this is an exact amount, nothing surprising should happen here");
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(47, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
alice_expected_balance -= 47;
BOOST_TEST_MESSAGE("alice's balance should be " << alice_expected_balance.value);
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
// lay 100 at 1.91 odds (100:91) -- this is an inexact match, we should get refunded 9 and leave a bet for 91 on the books
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 191 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
alice_expected_balance -= 91;
BOOST_TEST_MESSAGE("alice's balance should be " << alice_expected_balance.value);
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
transfer(account_id_type(), bob_id, asset(10000000));
share_type bob_expected_balance = 10000000;
BOOST_TEST_MESSAGE("bob's balance should be " << bob_expected_balance.value);
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
// now have bob match it with a back of 300 at 1.5
// This should:
// match the full 47 @ 1.94 with 50
// match the full 91 @ 1.91 with 100
// bob's balance goes down by 300 (150 is matched, 150 is still on the books)
// leaves a back bet of 150 @ 1.5 on the books
BOOST_TEST_MESSAGE("now have bob match it with a back of 300 at 1.5");
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(300, asset_id_type()), 15 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
bob_expected_balance -= 300;
BOOST_TEST_MESSAGE("bob's balance should be " << bob_expected_balance.value);
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
}
FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(match_using_takers_expected_amounts2)
{
try
{
generate_blocks(1);
ACTORS( (alice)(bob) );
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
transfer(account_id_type(), alice_id, asset(10000000));
share_type alice_expected_balance = 10000000;
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
// lay 470 at 1.94 odds (50:47) -- this is an exact amount, nothing surprising should happen here
BOOST_TEST_MESSAGE("alice lays 470 at 1.94 odds (50:47) -- this is an exact amount, nothing surprising should happen here");
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(470, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
alice_expected_balance -= 470;
BOOST_TEST_MESSAGE("alice's balance should be " << alice_expected_balance.value);
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
transfer(account_id_type(), bob_id, asset(10000000));
share_type bob_expected_balance = 10000000;
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
// now have bob match it with a back of 900 at 1.5
// This should:
// match all 500 of bob's bet, and leave 400 @ 1.5 on the books
// bob's balance goes down by the 900 he paid (500 matched, 400 unmatched)
// alice's bet is completely removed from the books.
BOOST_TEST_MESSAGE("now have bob match it with a back of 900 at 1.5");
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(900, asset_id_type()), 15 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
bob_expected_balance -= 900;
BOOST_TEST_MESSAGE("bob's balance should be " << bob_expected_balance.value);
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
}
FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(match_using_takers_expected_amounts3)
{
try
{
generate_blocks(1);
ACTORS( (alice)(bob) );
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
transfer(account_id_type(), alice_id, asset(10000000));
share_type alice_expected_balance = 10000000;
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
// lay 470 at 1.94 odds (50:47) -- this is an exact amount, nothing surprising should happen here
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(470, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
alice_expected_balance -= 470;
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
transfer(account_id_type(), bob_id, asset(10000000));
share_type bob_expected_balance = 10000000;
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
// now have bob match it with a back of 1000 at 1.5
// This should:
// match all of the 470 @ 1.94 with 500, and leave 500 left on the books
// bob's balance goes down by the 1000 he paid, 500 matching, 500 unmatching
// alice's bet is removed from the books
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(1000, asset_id_type()), 15 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
bob_expected_balance -= 1000;
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
}
FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(match_using_takers_expected_amounts4)
{
try
{
generate_blocks(1);
ACTORS( (alice)(bob) );
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
transfer(account_id_type(), alice_id, asset(10000000));
share_type alice_expected_balance = 10000000;
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
// back 1000 at 1.89 odds (100:89) -- this is an exact amount, nothing surprising should happen here
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000, asset_id_type()), 189 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
alice_expected_balance -= 1000;
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
// back 1000 at 1.97 odds (100:97) -- again, this is an exact amount, nothing surprising should happen here
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000, asset_id_type()), 197 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
alice_expected_balance -= 1000;
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
transfer(account_id_type(), bob_id, asset(10000000));
share_type bob_expected_balance = 10000000;
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
// now have bob match it with a lay of 3000 at 2.66
// * This means bob expects to pay 3000 and match against 1807.2289. Or,
// put another way, bob wants to buy a payout of up to 1807.2289 if the
// capitals win, and he is willing to pay up to 3000 to do so.
// * The first thing that happens is bob's bet gets rounded down to something
// that can match exactly. 2.66 is 50:83 odds, so bob's bet is
// reduced to 2988, which should match against 1800.
// So bob gets an immediate refund of 12
// * The next thing that happens is a match against the top bet on the order book.
// That's 1000 @ 1.89. We match at those odds (100:89), so bob will fully match
// this bet, paying 890 to get 1000 of his desired win position.
// * this top bet is removed from the order books
// * bob now has 1000 of the 1800 win position he wants. we adjust his bet
// so that what is left will only match 800. This means we will
// refund bob 770. His remaining bet is now lay 1328 @ 2.66
// * Now we match the next bet on the order books, which is 1000 @ 1.97 (100:97).
// Bob only wants 800 of 1000 win position being offered, so he will not
// completely consume this bet.
// * Bob pays 776 to get his 800 win position.
// * alice's top bet on the books is reduced 200 @ 1.97
// * Bob now has as much of a position as he was willing to buy. We refund
// the remainder of his bet, which is 552
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(3000, asset_id_type()), 266 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
bob_expected_balance -= 3000 - 12 - 770 - 552;
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
}
FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(match_using_takers_expected_amounts5)
{
try
{
generate_blocks(1);
ACTORS( (alice)(bob) );
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
transfer(account_id_type(), alice_id, asset(10000000));
share_type alice_expected_balance = 10000000;
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
// back 1100 at 1.86 odds (50:43) -- this is an exact amount, nothing surprising should happen here
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1100, asset_id_type()), 186 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
alice_expected_balance -= 1100;
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
transfer(account_id_type(), bob_id, asset(10000000));
share_type bob_expected_balance = 10000000;
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
// now have bob match it with a lay of 1100 at 1.98
// * This means bob expects to pay 1100 and match against 1122.4, Or,
// put another way, bob wants to buy a payout of up to 1122.4 if the
// capitals win, and he is willing to pay up to 1100 to do so.
// * The first thing that happens is bob's bet gets rounded down to something
// that can match exactly. 1.98 (50:49) odds, so bob's bet is
// reduced to 1078, which should match against 1100.
// So bob gets an immediate refund of 22
// * The next thing that happens is a match against the top bet on the order book.
// That's 1100 @ 1.86, At these odds, bob's 980 can buy all 1100 of bet, he
// pays 1100:946.
//
// * alice's bet is fully matched, it is removed from the books
// * bob's bet is fully matched, he is refunded the remaining 132 and his
// bet is complete
// * bob's balance is reduced by the 946 he paid
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1100, asset_id_type()), 198 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
bob_expected_balance -= 1100 - 22 - 132;
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
}
FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(match_using_takers_expected_amounts6)
{
try
{
generate_blocks(1);
ACTORS( (alice)(bob) );
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
graphene::bookie::bookie_api bookie_api(app);
transfer(account_id_type(), alice_id, asset(1000000000));
share_type alice_expected_balance = 1000000000;
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(10000000, asset_id_type()), 13 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(10000000, asset_id_type()), 15 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(10000000, asset_id_type()), 16 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
alice_expected_balance -= 30000000;
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
// check order books to see they match the bets we placed
const auto& bet_odds_idx = db.get_index_type<bet_object_index>().indices().get<by_odds>();
auto bet_iter = bet_odds_idx.lower_bound(std::make_tuple(capitals_win_market.id));
BOOST_REQUIRE(bet_iter != bet_odds_idx.end());
BOOST_REQUIRE(bet_iter->betting_market_id == capitals_win_market.id);
BOOST_REQUIRE(bet_iter->bettor_id == alice_id);
BOOST_REQUIRE(bet_iter->amount_to_bet == asset(10000000, asset_id_type()));
BOOST_REQUIRE(bet_iter->backer_multiplier == 13 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
BOOST_REQUIRE(bet_iter->back_or_lay == bet_type::back);
++bet_iter;
BOOST_REQUIRE(bet_iter != bet_odds_idx.end());
BOOST_REQUIRE(bet_iter->betting_market_id == capitals_win_market.id);
BOOST_REQUIRE(bet_iter->bettor_id == alice_id);
BOOST_REQUIRE(bet_iter->amount_to_bet == asset(10000000, asset_id_type()));
BOOST_REQUIRE(bet_iter->backer_multiplier == 15 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
BOOST_REQUIRE(bet_iter->back_or_lay == bet_type::back);
++bet_iter;
BOOST_REQUIRE(bet_iter != bet_odds_idx.end());
BOOST_REQUIRE(bet_iter->betting_market_id == capitals_win_market.id);
BOOST_REQUIRE(bet_iter->bettor_id == alice_id);
BOOST_REQUIRE(bet_iter->amount_to_bet == asset(10000000, asset_id_type()));
BOOST_REQUIRE(bet_iter->backer_multiplier == 16 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
BOOST_REQUIRE(bet_iter->back_or_lay == bet_type::back);
++bet_iter;
BOOST_REQUIRE(bet_iter == bet_odds_idx.end() || bet_iter->betting_market_id != capitals_win_market.id);
// check the binned order books from the bookie plugin to make sure they match
graphene::bookie::binned_order_book binned_orders_point_one = bookie_api.get_binned_order_book(capitals_win_market.id, 1);
auto aggregated_back_bets_iter = binned_orders_point_one.aggregated_back_bets.begin();
BOOST_REQUIRE(aggregated_back_bets_iter != binned_orders_point_one.aggregated_back_bets.end());
BOOST_REQUIRE(aggregated_back_bets_iter->amount_to_bet.value == 10000000);
BOOST_REQUIRE(aggregated_back_bets_iter->backer_multiplier == 13 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
++aggregated_back_bets_iter;
BOOST_REQUIRE(aggregated_back_bets_iter != binned_orders_point_one.aggregated_back_bets.end());
BOOST_REQUIRE(aggregated_back_bets_iter->amount_to_bet.value == 10000000);
BOOST_REQUIRE(aggregated_back_bets_iter->backer_multiplier == 15 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
++aggregated_back_bets_iter;
BOOST_REQUIRE(aggregated_back_bets_iter != binned_orders_point_one.aggregated_back_bets.end());
BOOST_REQUIRE(aggregated_back_bets_iter->amount_to_bet.value == 10000000);
BOOST_REQUIRE(aggregated_back_bets_iter->backer_multiplier == 16 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
++aggregated_back_bets_iter;
BOOST_REQUIRE(aggregated_back_bets_iter == binned_orders_point_one.aggregated_back_bets.end());
BOOST_REQUIRE(binned_orders_point_one.aggregated_lay_bets.empty());
transfer(account_id_type(), bob_id, asset(1000000000));
share_type bob_expected_balance = 1000000000;
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(5000000, asset_id_type()), 15 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
ilog("Order books after bob's matching lay bet:");
bet_iter = bet_odds_idx.lower_bound(std::make_tuple(capitals_win_market.id));
while (bet_iter != bet_odds_idx.end() &&
bet_iter->betting_market_id == capitals_win_market.id)
{
idump((*bet_iter));
++bet_iter;
}
bob_expected_balance -= 5000000 - 2000000;
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
// check order books to see they match after bob's bet matched
bet_iter = bet_odds_idx.lower_bound(std::make_tuple(capitals_win_market.id));
BOOST_REQUIRE(bet_iter != bet_odds_idx.end());
BOOST_REQUIRE(bet_iter->betting_market_id == capitals_win_market.id);
BOOST_REQUIRE(bet_iter->bettor_id == alice_id);
BOOST_REQUIRE(bet_iter->amount_to_bet == asset(10000000, asset_id_type()));
BOOST_REQUIRE(bet_iter->backer_multiplier == 15 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
BOOST_REQUIRE(bet_iter->back_or_lay == bet_type::back);
++bet_iter;
BOOST_REQUIRE(bet_iter != bet_odds_idx.end());
BOOST_REQUIRE(bet_iter->betting_market_id == capitals_win_market.id);
BOOST_REQUIRE(bet_iter->bettor_id == alice_id);
BOOST_REQUIRE(bet_iter->amount_to_bet == asset(10000000, asset_id_type()));
BOOST_REQUIRE(bet_iter->backer_multiplier == 16 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
BOOST_REQUIRE(bet_iter->back_or_lay == bet_type::back);
++bet_iter;
BOOST_REQUIRE(bet_iter == bet_odds_idx.end() || bet_iter->betting_market_id != capitals_win_market.id);
// check the binned order books from the bookie plugin to make sure they match
binned_orders_point_one = bookie_api.get_binned_order_book(capitals_win_market.id, 1);
aggregated_back_bets_iter = binned_orders_point_one.aggregated_back_bets.begin();
BOOST_REQUIRE(aggregated_back_bets_iter != binned_orders_point_one.aggregated_back_bets.end());
BOOST_REQUIRE(aggregated_back_bets_iter->amount_to_bet.value == 10000000);
BOOST_REQUIRE(aggregated_back_bets_iter->backer_multiplier == 15 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
++aggregated_back_bets_iter;
BOOST_REQUIRE(aggregated_back_bets_iter != binned_orders_point_one.aggregated_back_bets.end());
BOOST_REQUIRE(aggregated_back_bets_iter->amount_to_bet.value == 10000000);
BOOST_REQUIRE(aggregated_back_bets_iter->backer_multiplier == 16 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
++aggregated_back_bets_iter;
BOOST_REQUIRE(aggregated_back_bets_iter == binned_orders_point_one.aggregated_back_bets.end());
BOOST_REQUIRE(binned_orders_point_one.aggregated_lay_bets.empty());
}
FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(inexact_odds)
{
try
@ -442,6 +881,71 @@ BOOST_AUTO_TEST_CASE(inexact_odds)
FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(bet_reversal_test)
{
// test whether we can bet our entire balance in one direction, then reverse our bet (while having zero balance)
try
{
generate_blocks(1);
ACTORS( (alice)(bob) );
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
transfer(account_id_type(), alice_id, asset(10000000));
share_type alice_expected_balance = 10000000;
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
// back with our entire balance
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(10000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), 0);
// reverse the bet
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(20000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), 0);
// try to re-reverse it, but go too far
BOOST_CHECK_THROW( place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(30000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION), fc::exception);
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), 0);
}
FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(bet_against_exposure_test)
{
// test whether we can bet our entire balance in one direction, have it match, then reverse our bet (while having zero balance)
try
{
generate_blocks(1);
ACTORS( (alice)(bob) );
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
transfer(account_id_type(), alice_id, asset(10000000));
transfer(account_id_type(), bob_id, asset(10000000));
int64_t alice_expected_balance = 10000000;
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance);
int64_t bob_expected_balance = 10000000;
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance);
// back with alice's entire balance
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(10000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
alice_expected_balance -= 10000000;
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance);
// lay with bob's entire balance, which fully matches bob's bet
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(10000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
bob_expected_balance -= 10000000;
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance);
// reverse the bet
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(20000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance);
// try to re-reverse it, but go too far
BOOST_CHECK_THROW( place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(30000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION), fc::exception);
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance);
}
FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(persistent_objects_test)
{
try
@ -539,6 +1043,59 @@ BOOST_AUTO_TEST_CASE(persistent_objects_test)
FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(test_settled_market_states)
{
try
{
ACTORS( (alice)(bob) );
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
graphene::bookie::bookie_api bookie_api(app);
transfer(account_id_type(), alice_id, asset(10000000));
transfer(account_id_type(), bob_id, asset(10000000));
share_type alice_expected_balance = 10000000;
share_type bob_expected_balance = 10000000;
BOOST_REQUIRE_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
BOOST_REQUIRE_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
idump((capitals_win_market.get_status()));
BOOST_TEST_MESSAGE("setting the event to in_progress");
update_event(capitals_vs_blackhawks.id, _status = event_status::in_progress);
generate_blocks(1);
BOOST_TEST_MESSAGE("setting the event to finished");
update_event(capitals_vs_blackhawks.id, _status = event_status::finished);
generate_blocks(1);
resolve_betting_market_group(moneyline_betting_markets.id,
{{capitals_win_market.id, betting_market_resolution_type::win},
{blackhawks_win_market.id, betting_market_resolution_type::not_win}});
// as soon as the market is resolved during the generate_block(), these markets
// should be deleted and our references will go out of scope. Save the
// market ids here so we can verify that they were really deleted
betting_market_id_type capitals_win_market_id = capitals_win_market.id;
betting_market_id_type blackhawks_win_market_id = blackhawks_win_market.id;
generate_blocks(1);
// test getting markets
// test that we cannot get them from the database directly
BOOST_CHECK_THROW(capitals_win_market_id(db), fc::exception);
BOOST_CHECK_THROW(blackhawks_win_market_id(db), fc::exception);
fc::variants objects_from_bookie = bookie_api.get_objects({capitals_win_market_id, blackhawks_win_market_id});
BOOST_REQUIRE_EQUAL(objects_from_bookie.size(), 2u);
idump((objects_from_bookie));
BOOST_CHECK(!objects_from_bookie[0].is_null());
BOOST_CHECK(!objects_from_bookie[1].is_null());
}
FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(delayed_bets_test) // test live betting
{
try
@ -731,6 +1288,97 @@ BOOST_AUTO_TEST_CASE( chained_market_create_test )
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE( testnet_witness_block_production_error )
{
try
{
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
create_betting_market_group({{"en", "Unused"}}, capitals_vs_blackhawks.id, betting_market_rules.id, asset_id_type(), false, 0);
generate_blocks(1);
const betting_market_group_object& unused_betting_markets = *db.get_index_type<betting_market_group_object_index>().indices().get<by_id>().rbegin();
BOOST_TEST_MESSAGE("setting the event in progress");
update_event(capitals_vs_blackhawks.id, _status = event_status::in_progress);
generate_blocks(1);
BOOST_CHECK(capitals_vs_blackhawks.get_status() == event_status::in_progress);
BOOST_CHECK(moneyline_betting_markets.get_status() == betting_market_group_status::in_play);
BOOST_CHECK(unused_betting_markets.get_status() == betting_market_group_status::in_play);
BOOST_TEST_MESSAGE("setting the event to finished");
update_event(capitals_vs_blackhawks.id, _status = event_status::finished);
generate_blocks(1);
BOOST_CHECK(capitals_vs_blackhawks.get_status() == event_status::finished);
BOOST_CHECK(moneyline_betting_markets.get_status() == betting_market_group_status::closed);
BOOST_CHECK(capitals_win_market.get_status() == betting_market_status::unresolved);
BOOST_CHECK(blackhawks_win_market.get_status() == betting_market_status::unresolved);
BOOST_CHECK(unused_betting_markets.get_status() == betting_market_group_status::closed);
BOOST_TEST_MESSAGE("setting the event to canceled");
update_event(capitals_vs_blackhawks.id, _status = event_status::canceled);
generate_blocks(1);
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE( cancel_one_event_in_group )
{
// test that creates an event group with two events in it. We walk one event through the
// usual sequence and cancel it, verify that it doesn't alter the other event in the group
try
{
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
// create a second event in the same betting market group
create_event({{"en", "Boston Bruins/Pittsburgh Penguins"}}, {{"en", "2016-17"}}, nhl.id);
generate_blocks(1);
const event_object& bruins_vs_penguins = *db.get_index_type<event_object_index>().indices().get<by_id>().rbegin();
create_betting_market_group({{"en", "Moneyline"}}, bruins_vs_penguins.id, betting_market_rules.id, asset_id_type(), false, 0);
generate_blocks(1);
const betting_market_group_object& bruins_penguins_moneyline_betting_markets = *db.get_index_type<betting_market_group_object_index>().indices().get<by_id>().rbegin();
create_betting_market(bruins_penguins_moneyline_betting_markets.id, {{"en", "Boston Bruins win"}});
generate_blocks(1);
const betting_market_object& bruins_win_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin();
create_betting_market(bruins_penguins_moneyline_betting_markets.id, {{"en", "Pittsburgh Penguins win"}});
generate_blocks(1);
const betting_market_object& penguins_win_market = *db.get_index_type<betting_market_object_index>().indices().get<by_id>().rbegin();
(void)bruins_win_market; (void)penguins_win_market;
// check the initial state
BOOST_CHECK(capitals_vs_blackhawks.get_status() == event_status::upcoming);
BOOST_CHECK(bruins_vs_penguins.get_status() == event_status::upcoming);
BOOST_TEST_MESSAGE("setting the capitals_vs_blackhawks event to in-progress, leaving bruins_vs_penguins in upcoming");
update_event(capitals_vs_blackhawks.id, _status = event_status::in_progress);
generate_blocks(1);
BOOST_CHECK(capitals_vs_blackhawks.get_status() == event_status::in_progress);
BOOST_CHECK(moneyline_betting_markets.get_status() == betting_market_group_status::in_play);
BOOST_CHECK(bruins_vs_penguins.get_status() == event_status::upcoming);
BOOST_CHECK(bruins_penguins_moneyline_betting_markets.get_status() == betting_market_group_status::upcoming);
BOOST_CHECK(bruins_win_market.get_status() == betting_market_status::unresolved);
BOOST_CHECK(penguins_win_market.get_status() == betting_market_status::unresolved);
BOOST_TEST_MESSAGE("setting the capitals_vs_blackhawks event to finished");
update_event(capitals_vs_blackhawks.id, _status = event_status::finished);
generate_blocks(1);
BOOST_CHECK(capitals_vs_blackhawks.get_status() == event_status::finished);
BOOST_CHECK(moneyline_betting_markets.get_status() == betting_market_group_status::closed);
BOOST_CHECK(capitals_win_market.get_status() == betting_market_status::unresolved);
BOOST_CHECK(blackhawks_win_market.get_status() == betting_market_status::unresolved);
BOOST_CHECK(bruins_vs_penguins.get_status() == event_status::upcoming);
BOOST_CHECK(bruins_penguins_moneyline_betting_markets.get_status() == betting_market_group_status::upcoming);
BOOST_CHECK(bruins_win_market.get_status() == betting_market_status::unresolved);
BOOST_CHECK(penguins_win_market.get_status() == betting_market_status::unresolved);
BOOST_TEST_MESSAGE("setting the capitals_vs_blackhawks event to canceled");
update_event(capitals_vs_blackhawks.id, _status = event_status::canceled);
generate_blocks(1);
BOOST_CHECK(bruins_vs_penguins.get_status() == event_status::upcoming);
BOOST_CHECK(bruins_penguins_moneyline_betting_markets.get_status() == betting_market_group_status::upcoming);
BOOST_CHECK(bruins_win_market.get_status() == betting_market_status::unresolved);
BOOST_CHECK(penguins_win_market.get_status() == betting_market_status::unresolved);
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_SUITE_END()
@ -782,7 +1430,7 @@ BOOST_AUTO_TEST_CASE( win )
GET_ACTOR(alice);
GET_ACTOR(bob);
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage();
uint32_t rake_value;
//rake_value = (-100 + 1100 - 1100) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
// alice starts with 10000, pays 100 (bet), wins 1100, then pays 1100 (bet), wins 0
@ -808,7 +1456,7 @@ BOOST_AUTO_TEST_CASE( not_win )
GET_ACTOR(alice);
GET_ACTOR(bob);
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage();
uint32_t rake_value = (-100 - 1100 + 2200) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
// alice starts with 10000, pays 100 (bet), wins 0, then pays 1100 (bet), wins 2200
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
@ -925,7 +1573,7 @@ BOOST_AUTO_TEST_CASE(event_group_update_test)
generate_blocks(1);
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage();
uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value);
@ -973,7 +1621,7 @@ BOOST_AUTO_TEST_CASE(event_update_test)
generate_blocks(1);
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage();
uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value);
@ -1037,7 +1685,7 @@ BOOST_AUTO_TEST_CASE(betting_market_group_update_test)
generate_blocks(1);
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage();
uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value);
@ -1074,7 +1722,7 @@ BOOST_AUTO_TEST_CASE(betting_market_update_test)
{blackhawks_win_market.id, betting_market_resolution_type::not_win}});
generate_blocks(1);
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage();
uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value);
@ -1141,8 +1789,13 @@ BOOST_AUTO_TEST_CASE(event_driven_standard_progression_1_with_delay)
{
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 60 /* seconds */);
graphene::bookie::bookie_api bookie_api(app);
// save the event id for checking after it is deleted
// save the ids for checking after it is deleted
event_id_type capitals_vs_blackhawks_id = capitals_vs_blackhawks.id;
betting_market_group_id_type moneyline_betting_markets_id = moneyline_betting_markets.id;
betting_market_id_type capitals_win_market_id = capitals_win_market.id;
betting_market_id_type blackhawks_win_market_id = blackhawks_win_market.id;
BOOST_TEST_MESSAGE("verify everything is in the correct initial state");
BOOST_CHECK(capitals_vs_blackhawks.get_status() == event_status::upcoming);
@ -1163,17 +1816,31 @@ BOOST_AUTO_TEST_CASE(event_driven_standard_progression_1_with_delay)
{{capitals_win_market.id, betting_market_resolution_type::win},
{blackhawks_win_market.id, betting_market_resolution_type::not_win}});
generate_blocks(1);
// it should be waiting 60 seconds before it settles
BOOST_CHECK(capitals_vs_blackhawks.get_status() == event_status::finished);
BOOST_CHECK(moneyline_betting_markets.get_status() == betting_market_group_status::graded);
BOOST_CHECK(capitals_win_market.get_status() == betting_market_status::graded);
BOOST_CHECK(capitals_win_market.resolution == betting_market_resolution_type::win);
BOOST_CHECK(blackhawks_win_market.get_status() == betting_market_status::graded);
BOOST_CHECK(blackhawks_win_market.resolution == betting_market_resolution_type::not_win);
generate_blocks(60);
// as soon as a block is generated, the betting market group will settle, and the market
// and group will cease to exist. The event should transition to "settled", then
// removed.
fc::variants objects_from_bookie = bookie_api.get_objects({capitals_vs_blackhawks_id});
fc::variants objects_from_bookie = bookie_api.get_objects({capitals_vs_blackhawks_id,
moneyline_betting_markets_id,
capitals_win_market_id,
blackhawks_win_market_id});
idump((objects_from_bookie));
BOOST_CHECK_EQUAL(objects_from_bookie[0]["status"].as<std::string>(), "settled");
BOOST_CHECK_EQUAL(objects_from_bookie[1]["status"].as<std::string>(), "settled");
BOOST_CHECK_EQUAL(objects_from_bookie[2]["status"].as<std::string>(), "settled");
BOOST_CHECK_EQUAL(objects_from_bookie[2]["resolution"].as<std::string>(), "win");
BOOST_CHECK_EQUAL(objects_from_bookie[3]["status"].as<std::string>(), "settled");
BOOST_CHECK_EQUAL(objects_from_bookie[3]["resolution"].as<std::string>(), "not_win");
} FC_LOG_AND_RETHROW()
}
@ -1837,7 +2504,7 @@ BOOST_FIXTURE_TEST_CASE( another_event_group_update_test, database_fixture)
{blackhawks_win_market.id, betting_market_resolution_type::not_win}});
generate_blocks(1);
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage();
uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value);
@ -1857,7 +2524,7 @@ BOOST_AUTO_TEST_CASE( wimbledon_2017_gentelmen_singles_sf_test )
CREATE_TENNIS_BETTING_MARKET();
generate_blocks(1);
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage();
transfer(account_id_type(), alice_id, asset(10000000));
transfer(account_id_type(), bob_id, asset(10000000));
@ -1918,7 +2585,7 @@ BOOST_AUTO_TEST_CASE( wimbledon_2017_gentelmen_singles_final_test )
ACTORS( (alice)(bob) );
CREATE_TENNIS_BETTING_MARKET();
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage();
transfer(account_id_type(), alice_id, asset(10000000));
transfer(account_id_type(), bob_id, asset(10000000));
@ -1975,6 +2642,10 @@ BOOST_AUTO_TEST_SUITE_END()
boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
std::srand(time(NULL));
std::cout << "Random number generator seeded to " << time(NULL) << std::endl;
// betting operations don't take effect until HARDFORK 1000
GRAPHENE_TESTING_GENESIS_TIMESTAMP = HARDFORK_1000_TIME.sec_since_epoch();
return nullptr;
}

View file

@ -1150,6 +1150,45 @@ void database_fixture::process_operation_by_witnesses(operation op)
}
}
void database_fixture::process_operation_by_committee(operation op)
{
const vector<committee_member_id_type>& active_committee_members = db.get_global_properties().active_committee_members;
proposal_create_operation proposal_op;
proposal_op.fee_paying_account = (*active_committee_members.begin())(db).committee_member_account;
proposal_op.proposed_ops.emplace_back(op);
proposal_op.expiration_time = db.head_block_time() + fc::days(1);
signed_transaction tx;
tx.operations.push_back(proposal_op);
set_expiration(db, tx);
sign(tx, init_account_priv_key);
processed_transaction processed_tx = db.push_transaction(tx);
proposal_id_type proposal_id = processed_tx.operation_results[0].get<object_id_type>();
for (const committee_member_id_type& committee_member_id : active_committee_members)
{
const committee_member_object& committee_member = committee_member_id(db);
const account_object& committee_member_account = committee_member.committee_member_account(db);
proposal_update_operation pup;
pup.proposal = proposal_id;
pup.fee_paying_account = committee_member_account.id;
pup.active_approvals_to_add.insert(committee_member_account.id);
signed_transaction tx;
tx.operations.push_back( pup );
set_expiration( db, tx );
sign(tx, init_account_priv_key);
db.push_transaction(tx, ~0);
const auto& proposal_idx = db.get_index_type<proposal_index>().indices().get<by_id>();
if (proposal_idx.find(proposal_id) == proposal_idx.end())
break;
}
}
void database_fixture::force_operation_by_witnesses(operation op)
{
const chain_parameters& params = db.get_global_properties().parameters;

View file

@ -299,6 +299,7 @@ struct database_fixture {
asset_id_type dividend_payout_asset_type) const;
vector< operation_history_object > get_operation_history( account_id_type account_id )const;
void process_operation_by_witnesses(operation op);
void process_operation_by_committee(operation op);
void force_operation_by_witnesses(operation op);
void set_is_proposed_trx(operation op);
const sport_object& create_sport(internationalized_string_type name);

View file

@ -1287,6 +1287,7 @@ BOOST_AUTO_TEST_CASE( test_update_dividend_interval )
throw;
}
}
BOOST_AUTO_TEST_CASE( test_basic_dividend_distribution )
{
using namespace graphene;
@ -1400,7 +1401,8 @@ BOOST_AUTO_TEST_CASE( test_basic_dividend_distribution )
const operation_history_object& history_object = std::prev(account_history_range.second)->operation_id(db);
const asset_dividend_distribution_operation& distribution_operation = history_object.op.get<asset_dividend_distribution_operation>();
BOOST_CHECK(distribution_operation.account_id == destination_account.id);
BOOST_CHECK(distribution_operation.amounts.find(expected_payout) != distribution_operation.amounts.end());
BOOST_CHECK(std::find(distribution_operation.amounts.begin(), distribution_operation.amounts.end(), expected_payout)
!= distribution_operation.amounts.end());
};
BOOST_TEST_MESSAGE("Verifying the payouts");
@ -1420,6 +1422,178 @@ BOOST_AUTO_TEST_CASE( test_basic_dividend_distribution )
throw;
}
}
BOOST_AUTO_TEST_CASE( test_basic_dividend_distribution_to_core_asset )
{
using namespace graphene;
try {
BOOST_TEST_MESSAGE("Creating test accounts");
create_account("alice");
create_account("bob");
create_account("carol");
create_account("dave");
create_account("frank");
BOOST_TEST_MESSAGE("Creating test asset");
{
asset_create_operation creator;
creator.issuer = account_id_type();
creator.fee = asset();
creator.symbol = "TEST";
creator.common_options.max_supply = 100000000;
creator.precision = 2;
creator.common_options.market_fee_percent = GRAPHENE_MAX_MARKET_FEE_PERCENT/100; /*1%*/
creator.common_options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK;
creator.common_options.flags = charge_market_fee;
creator.common_options.core_exchange_rate = price({asset(2),asset(1,asset_id_type(1))});
trx.operations.push_back(std::move(creator));
set_expiration(db, trx);
PUSH_TX( db, trx, ~0 );
trx.operations.clear();
}
generate_block();
const auto& dividend_holder_asset_object = asset_id_type(0)(db);
const auto& dividend_data = dividend_holder_asset_object.dividend_data(db);
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(db);
const account_object& alice = get_account("alice");
const account_object& bob = get_account("bob");
const account_object& carol = get_account("carol");
const account_object& dave = get_account("dave");
const account_object& frank = get_account("frank");
const auto& test_asset_object = get_asset("TEST");
auto issue_asset_to_account = [&](const asset_object& asset_to_issue, const account_object& destination_account, int64_t amount_to_issue)
{
asset_issue_operation op;
op.issuer = asset_to_issue.issuer;
op.asset_to_issue = asset(amount_to_issue, asset_to_issue.id);
op.issue_to_account = destination_account.id;
trx.operations.push_back( op );
set_expiration(db, trx);
PUSH_TX( db, trx, ~0 );
trx.operations.clear();
};
auto verify_pending_balance = [&](const account_object& holder_account_obj, const asset_object& payout_asset_obj, int64_t expected_balance) {
int64_t pending_balance = get_dividend_pending_payout_balance(dividend_holder_asset_object.id,
holder_account_obj.id,
payout_asset_obj.id);
BOOST_CHECK_EQUAL(pending_balance, expected_balance);
};
auto advance_to_next_payout_time = [&]() {
// Advance to the next upcoming payout time
BOOST_REQUIRE(dividend_data.options.next_payout_time);
fc::time_point_sec next_payout_scheduled_time = *dividend_data.options.next_payout_time;
idump((next_payout_scheduled_time));
// generate blocks up to the next scheduled time
generate_blocks(next_payout_scheduled_time);
// if the scheduled time fell on a maintenance interval, then we should have paid out.
// if not, we need to advance to the next maintenance interval to trigger the payout
if (dividend_data.options.next_payout_time)
{
// we know there was a next_payout_time set when we entered this, so if
// it has been cleared, we must have already processed payouts, no need to
// further advance time.
BOOST_REQUIRE(dividend_data.options.next_payout_time);
if (*dividend_data.options.next_payout_time == next_payout_scheduled_time)
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
generate_block(); // get the maintenance skip slots out of the way
}
idump((db.head_block_time()));
};
// the first test will be testing pending balances, so we need to hit a
// maintenance interval that isn't the payout interval. Payout is
// every 3 days, maintenance interval is every 1 day.
advance_to_next_payout_time();
// Set up the first test, issue alice, bob, and carol, and dave each 1/4 of the total
// supply of the core asset.
// Then deposit 400 TEST in the distribution account, and see that they
// each are credited 100 TEST.
transfer( committee_account(db), alice, asset( 250000000000000 ) );
transfer( committee_account(db), bob, asset( 250000000000000 ) );
transfer( committee_account(db), carol, asset( 250000000000000 ) );
transfer( committee_account(db), dave, asset( 250000000000000 ) );
BOOST_TEST_MESSAGE("Issuing 300 TEST to the dividend account");
issue_asset_to_account(test_asset_object, dividend_distribution_account, 40000);
generate_block();
BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" );
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
generate_block(); // get the maintenance skip slots out of the way
verify_pending_balance(alice, test_asset_object, 10000);
verify_pending_balance(bob, test_asset_object, 10000);
verify_pending_balance(carol, test_asset_object, 10000);
verify_pending_balance(dave, test_asset_object, 10000);
// For the second test, issue dave more than the other two, so it's
// alice: 1/5 CORE, bob: 1/5 CORE, carol: 1/5 CORE, dave: 2/5 CORE
// Then deposit 500 TEST in the distribution account, and see that alice
// bob, and carol are credited with 100 TEST, and dave gets 200 TEST
BOOST_TEST_MESSAGE("Issuing dave twice as much of the holder asset");
transfer( alice, dave, asset( 50000000000000 ) );
transfer( bob, dave, asset( 50000000000000 ) );
transfer( carol, dave, asset( 50000000000000 ) );
issue_asset_to_account(test_asset_object, dividend_distribution_account, 50000); // 500 at two digits of precision
BOOST_TEST_MESSAGE( "Generating blocks until next maintenance interval" );
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
generate_block(); // get the maintenance skip slots out of the way
verify_pending_balance(alice, test_asset_object, 20000);
verify_pending_balance(bob, test_asset_object, 20000);
verify_pending_balance(carol, test_asset_object, 20000);
verify_pending_balance(dave, test_asset_object, 30000);
fc::time_point_sec old_next_payout_scheduled_time = *dividend_data.options.next_payout_time;
advance_to_next_payout_time();
BOOST_REQUIRE_MESSAGE(dividend_data.options.next_payout_time, "No new payout was scheduled");
BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time != *dividend_data.options.next_payout_time,
"New payout was scheduled for the same time as the last payout");
BOOST_CHECK_MESSAGE(old_next_payout_scheduled_time + *dividend_data.options.payout_interval == *dividend_data.options.next_payout_time,
"New payout was not scheduled for the expected time");
auto verify_dividend_payout_operations = [&](const account_object& destination_account, const asset& expected_payout)
{
BOOST_TEST_MESSAGE("Verifying the virtual op was created");
const account_transaction_history_index& hist_idx = db.get_index_type<account_transaction_history_index>();
auto account_history_range = hist_idx.indices().get<by_seq>().equal_range(boost::make_tuple(destination_account.id));
BOOST_REQUIRE(account_history_range.first != account_history_range.second);
const operation_history_object& history_object = std::prev(account_history_range.second)->operation_id(db);
const asset_dividend_distribution_operation& distribution_operation = history_object.op.get<asset_dividend_distribution_operation>();
BOOST_CHECK(distribution_operation.account_id == destination_account.id);
BOOST_CHECK(std::find(distribution_operation.amounts.begin(), distribution_operation.amounts.end(), expected_payout)
!= distribution_operation.amounts.end());
};
BOOST_TEST_MESSAGE("Verifying the payouts");
BOOST_CHECK_EQUAL(get_balance(alice, test_asset_object), 20000);
verify_dividend_payout_operations(alice, asset(20000, test_asset_object.id));
verify_pending_balance(alice, test_asset_object, 0);
BOOST_CHECK_EQUAL(get_balance(bob, test_asset_object), 20000);
verify_dividend_payout_operations(bob, asset(20000, test_asset_object.id));
verify_pending_balance(bob, test_asset_object, 0);
BOOST_CHECK_EQUAL(get_balance(carol, test_asset_object), 20000);
verify_dividend_payout_operations(carol, asset(20000, test_asset_object.id));
verify_pending_balance(carol, test_asset_object, 0);
BOOST_CHECK_EQUAL(get_balance(dave, test_asset_object), 30000);
verify_dividend_payout_operations(dave, asset(30000, test_asset_object.id));
verify_pending_balance(dave, test_asset_object, 0);
} catch(fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE( test_dividend_distribution_interval )
{
using namespace graphene;

View file

@ -1998,7 +1998,7 @@ BOOST_FIXTURE_TEST_CASE( simple, database_fixture )
#endif
auto n = db.get_balance(nathan_id, asset_id_type()); wdump(("# nathan's balance") (n));
auto r = db.get_balance(TOURNAMENT_RAKE_FEE_ACCOUNT_ID, asset_id_type()); wdump(("# rake's balance") (r));
auto r = db.get_balance(GRAPHENE_RAKE_FEE_ACCOUNT_ID, asset_id_type()); wdump(("# rake's balance") (r));
#endif
};