Merge branch 'beatrice' into 'master'

Merge beatrice to master 2022-04

See merge request PBSA/peerplays!84
This commit is contained in:
Bobinson K B 2022-04-14 13:18:26 +00:00
commit 6a59d9efba
66 changed files with 3960 additions and 1822 deletions

View file

@ -1,6 +1,5 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -3
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false
@ -12,7 +11,7 @@ AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortLambdasOnASingleLine: None
@ -57,6 +56,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 6
ContinuationIndentWidth: 6
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
@ -69,12 +69,17 @@ IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
- Regex: '.*'
Priority: 1
SortPriority: 0
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentCaseLabels: false
IndentGotoLabels: false
IndentPPDirectives: None
IndentWidth: 3
IndentWrappedFunctionNames: false
@ -110,18 +115,22 @@ SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
SpaceBeforeSquareBrackets: false
Standard: Latest
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 3
UseCRLF: false
UseTab: Never
...

View file

@ -19,7 +19,7 @@ build:
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release ..
- make -j4
- make -j$(nproc)
artifacts:
untracked: true
paths:
@ -49,6 +49,7 @@ test:
dependencies:
- build
script:
- ./build/libraries/fc/tests/all_tests
- ./build/tests/betting_test --log_level=message
- ./build/tests/chain_test --log_level=message
- ./build/tests/cli_test --log_level=message

2
.gitmodules vendored
View file

@ -4,6 +4,6 @@
ignore = dirty
[submodule "libraries/fc"]
path = libraries/fc
url = https://github.com/peerplays-network/peerplays-fc.git
url = https://gitlab.com/PBSA/tools-libs/peerplays-fc.git
branch = latest-fc
ignore = dirty

View file

@ -83,7 +83,6 @@ LIST(APPEND BOOST_COMPONENTS thread
system
filesystem
program_options
signals
serialization
chrono
unit_test_framework

102
README.md
View file

@ -2,95 +2,81 @@ Intro for new developers and witnesses
------------------------
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.
# Building on Ubuntu 18.04 LTS and Installation Instructions
The following dependencies were necessary for a clean install of Ubuntu 18.04 LTS:
```
sudo apt-get install autoconf bash build-essential ca-certificates cmake \
doxygen git graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev \
libreadline-dev libssl-dev libtool libzmq3-dev locales ntp pkg-config \
wget
# Building and Installation Instructions
Officially supported OS is Ubuntu 20.04.
Following dependencies are needed for a clean install of Ubuntu 20.04:
```
## Build Boost 1.67.0
```
mkdir $HOME/src
cd $HOME/src
export BOOST_ROOT=$HOME/src/boost_1_67_0
sudo apt-get update
sudo apt-get install -y autotools-dev build-essential libbz2-dev libicu-dev python-dev
wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download'\
-O boost_1_67_0.tar.bz2
tar xjf boost_1_67_0.tar.bz2
cd boost_1_67_0/
./bootstrap.sh "--prefix=$BOOST_ROOT"
./b2 install
sudo apt-get install \
apt-utils autoconf bash build-essential ca-certificates clang-format cmake
dnsutils doxygen expect git graphviz libboost-all-dev libbz2-dev \
libcurl4-openssl-dev libncurses-dev libreadline-dev libsnappy-dev \
libssl-dev libtool libzip-dev libzmq3-dev locales mc nano net-tools ntp \
openssh-server pkg-config perl python3 python3-jinja2 sudo wget
```
## Building Peerplays
```
mkdir $HOME/src
cd $HOME/src
export BOOST_ROOT=$HOME/src/boost_1_67_0
git clone https://github.com/peerplays-network/peerplays.git
git clone https://gitlab.com/PBSA/peerplays.git
cd peerplays
git submodule update --init --recursive
# If you want to build Mainnet node
cmake -DBOOST_ROOT="$BOOST_ROOT" -DCMAKE_BUILD_TYPE=Release
cmake -DCMAKE_BUILD_TYPE=Release
# If you want to build Testnet node
cmake -DBOOST_ROOT="$BOOST_ROOT" -DCMAKE_BUILD_TYPE=Release -DBUILD_PEERPLAYS_TESTNET=1
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_PEERPLAYS_TESTNET=1
# Update -j flag depending on your current system specs;
# Recommended 4GB of RAM per 1 CPU core
# make -j2 for 8GB RAM
# make -j4 for 16GB RAM
# make -j8 for 32GB RAM
make -j$(nproc)
make install # this can install the executable files under /usr/local
```
## Docker image
## Docker images
Install docker, and add current user to docker group.
```
# Install docker
sudo apt install docker.io
# Add current user to docker group
sudo usermod -a -G docker $USER
# You need to restart your shell session, to apply group membership
# Type 'groups' to verify that you are a member of a docker group
# Build docker image (from the project root, must be a docker group member)
docker build -t peerplays .
# Start docker image
docker start peerplays
# Exposed ports
# # rpc service:
# EXPOSE 8090
# # p2p service:
# EXPOSE 1776
```
Rest of the instructions on starting the chain remains same.
### Official docker image for Peerplas Mainnet
```
docker pull datasecuritynode/peerplays:latest
```
### Building docker image manually
```
# Build docker image (from the project root, must be a docker group member)
docker build -t peerplays .
```
### Start docker image
```
docker start peerplays
```
Rest of the instructions on starting the chain remains same.
Starting A Peerplays Node
-----------------
For Ubuntu 14.04 LTS and up users, see
[this](https://github.com/cryptonomex/graphene/wiki/build-ubuntu) and
then proceed with:
git clone https://github.com/peerplays-network/peerplays.git
cd peerplays
git submodule update --init --recursive
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.
$ vi witness_node_data_dir/config.ini

View file

@ -3,3 +3,4 @@
find ./libraries/app -regex ".*[c|h]pp" | xargs clang-format -i
find ./libraries/chain/hardfork.d -regex ".*hf" | xargs clang-format -i
find ./libraries/plugins/peerplays_sidechain -regex ".*[c|h]pp" | xargs clang-format -i
find ./programs/cli_wallet -regex ".*[c|h]pp" | xargs clang-format -i

2
docs

@ -1 +1 @@
Subproject commit 8df8f66389853df73ab8f6dd73981be2a6957df8
Subproject commit 1e924950c2f92b166c34ceb294e8b8c4997a6c4e

View file

@ -15,7 +15,7 @@ add_library( graphene_app
#target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_utilities graphene_debug_witness )
target_link_libraries( graphene_app
PUBLIC graphene_net graphene_utilities
graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_bookie graphene_debug_witness graphene_elasticsearch graphene_es_objects graphene_generate_genesis graphene_market_history )
graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_bookie graphene_debug_witness graphene_elasticsearch graphene_es_objects graphene_generate_genesis graphene_market_history peerplays_sidechain )
target_include_directories( graphene_app
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"

View file

@ -106,6 +106,10 @@ void login_api::enable_api(const std::string &api_name) {
// can only enable this API if the plugin was loaded
if (_app.get_plugin("affiliate_stats"))
_affiliate_stats_api = std::make_shared<graphene::affiliate_stats::affiliate_stats_api>(std::ref(_app));
} else if (api_name == "sidechain_api") {
// can only enable this API if the plugin was loaded
if (_app.get_plugin("peerplays_sidechain"))
_sidechain_api = std::make_shared<graphene::peerplays_sidechain::sidechain_api>(std::ref(_app));
}
return;
}
@ -310,6 +314,11 @@ fc::api<graphene::affiliate_stats::affiliate_stats_api> login_api::affiliate_sta
return *_affiliate_stats_api;
}
fc::api<graphene::peerplays_sidechain::sidechain_api> login_api::sidechain() const {
FC_ASSERT(_sidechain_api);
return *_sidechain_api;
}
vector<order_history_object> history_api::get_fill_order_history(std::string asset_a, std::string asset_b, uint32_t limit) const {
FC_ASSERT(_app.chain_database());
const auto &db = *_app.chain_database();

View file

@ -401,6 +401,7 @@ public:
wild_access.allowed_apis.push_back("crypto_api");
wild_access.allowed_apis.push_back("bookie_api");
wild_access.allowed_apis.push_back("affiliate_stats_api");
wild_access.allowed_apis.push_back("sidechain_api");
_apiaccess.permission_map["*"] = wild_access;
}

View file

@ -52,6 +52,22 @@ template class fc::api<graphene::app::database_api>;
namespace graphene { namespace app {
template <class T>
optional<T> maybe_id(const string &name_or_id) {
if (std::isdigit(name_or_id.front())) {
try {
return fc::variant(name_or_id, 1).as<T>(1);
} catch (const fc::exception &) { // not an ID
}
}
return optional<T>();
}
std::string object_id_to_string(object_id_type id) {
std::string object_id = fc::to_string(id.space()) + "." + fc::to_string(id.type()) + "." + fc::to_string(id.instance());
return object_id;
}
class database_api_impl : public std::enable_shared_from_this<database_api_impl> {
public:
database_api_impl(graphene::chain::database &db);
@ -146,18 +162,22 @@ public:
// Witnesses
vector<optional<witness_object>> get_witnesses(const vector<witness_id_type> &witness_ids) const;
fc::optional<witness_object> get_witness_by_account_id(account_id_type account) const;
fc::optional<witness_object> get_witness_by_account(const std::string account_id_or_name) const;
map<string, witness_id_type> lookup_witness_accounts(const string &lower_bound_name, uint32_t limit) const;
uint64_t get_witness_count() const;
// Committee members
vector<optional<committee_member_object>> get_committee_members(const vector<committee_member_id_type> &committee_member_ids) const;
fc::optional<committee_member_object> get_committee_member_by_account_id(account_id_type account) const;
fc::optional<committee_member_object> get_committee_member_by_account(const std::string account_id_or_name) const;
map<string, committee_member_id_type> lookup_committee_member_accounts(const string &lower_bound_name, uint32_t limit) const;
uint64_t get_committee_member_count() const;
// SON members
vector<optional<son_object>> get_sons(const vector<son_id_type> &son_ids) const;
fc::optional<son_object> get_son_by_account(account_id_type account) const;
fc::optional<son_object> get_son_by_account_id(account_id_type account) const;
fc::optional<son_object> get_son_by_account(const std::string account_id_or_name) const;
map<string, son_id_type> lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const;
uint64_t get_son_count() const;
@ -173,8 +193,32 @@ public:
fc::optional<sidechain_address_object> get_sidechain_address_by_account_and_sidechain(account_id_type account, sidechain_type sidechain) const;
uint64_t get_sidechain_addresses_count() const;
// Workers
vector<optional<worker_object>> get_workers(const vector<worker_id_type> &witness_ids) const;
vector<worker_object> get_workers_by_account_id(account_id_type account) const;
vector<worker_object> get_workers_by_account(const std::string account_id_or_name) const;
map<string, worker_id_type> lookup_worker_accounts(const string &lower_bound_name, uint32_t limit) const;
uint64_t get_worker_count() const;
// Votes
vector<variant> lookup_vote_ids(const vector<vote_id_type> &votes) const;
vector<vote_id_type> get_votes_ids(const string &account_name_or_id) const;
template <typename IndexType, typename Tag>
vector<variant> get_votes_objects(const vector<vote_id_type> &votes, unsigned int variant_max_depth = 1) const {
static_assert(std::is_base_of<index, IndexType>::value, "Type must be an index type");
vector<variant> result;
const auto &idx = _db.get_index_type<IndexType>().indices().template get<Tag>();
for (auto id : votes) {
auto itr = idx.find(id);
if (itr != idx.end())
result.emplace_back(variant(*itr, variant_max_depth));
}
return result;
}
votes_info get_votes(const string &account_name_or_id) const;
vector<account_object> get_voters_by_id(const vote_id_type &vote_id) const;
voters_info get_voters(const string &account_name_or_id) const;
// Authority / validation
std::string get_transaction_hex(const signed_transaction &trx) const;
@ -234,6 +278,9 @@ public:
vector<offer_history_object> get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const;
vector<offer_history_object> get_offer_history_by_bidder(const offer_history_id_type lower_id, const account_id_type bidder_account_id, uint32_t limit) const;
// Account Role
vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const;
uint32_t api_limit_get_lower_bound_symbol = 100;
uint32_t api_limit_get_limit_orders = 300;
uint32_t api_limit_get_limit_orders_by_account = 101;
@ -242,12 +289,11 @@ public:
uint32_t api_limit_lookup_accounts = 1000;
uint32_t api_limit_lookup_witness_accounts = 1000;
uint32_t api_limit_lookup_committee_member_accounts = 1000;
uint32_t api_limit_lookup_son_accounts = 1000;
uint32_t api_limit_lookup_worker_accounts = 1000;
uint32_t api_limit_get_trade_history = 100;
uint32_t api_limit_get_trade_history_by_sequence = 100;
// Account Role
vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const;
//private:
const account_object *get_account_from_string(const std::string &name_or_id,
bool throw_if_not_found = true) const;
@ -1552,20 +1598,6 @@ vector<optional<witness_object>> database_api::get_witnesses(const vector<witnes
return my->get_witnesses(witness_ids);
}
vector<worker_object> database_api::get_workers_by_account(const std::string account_id_or_name) const {
const auto &idx = my->_db.get_index_type<worker_index>().indices().get<by_account>();
const account_id_type account = my->get_account_from_string(account_id_or_name)->id;
auto itr = idx.find(account);
vector<worker_object> result;
if (itr != idx.end() && itr->worker_account == account) {
result.emplace_back(*itr);
++itr;
}
return result;
}
vector<optional<witness_object>> database_api_impl::get_witnesses(const vector<witness_id_type> &witness_ids) const {
vector<optional<witness_object>> result;
result.reserve(witness_ids.size());
@ -1578,17 +1610,25 @@ vector<optional<witness_object>> database_api_impl::get_witnesses(const vector<w
return result;
}
fc::optional<witness_object> database_api::get_witness_by_account_id(account_id_type account) const {
return my->get_witness_by_account_id(account);
}
fc::optional<witness_object> database_api_impl::get_witness_by_account_id(account_id_type account) const {
const auto &idx = _db.get_index_type<witness_index>().indices().get<by_account>();
auto itr = idx.find(account);
if (itr != idx.end())
return *itr;
return {};
}
fc::optional<witness_object> database_api::get_witness_by_account(const std::string account_id_or_name) const {
return my->get_witness_by_account(account_id_or_name);
}
fc::optional<witness_object> database_api_impl::get_witness_by_account(const std::string account_id_or_name) const {
const auto &idx = _db.get_index_type<witness_index>().indices().get<by_account>();
const account_id_type account = get_account_from_string(account_id_or_name)->id;
auto itr = idx.find(account);
if (itr != idx.end())
return *itr;
return {};
return get_witness_by_account_id(account);
}
map<string, witness_id_type> database_api::lookup_witness_accounts(const string &lower_bound_name, uint32_t limit) const {
@ -1649,17 +1689,25 @@ vector<optional<committee_member_object>> database_api_impl::get_committee_membe
return result;
}
fc::optional<committee_member_object> database_api::get_committee_member_by_account_id(account_id_type account) const {
return my->get_committee_member_by_account_id(account);
}
fc::optional<committee_member_object> database_api_impl::get_committee_member_by_account_id(account_id_type account) const {
const auto &idx = _db.get_index_type<committee_member_index>().indices().get<by_account>();
auto itr = idx.find(account);
if (itr != idx.end())
return *itr;
return {};
}
fc::optional<committee_member_object> database_api::get_committee_member_by_account(const std::string account_id_or_name) const {
return my->get_committee_member_by_account(account_id_or_name);
}
fc::optional<committee_member_object> database_api_impl::get_committee_member_by_account(const std::string account_id_or_name) const {
const auto &idx = _db.get_index_type<committee_member_index>().indices().get<by_account>();
const account_id_type account = get_account_from_string(account_id_or_name)->id;
auto itr = idx.find(account);
if (itr != idx.end())
return *itr;
return {};
return get_committee_member_by_account_id(account);
}
map<string, committee_member_id_type> database_api::lookup_committee_member_accounts(const string &lower_bound_name, uint32_t limit) const {
@ -1690,6 +1738,14 @@ map<string, committee_member_id_type> database_api_impl::lookup_committee_member
return committee_members_by_account_name;
}
uint64_t database_api::get_committee_member_count() const {
return my->get_committee_member_count();
}
uint64_t database_api_impl::get_committee_member_count() const {
return _db.get_index_type<committee_member_index>().indices().size();
}
//////////////////////////////////////////////////////////////////////
// //
// SON members //
@ -1712,11 +1768,11 @@ vector<optional<son_object>> database_api_impl::get_sons(const vector<son_id_typ
return result;
}
fc::optional<son_object> database_api::get_son_by_account(account_id_type account) const {
return my->get_son_by_account(account);
fc::optional<son_object> database_api::get_son_by_account_id(account_id_type account) const {
return my->get_son_by_account_id(account);
}
fc::optional<son_object> database_api_impl::get_son_by_account(account_id_type account) const {
fc::optional<son_object> database_api_impl::get_son_by_account_id(account_id_type account) const {
const auto &idx = _db.get_index_type<son_index>().indices().get<by_account>();
auto itr = idx.find(account);
if (itr != idx.end())
@ -1724,12 +1780,24 @@ fc::optional<son_object> database_api_impl::get_son_by_account(account_id_type a
return {};
}
fc::optional<son_object> database_api::get_son_by_account(const std::string account_id_or_name) const {
return my->get_son_by_account(account_id_or_name);
}
fc::optional<son_object> database_api_impl::get_son_by_account(const std::string account_id_or_name) const {
const account_id_type account = get_account_from_string(account_id_or_name)->id;
return get_son_by_account_id(account);
}
map<string, son_id_type> database_api::lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const {
return my->lookup_son_accounts(lower_bound_name, limit);
}
map<string, son_id_type> database_api_impl::lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const {
FC_ASSERT(limit <= 1000);
FC_ASSERT(limit <= api_limit_lookup_son_accounts,
"Number of querying accounts can not be greater than ${configured_limit}",
("configured_limit", api_limit_lookup_son_accounts));
const auto &sons_by_id = _db.get_index_type<son_index>().indices().get<by_id>();
// we want to order sons by account name, but that name is in the account object
@ -1875,6 +1943,91 @@ uint64_t database_api_impl::get_sidechain_addresses_count() const {
return _db.get_index_type<sidechain_address_index>().indices().size();
}
//////////////////////////////////////////////////////////////////////
// //
// Workers //
// //
//////////////////////////////////////////////////////////////////////
vector<optional<worker_object>> database_api::get_workers(const vector<worker_id_type> &worker_ids) const {
return my->get_workers(worker_ids);
}
vector<worker_object> database_api::get_workers_by_account_id(account_id_type account) const {
return my->get_workers_by_account_id(account);
}
vector<worker_object> database_api::get_workers_by_account(const std::string account_id_or_name) const {
return my->get_workers_by_account(account_id_or_name);
}
map<string, worker_id_type> database_api::lookup_worker_accounts(const string &lower_bound_name, uint32_t limit) const {
return my->lookup_worker_accounts(lower_bound_name, limit);
}
uint64_t database_api::get_worker_count() const {
return my->get_worker_count();
}
vector<optional<worker_object>> database_api_impl::get_workers(const vector<worker_id_type> &worker_ids) const {
vector<optional<worker_object>> result;
result.reserve(worker_ids.size());
std::transform(worker_ids.begin(), worker_ids.end(), std::back_inserter(result),
[this](worker_id_type id) -> optional<worker_object> {
if (auto o = _db.find(id))
return *o;
return {};
});
return result;
}
vector<worker_object> database_api_impl::get_workers_by_account_id(account_id_type account) const {
const auto &idx = _db.get_index_type<worker_index>().indices().get<by_account>();
auto itr = idx.find(account);
vector<worker_object> result;
if (itr != idx.end() && itr->worker_account == account) {
result.emplace_back(*itr);
++itr;
}
return result;
}
vector<worker_object> database_api_impl::get_workers_by_account(const std::string account_id_or_name) const {
const account_id_type account = get_account_from_string(account_id_or_name)->id;
return get_workers_by_account_id(account);
}
map<string, worker_id_type> database_api_impl::lookup_worker_accounts(const string &lower_bound_name, uint32_t limit) const {
FC_ASSERT(limit <= api_limit_lookup_worker_accounts,
"Number of querying accounts can not be greater than ${configured_limit}",
("configured_limit", api_limit_lookup_worker_accounts));
const auto &workers_by_id = _db.get_index_type<worker_index>().indices().get<by_id>();
// we want to order workers by account name, but that name is in the account object
// so the worker_index doesn't have a quick way to access it.
// get all the names and look them all up, sort them, then figure out what
// records to return. This could be optimized, but we expect the
// number of witnesses to be few and the frequency of calls to be rare
std::map<std::string, worker_id_type> workers_by_account_name;
for (const worker_object &worker : workers_by_id)
if (auto account_iter = _db.find(worker.worker_account))
if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name
workers_by_account_name.insert(std::make_pair(account_iter->name, worker.id));
auto end_iter = workers_by_account_name.begin();
while (end_iter != workers_by_account_name.end() && limit--)
++end_iter;
workers_by_account_name.erase(end_iter, workers_by_account_name.end());
return workers_by_account_name;
}
uint64_t database_api_impl::get_worker_count() const {
return _db.get_index_type<worker_index>().indices().size();
}
//////////////////////////////////////////////////////////////////////
// //
// Votes //
@ -1885,6 +2038,22 @@ vector<variant> database_api::lookup_vote_ids(const vector<vote_id_type> &votes)
return my->lookup_vote_ids(votes);
}
vector<vote_id_type> database_api::get_votes_ids(const string &account_name_or_id) const {
return my->get_votes_ids(account_name_or_id);
}
votes_info database_api::get_votes(const string &account_name_or_id) const {
return my->get_votes(account_name_or_id);
}
vector<account_object> database_api::get_voters_by_id(const vote_id_type &vote_id) const {
return my->get_voters_by_id(vote_id);
}
voters_info database_api::get_voters(const string &account_name_or_id) const {
return my->get_voters(account_name_or_id);
}
vector<variant> database_api_impl::lookup_vote_ids(const vector<vote_id_type> &votes) const {
FC_ASSERT(votes.size() < 1000, "Only 1000 votes can be queried at a time");
@ -1946,6 +2115,261 @@ vector<variant> database_api_impl::lookup_vote_ids(const vector<vote_id_type> &v
return result;
}
vector<vote_id_type> database_api_impl::get_votes_ids(const string &account_name_or_id) const {
vector<vote_id_type> result;
const account_object *account = get_account_from_string(account_name_or_id);
//! Iterate throug votes and fill vector
for (const auto &vote : account->options.votes) {
result.emplace_back(vote);
}
return result;
}
votes_info database_api_impl::get_votes(const string &account_name_or_id) const {
votes_info result;
const auto &votes_ids = get_votes_ids(account_name_or_id);
const auto &committee_ids = get_votes_objects<committee_member_index, by_vote_id>(votes_ids);
const auto &witness_ids = get_votes_objects<witness_index, by_vote_id>(votes_ids);
const auto &for_worker_ids = get_votes_objects<worker_index, by_vote_for>(votes_ids);
const auto &against_worker_ids = get_votes_objects<worker_index, by_vote_against>(votes_ids);
const auto &son_ids = get_votes_objects<son_index, by_vote_id>(votes_ids, 5);
//! Fill votes info
if (!committee_ids.empty()) {
vector<votes_info_object> votes_for_committee_members;
votes_for_committee_members.reserve(committee_ids.size());
for (const auto &committee : committee_ids) {
const auto &committee_obj = committee.as<committee_member_object>(2);
votes_for_committee_members.emplace_back(votes_info_object{committee_obj.vote_id, committee_obj.id});
}
result.votes_for_committee_members = std::move(votes_for_committee_members);
}
if (!witness_ids.empty()) {
vector<votes_info_object> votes_for_witnesses;
votes_for_witnesses.reserve(witness_ids.size());
for (const auto &witness : witness_ids) {
const auto &witness_obj = witness.as<witness_object>(2);
votes_for_witnesses.emplace_back(votes_info_object{witness_obj.vote_id, witness_obj.id});
}
result.votes_for_witnesses = std::move(votes_for_witnesses);
}
if (!for_worker_ids.empty()) {
vector<votes_info_object> votes_for_workers;
votes_for_workers.reserve(for_worker_ids.size());
for (const auto &for_worker : for_worker_ids) {
const auto &for_worker_obj = for_worker.as<worker_object>(2);
votes_for_workers.emplace_back(votes_info_object{for_worker_obj.vote_for, for_worker_obj.id});
}
result.votes_for_workers = std::move(votes_for_workers);
}
if (!against_worker_ids.empty()) {
vector<votes_info_object> votes_against_workers;
votes_against_workers.reserve(against_worker_ids.size());
for (const auto &against_worker : against_worker_ids) {
const auto &against_worker_obj = against_worker.as<worker_object>(2);
votes_against_workers.emplace_back(votes_info_object{against_worker_obj.vote_against, against_worker_obj.id});
}
result.votes_against_workers = std::move(votes_against_workers);
}
if (!son_ids.empty()) {
vector<votes_info_object> votes_for_sons;
votes_for_sons.reserve(son_ids.size());
for (const auto &son : son_ids) {
const auto &son_obj = son.as<son_object>(6);
votes_for_sons.emplace_back(votes_info_object{son_obj.vote_id, son_obj.id});
}
result.votes_for_sons = std::move(votes_for_sons);
}
return result;
}
vector<account_object> database_api_impl::get_voters_by_id(const vote_id_type &vote_id) const {
vector<account_object> result;
//! We search all accounts that have voted for this vote_id
const auto &account_index = _db.get_index_type<graphene::chain::account_index>().indices().get<by_id>();
for (const auto &account : account_index) {
if (account.options.votes.count(vote_id) != 0)
result.emplace_back(account);
}
return result;
}
voters_info database_api_impl::get_voters(const string &account_name_or_id) const {
voters_info result;
//! Find account name
bool owner_account_found = false;
std::string owner_account_id;
//! Check if we have account by name
const auto &account_object = get_account_by_name(account_name_or_id);
if (account_object) {
//! It is account
owner_account_id = object_id_to_string(account_object->get_id());
owner_account_found = true;
} else {
//! Check if we have account id
const auto &account_id = maybe_id<account_id_type>(account_name_or_id);
if (account_id) {
//! It may be account id
const auto &account_objects = get_accounts({account_name_or_id});
if (!account_objects.empty()) {
const auto &account_object = account_objects.front();
if (account_object) {
//! It is account object
owner_account_id = object_id_to_string(account_object->get_id());
owner_account_found = true;
}
}
} else {
//! Check if we have committee member id
const auto &committee_member_id = maybe_id<committee_member_id_type>(account_name_or_id);
if (committee_member_id) {
//! It may be committee member id
const auto &committee_member_objects = get_committee_members({*committee_member_id});
if (!committee_member_objects.empty()) {
const auto &committee_member_object = committee_member_objects.front();
if (committee_member_object) {
//! It is committee member object
owner_account_id = object_id_to_string(committee_member_object->committee_member_account);
owner_account_found = true;
}
}
} else {
//! Check if we have witness id
const auto &witness_id = maybe_id<witness_id_type>(account_name_or_id);
if (witness_id) {
//! It may be witness id
const auto &witness_objects = get_witnesses({*witness_id});
if (!witness_objects.empty()) {
const auto &witness_object = witness_objects.front();
if (witness_object) {
//! It is witness object
owner_account_id = object_id_to_string(witness_object->witness_account);
owner_account_found = true;
}
}
} else {
//! Check if we have worker id
const auto &worker_id = maybe_id<worker_id_type>(account_name_or_id);
if (worker_id) {
//! It may be worker id
const auto &worker_objects = get_workers({*worker_id});
if (!worker_objects.empty()) {
const auto &worker_object = worker_objects.front();
if (worker_object) {
//! It is worker object
owner_account_id = object_id_to_string(worker_object->worker_account);
owner_account_found = true;
}
}
} else {
//! Check if we have son id
const auto &son_id = maybe_id<son_id_type>(account_name_or_id);
if (son_id) {
//! It may be son id
const auto &son_objects = get_sons({*son_id});
if (!son_objects.empty()) {
const auto &son_object = son_objects.front();
if (son_object) {
//! It is son object
owner_account_id = object_id_to_string(son_object->son_account);
owner_account_found = true;
}
}
}
}
}
}
}
}
//! We didn't find who it was
if (!owner_account_found)
FC_THROW_EXCEPTION(database_query_exception, "Wrong account_name_or_id: ${account_name_or_id}", ("account_name_or_id", account_name_or_id));
//! Fill voters_info
const auto &committee_member_object = get_committee_member_by_account(owner_account_id);
const auto &witness_object = get_witness_by_account(owner_account_id);
const auto &worker_objects = get_workers_by_account(owner_account_id);
const auto &son_object = get_son_by_account(owner_account_id);
//! Info for committee member voters
if (committee_member_object) {
const auto &committee_member_voters = get_voters_by_id(committee_member_object->vote_id);
voters_info_object voters_for_committee_member;
voters_for_committee_member.vote_id = committee_member_object->vote_id;
voters_for_committee_member.voters.reserve(committee_member_voters.size());
for (const auto &voter : committee_member_voters) {
voters_for_committee_member.voters.emplace_back(voter.get_id());
}
result.voters_for_committee_member = std::move(voters_for_committee_member);
}
//! Info for witness voters
if (witness_object) {
const auto &witness_voters = get_voters_by_id(witness_object->vote_id);
voters_info_object voters_for_witness;
voters_for_witness.vote_id = witness_object->vote_id;
voters_for_witness.voters.reserve(witness_voters.size());
for (const auto &voter : witness_voters) {
voters_for_witness.voters.emplace_back(voter.get_id());
}
result.voters_for_witness = std::move(voters_for_witness);
}
//! Info for worker voters
if (!worker_objects.empty()) {
vector<voters_info_object> voters_for_workers(worker_objects.size());
vector<voters_info_object> voters_against_workers(worker_objects.size());
for (const auto &worker_object : worker_objects) {
voters_info_object voters_for_worker;
const auto &for_worker_voters = get_voters_by_id(worker_object.vote_for);
voters_for_worker.vote_id = worker_object.vote_for;
voters_for_worker.voters.reserve(for_worker_voters.size());
for (const auto &voter : for_worker_voters) {
voters_for_worker.voters.emplace_back(voter.get_id());
}
voters_for_workers.emplace_back(std::move(voters_for_worker));
voters_info_object voters_against_worker;
const auto &against_worker_voters = get_voters_by_id(worker_object.vote_against);
voters_against_worker.vote_id = worker_object.vote_against;
voters_against_worker.voters.reserve(against_worker_voters.size());
for (const auto &voter : against_worker_voters) {
voters_against_worker.voters.emplace_back(voter.get_id());
}
voters_against_workers.emplace_back(std::move(voters_against_worker));
}
result.voters_for_workers = std::move(voters_for_workers);
result.voters_against_workers = std::move(voters_against_workers);
}
//! Info for son voters
if (son_object) {
const auto &son_voters = get_voters_by_id(son_object->vote_id);
voters_info_object voters_for_son;
voters_for_son.vote_id = son_object->vote_id;
voters_for_son.voters.reserve(son_voters.size());
for (const auto &voter : son_voters) {
voters_for_son.voters.emplace_back(voter.get_id());
}
result.voters_for_son = std::move(voters_for_son);
}
return result;
}
//////////////////////////////////////////////////////////////////////
// //
// Authority / validation //

View file

@ -28,16 +28,15 @@
#include <graphene/chain/protocol/confidential.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/net/node.hpp>
#include <graphene/accounts_list/accounts_list_plugin.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
#include <graphene/elasticsearch/elasticsearch_plugin.hpp>
#include <graphene/affiliate_stats/affiliate_stats_api.hpp>
#include <graphene/bookie/bookie_api.hpp>
#include <graphene/debug_witness/debug_api.hpp>
#include <graphene/net/node.hpp>
#include <graphene/elasticsearch/elasticsearch_plugin.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
#include <graphene/peerplays_sidechain/sidechain_api.hpp>
#include <fc/api.hpp>
#include <fc/crypto/elliptic.hpp>
@ -405,6 +404,8 @@ public:
fc::api<graphene::bookie::bookie_api> bookie() const;
/// @brief Retrieve the affiliate_stats API (if available)
fc::api<graphene::affiliate_stats::affiliate_stats_api> affiliate_stats() const;
/// @brief Retrieve the sidechain_api API (if available)
fc::api<graphene::peerplays_sidechain::sidechain_api> sidechain() const;
/// @brief Called to enable an API, not reflected.
void enable_api(const string &api_name);
@ -421,6 +422,7 @@ private:
optional<fc::api<graphene::debug_witness::debug_api>> _debug_api;
optional<fc::api<graphene::bookie::bookie_api>> _bookie_api;
optional<fc::api<graphene::affiliate_stats::affiliate_stats_api>> _affiliate_stats_api;
optional<fc::api<graphene::peerplays_sidechain::sidechain_api>> _sidechain_api;
};
}} // namespace graphene::app
@ -498,6 +500,7 @@ FC_API(graphene::app::login_api,
(asset)
(debug)
(bookie)
(affiliate_stats))
(affiliate_stats)
(sidechain))
// clang-format on

View file

@ -56,6 +56,8 @@
#include <graphene/chain/custom_permission_object.hpp>
#include <graphene/chain/nft_object.hpp>
#include <graphene/chain/offer_object.hpp>
#include <graphene/chain/voters_info.hpp>
#include <graphene/chain/votes_info.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
@ -558,6 +560,13 @@ public:
* @param account The ID of the account whose witness should be retrieved
* @return The witness object, or null if the account does not have a witness
*/
fc::optional<witness_object> get_witness_by_account_id(account_id_type account) const;
/**
* @brief Get the witness owned by a given account
* @param account_id_or_name The ID or name of the account whose witness should be retrieved
* @return The witness object, or null if the account does not have a witness
*/
fc::optional<witness_object> get_witness_by_account(const std::string account_name_or_id) const;
/**
@ -586,6 +595,13 @@ public:
*/
vector<optional<committee_member_object>> get_committee_members(const vector<committee_member_id_type> &committee_member_ids) const;
/**
* @brief Get the committee_member owned by a given account
* @param account The ID of the account whose committee_member should be retrieved
* @return The committee_member object, or null if the account does not have a committee_member
*/
fc::optional<committee_member_object> get_committee_member_by_account_id(account_id_type account) const;
/**
* @brief Get the committee_member owned by a given account
* @param account_id_or_name The ID or name of the account whose committee_member should be retrieved
@ -601,6 +617,11 @@ public:
*/
map<string, committee_member_id_type> lookup_committee_member_accounts(const string &lower_bound_name, uint32_t limit) const;
/**
* @brief Get the total number of committee_members registered with the blockchain
*/
uint64_t get_committee_member_count() const;
/////////////////
// SON members //
/////////////////
@ -619,7 +640,14 @@ public:
* @param account The ID of the account whose SON should be retrieved
* @return The SON object, or null if the account does not have a SON
*/
fc::optional<son_object> get_son_by_account(account_id_type account) const;
fc::optional<son_object> get_son_by_account_id(account_id_type account) const;
/**
* @brief Get the SON owned by a given account
* @param account_id_or_name The ID of the account whose SON should be retrieved
* @return The SON object, or null if the account does not have a SON
*/
fc::optional<son_object> get_son_by_account(const std::string account_id_or_name) const;
/**
* @brief Get names and IDs for registered SONs
@ -698,15 +726,46 @@ public:
*/
uint64_t get_sidechain_addresses_count() const;
/// WORKERS
/////////////
// Workers //
/////////////
/**
* @brief Get a list of workers by ID
* @param worker_ids IDs of the workers to retrieve
* @return The workers corresponding to the provided IDs
*
* This function has semantics identical to @ref get_objects
*/
vector<optional<worker_object>> get_workers(const vector<worker_id_type> &worker_ids) const;
/**
* @brief Return the worker objects associated with this account.
* @param account_id_or_name The ID or name of the account whose worker should be retrieved
* @param account The ID of the account whose workers should be retrieved
* @return The worker object or null if the account does not have a worker
*/
vector<worker_object> get_workers_by_account_id(account_id_type account) const;
/**
* @brief Return the worker objects associated with this account.
* @param account_id_or_name The ID or name of the account whose workers should be retrieved
* @return The worker object or null if the account does not have a worker
*/
vector<worker_object> get_workers_by_account(const std::string account_id_or_name) const;
/**
* @brief Get names and IDs for registered workers
* @param lower_bound_name Lower bound of the first name to return
* @param limit Maximum number of results to return -- must not exceed 1000
* @return Map of worker names to corresponding IDs
*/
map<string, worker_id_type> lookup_worker_accounts(const string &lower_bound_name, uint32_t limit) const;
/**
* @brief Get the total number of workers registered with the blockchain
*/
uint64_t get_worker_count() const;
///////////
// Votes //
///////////
@ -721,6 +780,39 @@ public:
*/
vector<variant> lookup_vote_ids(const vector<vote_id_type> &votes) const;
/**
* @brief Get a list of vote_id_type that ID votes for
* @param account_name_or_id ID or name of the account to get votes for
* @return The list of vote_id_type ID votes for
*
*/
vector<vote_id_type> get_votes_ids(const string &account_name_or_id) const;
/**
* @brief Return the objects account_name_or_id votes for
* @param account_name_or_id ID or name of the account to get votes for
* @return The votes_info account_name_or_id votes for
*
*/
votes_info get_votes(const string &account_name_or_id) const;
/**
*
* @brief Get a list of accounts that votes for vote_id
* @param vote_id We search accounts that vote for this ID
* @return The accounts that votes for provided ID
*
*/
vector<account_object> get_voters_by_id(const vote_id_type &vote_id) const;
/**
* @brief Return the accounts that votes for account_name_or_id
* @param account_name_or_id ID or name of the account to get voters for
* @return The voters_info for account_name_or_id
*
*/
voters_info get_voters(const string &account_name_or_id) const;
////////////////////////////
// Authority / validation //
////////////////////////////
@ -1033,17 +1125,21 @@ FC_API(graphene::app::database_api,
// Witnesses
(get_witnesses)
(get_witness_by_account_id)
(get_witness_by_account)
(lookup_witness_accounts)
(get_witness_count)
// Committee members
(get_committee_members)
(get_committee_member_by_account_id)
(get_committee_member_by_account)
(lookup_committee_member_accounts)
(get_committee_member_count)
// SON members
(get_sons)
(get_son_by_account_id)
(get_son_by_account)
(lookup_son_accounts)
(get_son_count)
@ -1060,10 +1156,19 @@ FC_API(graphene::app::database_api,
(get_sidechain_address_by_account_and_sidechain)
(get_sidechain_addresses_count)
// workers
// Workers
(get_workers)
(get_workers_by_account_id)
(get_workers_by_account)
(lookup_worker_accounts)
(get_worker_count)
// Votes
(lookup_vote_ids)
(get_votes_ids)
(get_votes)
(get_voters_by_id)
(get_voters)
// Authority / validation
(get_transaction_hex)

View file

@ -493,4 +493,3 @@ namespace fc {
const_cast<int*>(event_obj.my->state_machine.current_state())[0] = (int)status;
}
} //end namespace fc

View file

@ -2034,16 +2034,9 @@ void database::perform_son_tasks()
void update_son_params(database& db)
{
if( db.head_block_time() >= HARDFORK_SON2_TIME )
if( (db.head_block_time() >= HARDFORK_SON2_TIME) && (db.head_block_time() < HARDFORK_SON3_TIME) )
{
const auto& gpo = db.get_global_properties();
const asset_object& btc_asset = gpo.parameters.btc_asset()(db);
if( btc_asset.is_transfer_restricted() ) {
db.modify( btc_asset, []( asset_object& ao ) {
ao.options.flags = asset_issuer_permission_flags::charge_market_fee |
asset_issuer_permission_flags::override_authority;
});
}
db.modify( gpo, []( global_property_object& gpo ) {
gpo.parameters.extensions.value.maximum_son_count = 7;
});
@ -2274,6 +2267,14 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
p.pending_parameters->extensions.value.hbd_asset = p.parameters.extensions.value.hbd_asset;
if( !p.pending_parameters->extensions.value.hive_asset.valid() )
p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset;
// the following parameters are not allowed to be changed. So take what is in global property
p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset;
p.pending_parameters->extensions.value.hbd_asset = p.parameters.extensions.value.hbd_asset;
p.pending_parameters->extensions.value.btc_asset = p.parameters.extensions.value.btc_asset;
p.pending_parameters->extensions.value.son_account = p.parameters.extensions.value.son_account;
p.pending_parameters->extensions.value.gpos_period_start = p.parameters.extensions.value.gpos_period_start;
p.parameters = std::move(*p.pending_parameters);
p.pending_parameters.reset();
}

View file

@ -344,7 +344,6 @@ namespace graphene { namespace chain {
{
FC_THROW_EXCEPTION(graphene::chain::no_transition, "No transition");
}
template <class Fsm>
void no_transition(canceled_event const& e, Fsm&, int state)
{

View file

@ -0,0 +1,7 @@
#ifndef HARDFORK_SON3_TIME
#ifdef BUILD_PEERPLAYS_TESTNET
#define HARDFORK_SON3_TIME (fc::time_point_sec::from_iso_string("2022-04-30T00:00:00"))
#else
#define HARDFORK_SON3_TIME (fc::time_point_sec::from_iso_string("2022-04-30T00:00:00"))
#endif
#endif

View file

@ -24,12 +24,11 @@
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/protocol/betting_market.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
#include <graphene/chain/protocol/betting_market.hpp>
#include <sstream>
#include <boost/multi_index/composite_key.hpp>
#include <sstream>
namespace graphene { namespace chain {
class betting_market_object;
@ -626,7 +625,6 @@ typedef multi_index_container<
typedef generic_index<betting_market_position_object, betting_market_position_multi_index_type> betting_market_position_index;
template<typename Stream>
inline Stream& operator<<( Stream& s, const betting_market_object& betting_market_obj )
{
@ -714,12 +712,110 @@ inline Stream& operator>>( Stream& s, betting_market_group_object& betting_marke
return s;
}
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::betting_market_rules_object, (graphene::db::object), (name)(description) )
FC_REFLECT_DERIVED( graphene::chain::betting_market_group_object, (graphene::db::object), (description)(event_id)(rules_id)(asset_id)(total_matched_bets_amount)(never_in_play)(delay_before_settling)(settling_time) )
FC_REFLECT_DERIVED( graphene::chain::betting_market_object, (graphene::db::object), (group_id)(description)(payout_condition)(resolution) )
FC_REFLECT_DERIVED( graphene::chain::bet_object, (graphene::db::object), (bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(back_or_lay)(end_of_delay) )
FC_REFLECT_DERIVED( graphene::chain::betting_market_position_object, (graphene::db::object), (bettor_id)(betting_market_id)(pay_if_payout_condition)(pay_if_not_payout_condition)(pay_if_canceled)(pay_if_not_canceled)(fees_collected) )
namespace fc {
template<>
template<>
inline void if_enum<fc::false_type>::from_variant(const variant &vo, graphene::chain::betting_market_object &v, uint32_t max_depth) {
from_variant(vo, v, max_depth);
}
template<>
template<>
inline void if_enum<fc::false_type>::to_variant(const graphene::chain::betting_market_object &v, variant &vo, uint32_t max_depth) {
to_variant(v, vo, max_depth);
}
namespace raw { namespace detail {
template<>
template<>
inline void if_enum<fc::false_type>::pack(fc::datastream<size_t> &s, const graphene::chain::betting_market_object &v, uint32_t) {
s << v;
}
template<>
template<>
inline void if_enum<fc::false_type>::pack(fc::datastream<char*> &s, const graphene::chain::betting_market_object &v, uint32_t) {
s << v;
}
template<>
template<>
inline void if_enum<fc::false_type>::unpack(fc::datastream<const char*> &s, graphene::chain::betting_market_object &v, uint32_t) {
s >> v;
}
} } // namespace fc::raw::detail
template <>
struct get_typename<graphene::chain::betting_market_object> {
static const char *name() {
return "graphene::chain::betting_market_object";
}
};
template <>
struct reflector<graphene::chain::betting_market_object> {
typedef graphene::chain::betting_market_object type;
typedef fc::true_type is_defined;
typedef fc::false_type is_enum;
};
} // namespace fc
namespace fc {
template<>
template<>
inline void if_enum<fc::false_type>::from_variant(const variant &vo, graphene::chain::betting_market_group_object &v, uint32_t max_depth) {
from_variant(vo, v, max_depth);
}
template<>
template<>
inline void if_enum<fc::false_type>::to_variant(const graphene::chain::betting_market_group_object &v, variant &vo, uint32_t max_depth) {
to_variant(v, vo, max_depth);
}
namespace raw { namespace detail {
template<>
template<>
inline void if_enum<fc::false_type>::pack(fc::datastream<size_t> &s, const graphene::chain::betting_market_group_object &v, uint32_t) {
s << v;
}
template<>
template<>
inline void if_enum<fc::false_type>::pack(fc::datastream<char*> &s, const graphene::chain::betting_market_group_object &v, uint32_t) {
s << v;
}
template<>
template<>
inline void if_enum<fc::false_type>::unpack(fc::datastream<const char*> &s, graphene::chain::betting_market_group_object &v, uint32_t) {
s >> v;
}
} } // namespace fc::raw:detail
template <>
struct get_typename<graphene::chain::betting_market_group_object> {
static const char *name() {
return "graphene::chain::betting_market_group_object";
}
};
template <>
struct reflector<graphene::chain::betting_market_group_object> {
typedef graphene::chain::betting_market_group_object type;
typedef fc::true_type is_defined;
typedef fc::false_type is_enum;
};
} // namespace fc

View file

@ -158,6 +158,53 @@ typedef generic_index<event_object, event_object_multi_index_type> event_object_
return s;
}
} } // graphene::chain
FC_REFLECT(graphene::chain::event_object, (name)(season)(start_time)(event_group_id)(at_least_one_betting_market_group_settled)(scores))
namespace fc {
template<>
template<>
inline void if_enum<fc::false_type>::from_variant(const variant &vo, graphene::chain::event_object &v, uint32_t max_depth) {
from_variant(vo, v, max_depth);
}
template<>
template<>
inline void if_enum<fc::false_type>::to_variant(const graphene::chain::event_object &v, variant &vo, uint32_t max_depth) {
to_variant(v, vo, max_depth);
}
namespace raw { namespace detail {
template<>
template<>
inline void if_enum<fc::false_type>::pack(fc::datastream<size_t> &s, const graphene::chain::event_object &v, uint32_t) {
s << v;
}
template<>
template<>
inline void if_enum<fc::false_type>::pack(fc::datastream<char*> &s, const graphene::chain::event_object &v, uint32_t) {
s << v;
}
template<>
template<>
inline void if_enum<fc::false_type>::unpack(fc::datastream<const char*> &s, graphene::chain::event_object &v, uint32_t) {
s >> v;
}
} } // namespace fc::raw::detail
template <>
struct get_typename<graphene::chain::event_object> {
static const char *name() {
return "graphene::chain::event_object";
}
};
template <>
struct reflector<graphene::chain::event_object> {
typedef graphene::chain::event_object type;
typedef fc::true_type is_defined;
typedef fc::false_type is_enum;
};
} // namespace fc

View file

@ -23,6 +23,7 @@
*/
#pragma once
#include <boost/exception/diagnostic_information.hpp>
#include <fc/exception/exception.hpp>
#include <graphene/chain/protocol/protocol.hpp>
@ -75,6 +76,14 @@
elog( "Caught plugin exception: ${e}", ("e", e.to_detail_string() ) ); \
throw; \
} \
catch( const boost::exception& e ) \
{ \
elog( "Caught plugin boost::exception: ${e}", ("e", boost::diagnostic_information(e) ) ); \
} \
catch( const std::exception& e ) \
{ \
elog( "Caught plugin std::exception: ${e}", ("e", e.what() ) ); \
} \
catch( ... ) \
{ \
wlog( "Caught unexpected exception in plugin" ); \

View file

@ -23,10 +23,8 @@
*/
#pragma once
#include <graphene/chain/match_object.hpp>
#include <graphene/chain/rock_paper_scissors.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <graphene/db/flat_index.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
#include <fc/crypto/hex.hpp>
#include <sstream>
@ -156,7 +154,6 @@ namespace graphene { namespace chain {
return s;
}
} }
FC_REFLECT_ENUM(graphene::chain::game_state,
@ -165,7 +162,52 @@ FC_REFLECT_ENUM(graphene::chain::game_state,
(expecting_reveal_moves)
(game_complete))
//FC_REFLECT_TYPENAME(graphene::chain::game_object) // manually serialized
FC_REFLECT(graphene::chain::game_object, (players))
namespace fc {
template<>
template<>
inline void if_enum<fc::false_type>::from_variant(const variant &vo, graphene::chain::game_object &v, uint32_t max_depth) {
from_variant(vo, v, max_depth);
}
template<>
template<>
inline void if_enum<fc::false_type>::to_variant(const graphene::chain::game_object &v, variant &vo, uint32_t max_depth) {
to_variant(v, vo, max_depth);
}
namespace raw { namespace detail {
template<>
template<>
inline void if_enum<fc::false_type>::pack(fc::datastream<size_t> &s, const graphene::chain::game_object &v, uint32_t) {
s << v;
}
template<>
template<>
inline void if_enum<fc::false_type>::pack(fc::datastream<char*> &s, const graphene::chain::game_object &v, uint32_t) {
s << v;
}
template<>
template<>
inline void if_enum<fc::false_type>::unpack(fc::datastream<const char*> &s, graphene::chain::game_object &v, uint32_t) {
s >> v;
}
} } // namespace fc::raw::detail
template <>
struct get_typename<graphene::chain::game_object> {
static const char *name() {
return "graphene::chain::game_object";
}
};
template <>
struct reflector<graphene::chain::game_object> {
typedef graphene::chain::game_object type;
typedef fc::true_type is_defined;
typedef fc::false_type is_enum;
};
} // namespace fc

View file

@ -1,8 +1,5 @@
#pragma once
#include <graphene/chain/protocol/tournament.hpp>
#include <graphene/chain/rock_paper_scissors.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <graphene/db/flat_index.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
#include <fc/crypto/hex.hpp>
#include <sstream>
@ -16,6 +13,7 @@ namespace fc {
void from_variant(const fc::variant& v, graphene::chain::match_object& match_obj, uint32_t max_depth = 1);
} //end namespace fc
namespace graphene { namespace chain {
class database;
using namespace graphene::db;
@ -89,6 +87,7 @@ namespace graphene { namespace chain {
void pack_impl(std::ostream& stream) const;
void unpack_impl(std::istream& stream);
void on_initiate_match(database& db);
void on_game_complete(database& db, const game_object& game);
game_id_type start_next_game(database& db, match_id_type match_id);
@ -154,7 +153,6 @@ namespace graphene { namespace chain {
return s;
}
} }
FC_REFLECT_ENUM(graphene::chain::match_state,
@ -162,6 +160,52 @@ FC_REFLECT_ENUM(graphene::chain::match_state,
(match_in_progress)
(match_complete))
//FC_REFLECT_TYPENAME(graphene::chain::match_object) // manually serialized
FC_REFLECT(graphene::chain::match_object, (players))
namespace fc {
template<>
template<>
inline void if_enum<fc::false_type>::from_variant(const variant &vo, graphene::chain::match_object &v, uint32_t max_depth) {
from_variant(vo, v, max_depth);
}
template<>
template<>
inline void if_enum<fc::false_type>::to_variant(const graphene::chain::match_object &v, variant &vo, uint32_t max_depth) {
to_variant(v, vo, max_depth);
}
namespace raw { namespace detail {
template<>
template<>
inline void if_enum<fc::false_type>::pack(fc::datastream<size_t> &s, const graphene::chain::match_object &v, uint32_t) {
s << v;
}
template<>
template<>
inline void if_enum<fc::false_type>::pack(fc::datastream<char*> &s, const graphene::chain::match_object &v, uint32_t) {
s << v;
}
template<>
template<>
inline void if_enum<fc::false_type>::unpack(fc::datastream<const char*> &s, graphene::chain::match_object &v, uint32_t) {
s >> v;
}
} } // namespace fc::raw::detail
template <>
struct get_typename<graphene::chain::match_object> {
static const char *name() {
return "graphene::chain::match_object";
}
};
template <>
struct reflector<graphene::chain::match_object> {
typedef graphene::chain::match_object type;
typedef fc::true_type is_defined;
typedef fc::false_type is_enum;
};
} // namespace fc

View file

@ -577,6 +577,8 @@ FC_REFLECT_TYPENAME( graphene::chain::fba_accumulator_id_type )
FC_REFLECT_TYPENAME( graphene::chain::betting_market_position_id_type )
FC_REFLECT_TYPENAME( graphene::chain::global_betting_statistics_id_type )
FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type )
FC_REFLECT_TYPENAME( graphene::chain::game_id_type )
FC_REFLECT_TYPENAME( graphene::chain::match_id_type )
FC_REFLECT_TYPENAME( graphene::chain::custom_permission_id_type )
FC_REFLECT_TYPENAME( graphene::chain::custom_account_authority_id_type )
FC_REFLECT_TYPENAME( graphene::chain::offer_history_id_type )

View file

@ -1,8 +1,7 @@
#pragma once
#include <graphene/chain/protocol/tournament.hpp>
#include <graphene/chain/rock_paper_scissors.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <graphene/db/flat_index.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
#include <fc/crypto/hex.hpp>
#include <sstream>
@ -154,7 +153,6 @@ namespace graphene { namespace chain {
> tournament_details_object_multi_index_type;
typedef generic_index<tournament_details_object, tournament_details_object_multi_index_type> tournament_details_index;
template<typename Stream>
inline Stream& operator<<( Stream& s, const tournament_object& tournament_obj )
{
@ -181,6 +179,7 @@ namespace graphene { namespace chain {
return s;
}
template<typename Stream>
inline Stream& operator>>( Stream& s, tournament_object& tournament_obj )
{
@ -230,8 +229,6 @@ namespace graphene { namespace chain {
flat_set<account_id_type> before_account_ids;
};
} }
FC_REFLECT_DERIVED(graphene::chain::tournament_details_object, (graphene::db::object),
@ -240,8 +237,7 @@ FC_REFLECT_DERIVED(graphene::chain::tournament_details_object, (graphene::db::ob
(payers)
(players_payers)
(matches))
//FC_REFLECT_TYPENAME(graphene::chain::tournament_object) // manually serialized
FC_REFLECT(graphene::chain::tournament_object, (creator))
FC_REFLECT_ENUM(graphene::chain::tournament_state,
(accepting_registrations)
(awaiting_start)
@ -249,3 +245,52 @@ FC_REFLECT_ENUM(graphene::chain::tournament_state,
(registration_period_expired)
(concluded))
namespace fc {
template<>
template<>
inline void if_enum<fc::false_type>::from_variant(const variant &vo, graphene::chain::tournament_object &v, uint32_t max_depth) {
from_variant(vo, v, max_depth);
}
template<>
template<>
inline void if_enum<fc::false_type>::to_variant(const graphene::chain::tournament_object &v, variant &vo, uint32_t max_depth) {
to_variant(v, vo, max_depth);
}
namespace raw { namespace detail {
template<>
template<>
inline void if_enum<fc::false_type>::pack(fc::datastream<size_t> &s, const graphene::chain::tournament_object &v, uint32_t) {
s << v;
}
template<>
template<>
inline void if_enum<fc::false_type>::pack(fc::datastream<char*> &s, const graphene::chain::tournament_object &v, uint32_t) {
s << v;
}
template<>
template<>
inline void if_enum<fc::false_type>::unpack(fc::datastream<const char*> &s, graphene::chain::tournament_object &v, uint32_t) {
s >> v;
}
} } // namespace fc::raw::detail
template <>
struct get_typename<graphene::chain::tournament_object> {
static const char *name() {
return "graphene::chain::tournament_object";
}
};
template <>
struct reflector<graphene::chain::tournament_object> {
typedef graphene::chain::tournament_object type;
typedef fc::true_type is_defined;
typedef fc::false_type is_enum;
};
} // namespace fc

View file

@ -0,0 +1,40 @@
#pragma once
#include <graphene/chain/protocol/vote.hpp>
namespace graphene { namespace chain {
/**
* @class voters_info_object
* @ingroup object
*/
struct voters_info_object {
vote_id_type vote_id;
vector<account_id_type> voters;
};
/**
* @class voters_info
* @brief tracks information about a voters info
* @ingroup object
*/
struct voters_info {
optional<voters_info_object> voters_for_committee_member;
optional<voters_info_object> voters_for_witness;
optional<vector<voters_info_object> > voters_for_workers;
optional<vector<voters_info_object> > voters_against_workers;
optional<voters_info_object> voters_for_son;
};
} } // graphene::chain
FC_REFLECT( graphene::chain::voters_info_object,
(vote_id)
(voters) )
FC_REFLECT( graphene::chain::voters_info,
(voters_for_committee_member)
(voters_for_witness)
(voters_for_workers)
(voters_against_workers)
(voters_for_son) )

View file

@ -0,0 +1,40 @@
#pragma once
#include <graphene/chain/protocol/vote.hpp>
namespace graphene { namespace chain {
/**
* @class votes_info_object
* @ingroup object
*/
struct votes_info_object {
vote_id_type vote_id;
object_id_type id;
};
/**
* @class votes_info
* @brief tracks information about a votes info
* @ingroup object
*/
struct votes_info {
optional< vector< votes_info_object > > votes_for_committee_members;
optional< vector< votes_info_object > > votes_for_witnesses;
optional< vector< votes_info_object > > votes_for_workers;
optional< vector< votes_info_object > > votes_against_workers;
optional< vector< votes_info_object > > votes_for_sons;
};
} } // graphene::chain
FC_REFLECT( graphene::chain::votes_info_object,
(vote_id)
(id) )
FC_REFLECT( graphene::chain::votes_info,
(votes_for_committee_members)
(votes_for_witnesses)
(votes_for_workers)
(votes_against_workers)
(votes_for_sons) )

View file

@ -192,6 +192,16 @@ namespace graphene { namespace chain {
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));
}
if( extensions.value.son_heartbeat_frequency.valid() && extensions.value.son_deregister_time.valid() )
FC_ASSERT( *extensions.value.son_heartbeat_frequency < *extensions.value.son_deregister_time );
if( extensions.value.son_heartbeat_frequency.valid() && extensions.value.son_down_time.valid() )
FC_ASSERT( *extensions.value.son_heartbeat_frequency < *extensions.value.son_down_time );
if( extensions.value.son_heartbeat_frequency.valid() && extensions.value.son_pay_time.valid() )
FC_ASSERT( *extensions.value.son_heartbeat_frequency < *extensions.value.son_pay_time );
}
} } // graphene::chain

View file

@ -36,7 +36,7 @@ namespace graphene { namespace db {
{
unordered_map<object_id_type, unique_ptr<object> > old_values;
unordered_map<object_id_type, object_id_type> old_index_next_ids;
std::unordered_set<object_id_type> new_ids;
std::set<object_id_type, std::greater<object_id_type> > new_ids;
unordered_map<object_id_type, unique_ptr<object> > removed;
};

@ -1 +1 @@
Subproject commit 488883921936139e8734b99822d3a589afe80da1
Subproject commit 6171e973c7fcfc9e0a39eaee2f05da84416a90e6

View file

@ -79,6 +79,8 @@ account_history_plugin_impl::~account_history_plugin_impl()
void account_history_plugin_impl::update_account_histories( const signed_block& b )
{
try \
{
graphene::chain::database& db = database();
vector<optional< operation_history_object > >& hist = db.get_applied_operations();
bool is_first = true;
@ -197,6 +199,19 @@ void account_history_plugin_impl::update_account_histories( const signed_block&
if (_partial_operations && ! oho.valid())
skip_oho_id();
}
}
catch( const boost::exception& e )
{
elog( "Caught account_history_plugin::update_account_histories(...) boost::exception: ${e}", ("e", boost::diagnostic_information(e) ) );
}
catch( const std::exception& e )
{
elog( "Caught account_history_plugin::update_account_histories(...) std::exception: ${e}", ("e", e.what() ) );
}
catch( ... )
{
wlog( "Caught unexpected exception in account_history_plugin::update_account_histories(...)" );
}
}
void account_history_plugin_impl::add_account_history( const account_id_type account_id, const operation_history_id_type op_id )

View file

@ -29,7 +29,6 @@
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/protocol/asset.hpp>
#include <graphene/chain/event_object.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/affiliate_stats/affiliate_stats_objects.hpp>

View file

@ -157,37 +157,45 @@ fc::variants bookie_api_impl::get_objects(const vector<object_id_type>& ids) con
{
case event_id_type::type_id:
{
auto& persistent_events_by_event_id = db->get_index_type<detail::persistent_event_index>().indices().get<by_event_id>();
auto iter = persistent_events_by_event_id.find(id.as<event_id_type>());
if (iter != persistent_events_by_event_id.end())
return iter->ephemeral_event_object.to_variant();
const auto &idx = db->get_index_type<event_object_index>();
const auto &aidx = dynamic_cast<const base_primary_index &>(idx);
const auto &refs = aidx.get_secondary_index<detail::persistent_event_index>();
auto iter = refs.ephemeral_event_object.find(id.as<event_id_type>());
if (iter != refs.ephemeral_event_object.end())
return iter->second.to_variant();
else
return {};
}
case bet_id_type::type_id:
{
auto& persistent_bets_by_bet_id = db->get_index_type<detail::persistent_bet_index>().indices().get<by_bet_id>();
auto iter = persistent_bets_by_bet_id.find(id.as<bet_id_type>());
if (iter != persistent_bets_by_bet_id.end())
return iter->ephemeral_bet_object.to_variant();
const auto &idx = db->get_index_type<bet_object_index>();
const auto &aidx = dynamic_cast<const base_primary_index &>(idx);
const auto &refs = aidx.get_secondary_index<detail::persistent_bet_index>();
auto iter = refs.internal.find(id.as<bet_id_type>());
if (iter != refs.internal.end())
return iter->second.ephemeral_bet_object.to_variant();
else
return {};
}
case betting_market_object::type_id:
{
auto& persistent_betting_markets_by_betting_market_id = db->get_index_type<detail::persistent_betting_market_index>().indices().get<by_betting_market_id>();
auto iter = persistent_betting_markets_by_betting_market_id.find(id.as<betting_market_id_type>());
if (iter != persistent_betting_markets_by_betting_market_id.end())
return iter->ephemeral_betting_market_object.to_variant();
const auto &idx = db->get_index_type<betting_market_object_index>();
const auto &aidx = dynamic_cast<const base_primary_index &>(idx);
const auto &refs = aidx.get_secondary_index<detail::persistent_betting_market_index>();
auto iter = refs.ephemeral_betting_market_object.find(id.as<betting_market_id_type>());
if (iter != refs.ephemeral_betting_market_object.end())
return iter->second.to_variant();
else
return {};
}
case betting_market_group_object::type_id:
{
auto& persistent_betting_market_groups_by_betting_market_group_id = db->get_index_type<detail::persistent_betting_market_group_index>().indices().get<by_betting_market_group_id>();
auto iter = persistent_betting_market_groups_by_betting_market_group_id.find(id.as<betting_market_group_id_type>());
if (iter != persistent_betting_market_groups_by_betting_market_group_id.end())
return iter->ephemeral_betting_market_group_object.to_variant();
const auto &idx = db->get_index_type<betting_market_group_object_index>();
const auto &aidx = dynamic_cast<const base_primary_index &>(idx);
const auto &refs = aidx.get_secondary_index<detail::persistent_betting_market_group_index>();
auto iter = refs.internal.find(id.as<betting_market_group_id_type>());
if (iter != refs.internal.end())
return iter->second.ephemeral_betting_market_group_object.to_variant();
else
return {};
}
@ -203,25 +211,28 @@ std::vector<matched_bet_object> bookie_api_impl::get_matched_bets_for_bettor(acc
{
std::vector<matched_bet_object> result;
std::shared_ptr<graphene::chain::database> db = app.chain_database();
auto& persistent_bets_by_bettor_id = db->get_index_type<detail::persistent_bet_index>().indices().get<by_bettor_id>();
auto iter = persistent_bets_by_bettor_id.lower_bound(std::make_tuple(bettor_id, true));
while (iter != persistent_bets_by_bettor_id.end() &&
iter->get_bettor_id() == bettor_id &&
iter->is_matched())
const auto &idx = db->get_index_type<bet_object_index>();
const auto &aidx = dynamic_cast<const base_primary_index &>(idx);
const auto &refs = aidx.get_secondary_index<detail::persistent_bet_index>();
for( const auto& bet_pair : refs.internal )
{
const auto& bet = bet_pair.second;
if( bet.get_bettor_id() == bettor_id && bet.is_matched() )
{
matched_bet_object match;
match.id = iter->ephemeral_bet_object.id;
match.bettor_id = iter->ephemeral_bet_object.bettor_id;
match.betting_market_id = iter->ephemeral_bet_object.betting_market_id;
match.amount_to_bet = iter->ephemeral_bet_object.amount_to_bet;
match.back_or_lay = iter->ephemeral_bet_object.back_or_lay;
match.end_of_delay = iter->ephemeral_bet_object.end_of_delay;
match.amount_matched = iter->amount_matched;
match.associated_operations = iter->associated_operations;
match.id = bet.ephemeral_bet_object.id;
match.bettor_id = bet.ephemeral_bet_object.bettor_id;
match.betting_market_id = bet.ephemeral_bet_object.betting_market_id;
match.amount_to_bet = bet.ephemeral_bet_object.amount_to_bet;
match.back_or_lay = bet.ephemeral_bet_object.back_or_lay;
match.end_of_delay = bet.ephemeral_bet_object.end_of_delay;
match.amount_matched = bet.amount_matched;
match.associated_operations = bet.associated_operations;
result.emplace_back(std::move(match));
++iter;
}
}
return result;
}
@ -231,29 +242,32 @@ std::vector<matched_bet_object> bookie_api_impl::get_all_matched_bets_for_bettor
std::vector<matched_bet_object> result;
std::shared_ptr<graphene::chain::database> db = app.chain_database();
auto& persistent_bets_by_bettor_id = db->get_index_type<detail::persistent_bet_index>().indices().get<by_bettor_id>();
persistent_bet_multi_index_type::index<by_bettor_id>::type::iterator iter;
if (start == bet_id_type())
iter = persistent_bets_by_bettor_id.lower_bound(std::make_tuple(bettor_id, true));
else
iter = persistent_bets_by_bettor_id.lower_bound(std::make_tuple(bettor_id, true, start));
while (iter != persistent_bets_by_bettor_id.end() &&
iter->get_bettor_id() == bettor_id &&
iter->is_matched() &&
result.size() < limit)
const auto &idx = db->get_index_type<bet_object_index>();
const auto &aidx = dynamic_cast<const base_primary_index &>(idx);
const auto &refs = aidx.get_secondary_index<detail::persistent_bet_index>();
for( const auto& bet_pair : refs.internal )
{
const auto& bet_id = bet_pair.first;
const auto& bet = bet_pair.second;
if( bet.get_bettor_id() == bettor_id &&
bet.is_matched() &&
bet_id > start &&
result.size() < limit )
{
matched_bet_object match;
match.id = iter->ephemeral_bet_object.id;
match.bettor_id = iter->ephemeral_bet_object.bettor_id;
match.betting_market_id = iter->ephemeral_bet_object.betting_market_id;
match.amount_to_bet = iter->ephemeral_bet_object.amount_to_bet;
match.back_or_lay = iter->ephemeral_bet_object.back_or_lay;
match.end_of_delay = iter->ephemeral_bet_object.end_of_delay;
match.amount_matched = iter->amount_matched;
match.id = bet.ephemeral_bet_object.id;
match.bettor_id = bet.ephemeral_bet_object.bettor_id;
match.betting_market_id = bet.ephemeral_bet_object.betting_market_id;
match.amount_to_bet = bet.ephemeral_bet_object.amount_to_bet;
match.back_or_lay = bet.ephemeral_bet_object.back_or_lay;
match.end_of_delay = bet.ephemeral_bet_object.end_of_delay;
match.amount_matched = bet.amount_matched;
match.associated_operations = bet.associated_operations;
result.emplace_back(std::move(match));
++iter;
}
}
return result;
}

View file

@ -59,143 +59,80 @@ namespace detail
* We do this by creating a secondary index on bet_object. We don't actually use it
* to index any property of the bet, we just use it to register for callbacks.
*/
class persistent_bet_object_helper : public secondary_index
{
public:
virtual ~persistent_bet_object_helper() {}
virtual void object_inserted(const object& obj) override;
//virtual void object_removed( const object& obj ) override;
//virtual void about_to_modify( const object& before ) override;
virtual void object_modified(const object& after) override;
void set_plugin_instance(bookie_plugin* instance) { _bookie_plugin = instance; }
private:
bookie_plugin* _bookie_plugin;
};
void persistent_bet_object_helper::object_inserted(const object& obj)
void persistent_bet_index::object_inserted(const object& obj)
{
const bet_object& bet_obj = *boost::polymorphic_downcast<const bet_object*>(&obj);
_bookie_plugin->database().create<persistent_bet_object>([&](persistent_bet_object& saved_bet_obj) {
saved_bet_obj.ephemeral_bet_object = bet_obj;
});
if(0 == internal.count(bet_obj.id))
internal.insert( {bet_obj.id, bet_obj} );
else
internal[bet_obj.id] = bet_obj;
}
void persistent_bet_object_helper::object_modified(const object& after)
void persistent_bet_index::object_modified(const object& after)
{
database& db = _bookie_plugin->database();
auto& persistent_bets_by_bet_id = db.get_index_type<persistent_bet_index>().indices().get<by_bet_id>();
const bet_object& bet_obj = *boost::polymorphic_downcast<const bet_object*>(&after);
auto iter = persistent_bets_by_bet_id.find(bet_obj.id);
assert (iter != persistent_bets_by_bet_id.end());
if (iter != persistent_bets_by_bet_id.end())
db.modify(*iter, [&](persistent_bet_object& saved_bet_obj) {
saved_bet_obj.ephemeral_bet_object = bet_obj;
});
auto iter = internal.find(bet_obj.id);
assert (iter != internal.end());
if (iter != internal.end())
iter->second = bet_obj;
}
//////////// end bet_object ///////////////////
class persistent_betting_market_object_helper : public secondary_index
{
public:
virtual ~persistent_betting_market_object_helper() {}
virtual void object_inserted(const object& obj) override;
//virtual void object_removed( const object& obj ) override;
//virtual void about_to_modify( const object& before ) override;
virtual void object_modified(const object& after) override;
void set_plugin_instance(bookie_plugin* instance) { _bookie_plugin = instance; }
private:
bookie_plugin* _bookie_plugin;
};
void persistent_betting_market_object_helper::object_inserted(const object& obj)
void persistent_betting_market_index::object_inserted(const object& obj)
{
const betting_market_object& betting_market_obj = *boost::polymorphic_downcast<const betting_market_object*>(&obj);
_bookie_plugin->database().create<persistent_betting_market_object>([&](persistent_betting_market_object& saved_betting_market_obj) {
saved_betting_market_obj.ephemeral_betting_market_object = betting_market_obj;
});
if(0 == ephemeral_betting_market_object.count(betting_market_obj.id))
ephemeral_betting_market_object.insert( {betting_market_obj.id, betting_market_obj} );
else
ephemeral_betting_market_object[betting_market_obj.id] = betting_market_obj;
}
void persistent_betting_market_object_helper::object_modified(const object& after)
void persistent_betting_market_index::object_modified(const object& after)
{
database& db = _bookie_plugin->database();
auto& persistent_betting_markets_by_betting_market_id = db.get_index_type<persistent_betting_market_index>().indices().get<by_betting_market_id>();
const betting_market_object& betting_market_obj = *boost::polymorphic_downcast<const betting_market_object*>(&after);
auto iter = persistent_betting_markets_by_betting_market_id.find(betting_market_obj.id);
assert (iter != persistent_betting_markets_by_betting_market_id.end());
if (iter != persistent_betting_markets_by_betting_market_id.end())
db.modify(*iter, [&](persistent_betting_market_object& saved_betting_market_obj) {
saved_betting_market_obj.ephemeral_betting_market_object = betting_market_obj;
});
auto iter = ephemeral_betting_market_object.find(betting_market_obj.id);
assert (iter != ephemeral_betting_market_object.end());
if (iter != ephemeral_betting_market_object.end())
iter->second = betting_market_obj;
}
//////////// end betting_market_object ///////////////////
class persistent_betting_market_group_object_helper : public secondary_index
{
public:
virtual ~persistent_betting_market_group_object_helper() {}
virtual void object_inserted(const object& obj) override;
//virtual void object_removed( const object& obj ) override;
//virtual void about_to_modify( const object& before ) override;
virtual void object_modified(const object& after) override;
void set_plugin_instance(bookie_plugin* instance) { _bookie_plugin = instance; }
private:
bookie_plugin* _bookie_plugin;
};
void persistent_betting_market_group_object_helper::object_inserted(const object& obj)
void persistent_betting_market_group_index::object_inserted(const object& obj)
{
const betting_market_group_object& betting_market_group_obj = *boost::polymorphic_downcast<const betting_market_group_object*>(&obj);
_bookie_plugin->database().create<persistent_betting_market_group_object>([&](persistent_betting_market_group_object& saved_betting_market_group_obj) {
saved_betting_market_group_obj.ephemeral_betting_market_group_object = betting_market_group_obj;
});
if(0 == internal.count(betting_market_group_obj.id))
internal.insert( {betting_market_group_obj.id, betting_market_group_obj} );
else
internal[betting_market_group_obj.id] = betting_market_group_obj;
}
void persistent_betting_market_group_object_helper::object_modified(const object& after)
void persistent_betting_market_group_index::object_modified(const object& after)
{
database& db = _bookie_plugin->database();
auto& persistent_betting_market_groups_by_betting_market_group_id = db.get_index_type<persistent_betting_market_group_index>().indices().get<by_betting_market_group_id>();
const betting_market_group_object& betting_market_group_obj = *boost::polymorphic_downcast<const betting_market_group_object*>(&after);
auto iter = persistent_betting_market_groups_by_betting_market_group_id.find(betting_market_group_obj.id);
assert (iter != persistent_betting_market_groups_by_betting_market_group_id.end());
if (iter != persistent_betting_market_groups_by_betting_market_group_id.end())
db.modify(*iter, [&](persistent_betting_market_group_object& saved_betting_market_group_obj) {
saved_betting_market_group_obj.ephemeral_betting_market_group_object = betting_market_group_obj;
});
auto iter = internal.find(betting_market_group_obj.id);
assert (iter != internal.end());
if (iter != internal.end())
iter->second = betting_market_group_obj;
}
//////////// end betting_market_group_object ///////////////////
class persistent_event_object_helper : public secondary_index
{
public:
virtual ~persistent_event_object_helper() {}
virtual void object_inserted(const object& obj) override;
//virtual void object_removed( const object& obj ) override;
//virtual void about_to_modify( const object& before ) override;
virtual void object_modified(const object& after) override;
void set_plugin_instance(bookie_plugin* instance) { _bookie_plugin = instance; }
private:
bookie_plugin* _bookie_plugin;
};
void persistent_event_object_helper::object_inserted(const object& obj)
void persistent_event_index::object_inserted(const object& obj)
{
const event_object& event_obj = *boost::polymorphic_downcast<const event_object*>(&obj);
_bookie_plugin->database().create<persistent_event_object>([&](persistent_event_object& saved_event_obj) {
saved_event_obj.ephemeral_event_object = event_obj;
});
if(0 == ephemeral_event_object.count(event_obj.id))
ephemeral_event_object.insert( {event_obj.id, event_obj} );
else
ephemeral_event_object[event_obj.id] = event_obj;
}
void persistent_event_object_helper::object_modified(const object& after)
void persistent_event_index::object_modified(const object& after)
{
database& db = _bookie_plugin->database();
auto& persistent_events_by_event_id = db.get_index_type<persistent_event_index>().indices().get<by_event_id>();
const event_object& event_obj = *boost::polymorphic_downcast<const event_object*>(&after);
auto iter = persistent_events_by_event_id.find(event_obj.id);
assert (iter != persistent_events_by_event_id.end());
if (iter != persistent_events_by_event_id.end())
db.modify(*iter, [&](persistent_event_object& saved_event_obj) {
saved_event_obj.ephemeral_event_object = event_obj;
});
auto iter = ephemeral_event_object.find(event_obj.id);
assert (iter != ephemeral_event_object.end());
if (iter != ephemeral_event_object.end())
iter->second = event_obj;
}
//////////// end event_object ///////////////////
@ -207,7 +144,6 @@ class bookie_plugin_impl
{ }
virtual ~bookie_plugin_impl();
/**
* Called After a block has been applied and committed. The callback
* should not yield and should execute quickly.
@ -299,27 +235,35 @@ void bookie_plugin_impl::on_block_applied( const signed_block& )
const asset& amount_bet = bet_matched_op.amount_bet;
// object may no longer exist
//const bet_object& bet = bet_matched_op.bet_id(db);
auto& persistent_bets_by_bet_id = db.get_index_type<persistent_bet_index>().indices().get<by_bet_id>();
auto bet_iter = persistent_bets_by_bet_id.find(bet_matched_op.bet_id);
assert(bet_iter != persistent_bets_by_bet_id.end());
if (bet_iter != persistent_bets_by_bet_id.end())
const auto &idx_bet_object = db.get_index_type<bet_object_index>();
const auto &aidx_bet_object = dynamic_cast<const base_primary_index &>(idx_bet_object);
const auto &refs_bet_object = aidx_bet_object.get_secondary_index<detail::persistent_bet_index>();
auto& nonconst_refs_bet_object = const_cast<persistent_bet_index&>(refs_bet_object);
auto bet_iter = nonconst_refs_bet_object.internal.find(bet_matched_op.bet_id);
assert(bet_iter != nonconst_refs_bet_object.internal.end());
if (bet_iter != nonconst_refs_bet_object.internal.end())
{
db.modify(*bet_iter, [&]( persistent_bet_object& obj ) {
obj.amount_matched += amount_bet.amount;
bet_iter->second.amount_matched += amount_bet.amount;
if (is_operation_history_object_stored(op.id))
obj.associated_operations.emplace_back(op.id);
});
const bet_object& bet_obj = bet_iter->ephemeral_bet_object;
bet_iter->second.associated_operations.emplace_back(op.id);
auto& persistent_betting_market_idx = db.get_index_type<persistent_betting_market_index>().indices().get<by_betting_market_id>();
auto persistent_betting_market_object_iter = persistent_betting_market_idx.find(bet_obj.betting_market_id);
FC_ASSERT(persistent_betting_market_object_iter != persistent_betting_market_idx.end());
const betting_market_object& betting_market = persistent_betting_market_object_iter->ephemeral_betting_market_object;
const bet_object& bet_obj = bet_iter->second.ephemeral_bet_object;
auto& persistent_betting_market_group_idx = db.get_index_type<persistent_betting_market_group_index>().indices().get<by_betting_market_group_id>();
auto persistent_betting_market_group_object_iter = persistent_betting_market_group_idx.find(betting_market.group_id);
FC_ASSERT(persistent_betting_market_group_object_iter != persistent_betting_market_group_idx.end());
const betting_market_group_object& betting_market_group = persistent_betting_market_group_object_iter->ephemeral_betting_market_group_object;
const auto &idx_betting_market = db.get_index_type<betting_market_object_index>();
const auto &aidx_betting_market = dynamic_cast<const base_primary_index &>(idx_betting_market);
const auto &refs_betting_market = aidx_betting_market.get_secondary_index<detail::persistent_betting_market_index>();
auto persistent_betting_market_object_iter = refs_betting_market.ephemeral_betting_market_object.find(bet_obj.betting_market_id);
FC_ASSERT(persistent_betting_market_object_iter != refs_betting_market.ephemeral_betting_market_object.end());
const betting_market_object& betting_market = persistent_betting_market_object_iter->second;
const auto &idx_betting_market_group = db.get_index_type<betting_market_group_object_index>();
const auto &aidx_betting_market_group = dynamic_cast<const base_primary_index &>(idx_betting_market_group);
const auto &refs_betting_market_group = aidx_betting_market_group.get_secondary_index<detail::persistent_betting_market_group_index>();
auto& nonconst_refs_betting_market_group = const_cast<persistent_betting_market_group_index&>(refs_betting_market_group);
auto persistent_betting_market_group_object_iter = nonconst_refs_betting_market_group.internal.find(betting_market.group_id);
FC_ASSERT(persistent_betting_market_group_object_iter != nonconst_refs_betting_market_group.internal.end());
const betting_market_group_object& betting_market_group = persistent_betting_market_group_object_iter->second.ephemeral_betting_market_group_object;
// if the object is still in the main database, keep the running total there
// otherwise, add it directly to the persistent version
@ -330,9 +274,7 @@ void bookie_plugin_impl::on_block_applied( const signed_block& )
obj.total_matched_bets_amount += amount_bet.amount;
});
else
db.modify( *persistent_betting_market_group_object_iter, [&]( persistent_betting_market_group_object& obj ){
obj.ephemeral_betting_market_group_object.total_matched_bets_amount += amount_bet.amount;
});
persistent_betting_market_group_object_iter->second.total_matched_bets_amount += amount_bet.amount;
}
}
else if( op.op.which() == operation::tag<event_create_operation>::value )
@ -364,33 +306,35 @@ void bookie_plugin_impl::on_block_applied( const signed_block& )
else if ( op.op.which() == operation::tag<bet_canceled_operation>::value )
{
const bet_canceled_operation& bet_canceled_op = op.op.get<bet_canceled_operation>();
auto& persistent_bets_by_bet_id = db.get_index_type<persistent_bet_index>().indices().get<by_bet_id>();
auto bet_iter = persistent_bets_by_bet_id.find(bet_canceled_op.bet_id);
assert(bet_iter != persistent_bets_by_bet_id.end());
if (bet_iter != persistent_bets_by_bet_id.end())
const auto &idx_bet_object = db.get_index_type<bet_object_index>();
const auto &aidx_bet_object = dynamic_cast<const base_primary_index &>(idx_bet_object);
const auto &refs_bet_object = aidx_bet_object.get_secondary_index<detail::persistent_bet_index>();
auto& nonconst_refs_bet_object = const_cast<persistent_bet_index&>(refs_bet_object);
auto bet_iter = nonconst_refs_bet_object.internal.find(bet_canceled_op.bet_id);
assert(bet_iter != nonconst_refs_bet_object.internal.end());
if (bet_iter != nonconst_refs_bet_object.internal.end())
{
// ilog("Adding bet_canceled_operation ${canceled_id} to bet ${bet_id}'s associated operations",
// ("canceled_id", op.id)("bet_id", bet_canceled_op.bet_id));
if (is_operation_history_object_stored(op.id))
db.modify(*bet_iter, [&]( persistent_bet_object& obj ) {
obj.associated_operations.emplace_back(op.id);
});
bet_iter->second.associated_operations.emplace_back(op.id);
}
}
else if ( op.op.which() == operation::tag<bet_adjusted_operation>::value )
{
const bet_adjusted_operation& bet_adjusted_op = op.op.get<bet_adjusted_operation>();
auto& persistent_bets_by_bet_id = db.get_index_type<persistent_bet_index>().indices().get<by_bet_id>();
auto bet_iter = persistent_bets_by_bet_id.find(bet_adjusted_op.bet_id);
assert(bet_iter != persistent_bets_by_bet_id.end());
if (bet_iter != persistent_bets_by_bet_id.end())
const auto &idx_bet_object = db.get_index_type<bet_object_index>();
const auto &aidx_bet_object = dynamic_cast<const base_primary_index &>(idx_bet_object);
const auto &refs_bet_object = aidx_bet_object.get_secondary_index<detail::persistent_bet_index>();
auto& nonconst_refs_bet_object = const_cast<persistent_bet_index&>(refs_bet_object);
auto bet_iter = nonconst_refs_bet_object.internal.find(bet_adjusted_op.bet_id);
assert(bet_iter != nonconst_refs_bet_object.internal.end());
if (bet_iter != nonconst_refs_bet_object.internal.end())
{
// ilog("Adding bet_adjusted_operation ${adjusted_id} to bet ${bet_id}'s associated operations",
// ("adjusted_id", op.id)("bet_id", bet_adjusted_op.bet_id));
if (is_operation_history_object_stored(op.id))
db.modify(*bet_iter, [&]( persistent_bet_object& obj ) {
obj.associated_operations.emplace_back(op.id);
});
bet_iter->second.associated_operations.emplace_back(op.id);
}
}
@ -472,31 +416,21 @@ void bookie_plugin::plugin_initialize(const boost::program_options::variables_ma
database().new_objects.connect([this](const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts) { my->on_objects_new(ids); });
database().removed_objects.connect([this](const vector<object_id_type>& ids, const vector<const object*>& objs, const flat_set<account_id_type>& impacted_accounts) { my->on_objects_removed(ids); });
//auto event_index =
database().add_index<primary_index<detail::persistent_event_index> >();
database().add_index<primary_index<detail::persistent_betting_market_group_index> >();
database().add_index<primary_index<detail::persistent_betting_market_index> >();
database().add_index<primary_index<detail::persistent_bet_index> >();
const primary_index<bet_object_index>& bet_object_idx = database().get_index_type<primary_index<bet_object_index> >();
primary_index<bet_object_index>& nonconst_bet_object_idx = const_cast<primary_index<bet_object_index>&>(bet_object_idx);
detail::persistent_bet_object_helper* persistent_bet_object_helper_index = nonconst_bet_object_idx.add_secondary_index<detail::persistent_bet_object_helper>();
persistent_bet_object_helper_index->set_plugin_instance(this);
nonconst_bet_object_idx.add_secondary_index<detail::persistent_bet_index>();
const primary_index<betting_market_object_index>& betting_market_object_idx = database().get_index_type<primary_index<betting_market_object_index> >();
primary_index<betting_market_object_index>& nonconst_betting_market_object_idx = const_cast<primary_index<betting_market_object_index>&>(betting_market_object_idx);
detail::persistent_betting_market_object_helper* persistent_betting_market_object_helper_index = nonconst_betting_market_object_idx.add_secondary_index<detail::persistent_betting_market_object_helper>();
persistent_betting_market_object_helper_index->set_plugin_instance(this);
nonconst_betting_market_object_idx.add_secondary_index<detail::persistent_betting_market_index>();
const primary_index<betting_market_group_object_index>& betting_market_group_object_idx = database().get_index_type<primary_index<betting_market_group_object_index> >();
primary_index<betting_market_group_object_index>& nonconst_betting_market_group_object_idx = const_cast<primary_index<betting_market_group_object_index>&>(betting_market_group_object_idx);
detail::persistent_betting_market_group_object_helper* persistent_betting_market_group_object_helper_index = nonconst_betting_market_group_object_idx.add_secondary_index<detail::persistent_betting_market_group_object_helper>();
persistent_betting_market_group_object_helper_index->set_plugin_instance(this);
nonconst_betting_market_group_object_idx.add_secondary_index<detail::persistent_betting_market_group_index>();
const primary_index<event_object_index>& event_object_idx = database().get_index_type<primary_index<event_object_index> >();
primary_index<event_object_index>& nonconst_event_object_idx = const_cast<primary_index<event_object_index>&>(event_object_idx);
detail::persistent_event_object_helper* persistent_event_object_helper_index = nonconst_event_object_idx.add_secondary_index<detail::persistent_event_object_helper>();
persistent_event_object_helper_index->set_plugin_instance(this);
nonconst_event_object_idx.add_secondary_index<detail::persistent_event_index>();
ilog("bookie plugin: plugin_startup() end");
}

View file

@ -29,39 +29,21 @@
namespace graphene { namespace bookie {
using namespace chain;
enum bookie_object_type
{
persistent_event_object_type,
persistent_betting_market_group_object_type,
persistent_betting_market_object_type,
persistent_bet_object_type,
BOOKIE_OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
};
namespace detail
{
class persistent_event_object : public graphene::db::abstract_object<persistent_event_object>
/**
* @brief This secondary index will allow a reverse lookup of all events that happened
*/
class persistent_event_index : public secondary_index
{
public:
static const uint8_t space_id = bookie_objects;
static const uint8_t type_id = persistent_event_object_type;
public:
virtual void object_inserted( const object& obj ) override;
virtual void object_modified( const object& after ) override;
event_object ephemeral_event_object;
event_id_type get_event_id() const { return ephemeral_event_object.id; }
map< event_id_type, event_object > ephemeral_event_object;
};
typedef object_id<bookie_objects, persistent_event_object_type, persistent_event_object> persistent_event_id_type;
struct by_event_id;
typedef multi_index_container<
persistent_event_object,
indexed_by<
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
ordered_unique<tag<by_event_id>, const_mem_fun<persistent_event_object, event_id_type, &persistent_event_object::get_event_id> > > > persistent_event_multi_index_type;
typedef generic_index<persistent_event_object, persistent_event_multi_index_type> persistent_event_index;
#if 0 // we no longer have competitors, just leaving this here as an example of how to do a secondary index
class events_by_competitor_index : public secondary_index
{
@ -101,95 +83,122 @@ void events_by_competitor_index::object_modified( const object& after )
}
#endif
//////////// betting_market_group_object //////////////////
class persistent_betting_market_group_object : public graphene::db::abstract_object<persistent_betting_market_group_object>
/**
* @brief This secondary index will allow a reverse lookup of all betting_market_group that happened
*/
class persistent_betting_market_group_index : public secondary_index
{
public:
static const uint8_t space_id = bookie_objects;
static const uint8_t type_id = persistent_betting_market_group_object_type;
public:
struct internal_type
{
internal_type() = default;
internal_type(const betting_market_group_object& other)
: ephemeral_betting_market_group_object{other}
{}
internal_type& operator=(const betting_market_group_object& other)
{
ephemeral_betting_market_group_object = other;
return *this;
}
friend bool operator==(const internal_type& lhs, const internal_type& rhs);
friend bool operator<(const internal_type& lhs, const internal_type& rhs);
friend bool operator>(const internal_type& lhs, const internal_type& rhs);
betting_market_group_object ephemeral_betting_market_group_object;
share_type total_matched_bets_amount;
};
betting_market_group_id_type get_betting_market_group_id() const { return ephemeral_betting_market_group_object.id; }
public:
virtual void object_inserted( const object& obj ) override;
virtual void object_modified( const object& after ) override;
map< betting_market_group_id_type, internal_type > internal;
};
struct by_betting_market_group_id;
typedef multi_index_container<
persistent_betting_market_group_object,
indexed_by<
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
ordered_unique<tag<by_betting_market_group_id>, const_mem_fun<persistent_betting_market_group_object, betting_market_group_id_type, &persistent_betting_market_group_object::get_betting_market_group_id> > > > persistent_betting_market_group_multi_index_type;
typedef generic_index<persistent_betting_market_group_object, persistent_betting_market_group_multi_index_type> persistent_betting_market_group_index;
//////////// betting_market_object //////////////////
class persistent_betting_market_object : public graphene::db::abstract_object<persistent_betting_market_object>
inline bool operator==(const persistent_betting_market_group_index::internal_type& lhs, const persistent_betting_market_group_index::internal_type& rhs)
{
public:
static const uint8_t space_id = bookie_objects;
static const uint8_t type_id = persistent_betting_market_object_type;
return lhs.ephemeral_betting_market_group_object == rhs.ephemeral_betting_market_group_object;
}
betting_market_object ephemeral_betting_market_object;
inline bool operator<(const persistent_betting_market_group_index::internal_type& lhs, const persistent_betting_market_group_index::internal_type& rhs)
{
return lhs.ephemeral_betting_market_group_object < rhs.ephemeral_betting_market_group_object;
}
share_type total_matched_bets_amount;
inline bool operator>(const persistent_betting_market_group_index::internal_type& lhs, const persistent_betting_market_group_index::internal_type& rhs)
{
return !operator<(lhs, rhs);
}
betting_market_id_type get_betting_market_id() const { return ephemeral_betting_market_object.id; }
/**
* @brief This secondary index will allow a reverse lookup of all betting_market_object that happened
*/
class persistent_betting_market_index : public secondary_index
{
public:
virtual void object_inserted( const object& obj ) override;
virtual void object_modified( const object& after ) override;
map< betting_market_id_type, betting_market_object > ephemeral_betting_market_object;
};
struct by_betting_market_id;
typedef multi_index_container<
persistent_betting_market_object,
indexed_by<
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
ordered_unique<tag<by_betting_market_id>, const_mem_fun<persistent_betting_market_object, betting_market_id_type, &persistent_betting_market_object::get_betting_market_id> > > > persistent_betting_market_multi_index_type;
typedef generic_index<persistent_betting_market_object, persistent_betting_market_multi_index_type> persistent_betting_market_index;
//////////// bet_object //////////////////
class persistent_bet_object : public graphene::db::abstract_object<persistent_bet_object>
/**
* @brief This secondary index will allow a reverse lookup of all bet_object that happened
*/
class persistent_bet_index : public secondary_index
{
public:
static const uint8_t space_id = bookie_objects;
static const uint8_t type_id = persistent_bet_object_type;
public:
struct internal_type
{
internal_type() = default;
bet_object ephemeral_bet_object;
internal_type(const bet_object& other)
: ephemeral_bet_object{other}
{}
// total amount of the bet that matched
share_type amount_matched;
internal_type& operator=(const bet_object& other)
{
ephemeral_bet_object = other;
return *this;
}
std::vector<operation_history_id_type> associated_operations;
bet_id_type get_bet_id() const { return ephemeral_bet_object.id; }
account_id_type get_bettor_id() const { return ephemeral_bet_object.bettor_id; }
bool is_matched() const { return amount_matched != share_type(); }
friend bool operator==(const internal_type& lhs, const internal_type& rhs);
friend bool operator<(const internal_type& lhs, const internal_type& rhs);
friend bool operator>(const internal_type& lhs, const internal_type& rhs);
bet_object ephemeral_bet_object;
// total amount of the bet that matched
share_type amount_matched;
std::vector<operation_history_id_type> associated_operations;
};
public:
virtual void object_inserted( const object& obj ) override;
virtual void object_modified( const object& after ) override;
map< bet_id_type, internal_type > internal;
};
struct by_bet_id;
struct by_bettor_id;
typedef multi_index_container<
persistent_bet_object,
indexed_by<
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
ordered_unique<tag<by_bet_id>, const_mem_fun<persistent_bet_object, bet_id_type, &persistent_bet_object::get_bet_id> >,
ordered_unique<tag<by_bettor_id>,
composite_key<
persistent_bet_object,
const_mem_fun<persistent_bet_object, account_id_type, &persistent_bet_object::get_bettor_id>,
const_mem_fun<persistent_bet_object, bool, &persistent_bet_object::is_matched>,
const_mem_fun<persistent_bet_object, bet_id_type, &persistent_bet_object::get_bet_id> >,
composite_key_compare<
std::less<account_id_type>,
std::less<bool>,
std::greater<bet_id_type> > > > > persistent_bet_multi_index_type;
inline bool operator==(const persistent_bet_index::internal_type& lhs, const persistent_bet_index::internal_type& rhs)
{
return lhs.ephemeral_bet_object == rhs.ephemeral_bet_object;
}
typedef generic_index<persistent_bet_object, persistent_bet_multi_index_type> persistent_bet_index;
inline bool operator<(const persistent_bet_index::internal_type& lhs, const persistent_bet_index::internal_type& rhs)
{
return lhs.ephemeral_bet_object < rhs.ephemeral_bet_object;
}
inline bool operator>(const persistent_bet_index::internal_type& lhs, const persistent_bet_index::internal_type& rhs)
{
return !operator<(lhs, rhs);
}
} } } //graphene::bookie::detail
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_event_object, (graphene::db::object), (ephemeral_event_object) )
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_betting_market_group_object, (graphene::db::object), (ephemeral_betting_market_group_object)(total_matched_bets_amount) )
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_betting_market_object, (graphene::db::object), (ephemeral_betting_market_object) )
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_bet_object, (graphene::db::object), (ephemeral_bet_object)(amount_matched)(associated_operations) )

View file

@ -4,7 +4,7 @@ add_library( graphene_delayed_node
delayed_node_plugin.cpp
)
target_link_libraries( graphene_delayed_node PRIVATE graphene_plugin graphene_accounts_list graphene_affiliate_stats graphene_bookie graphene_debug_witness graphene_elasticsearch graphene_market_history )
target_link_libraries( graphene_delayed_node PRIVATE graphene_plugin graphene_accounts_list graphene_affiliate_stats graphene_bookie graphene_debug_witness graphene_elasticsearch graphene_market_history peerplays_sidechain )
target_include_directories( graphene_delayed_node
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )

View file

@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
#include <boost/algorithm/string.hpp>
#include <graphene/elasticsearch/elasticsearch_plugin.hpp>
#include <graphene/chain/impacted.hpp>
#include <graphene/chain/account_evaluator.hpp>
@ -33,6 +34,15 @@ namespace graphene { namespace elasticsearch {
namespace detail
{
const std::string generateIndexName(const fc::time_point_sec& block_date, const std::string& _elasticsearch_index_prefix)
{
auto block_date_string = block_date.to_iso_string();
std::vector<std::string> parts;
boost::split(parts, block_date_string, boost::is_any_of("-"));
std::string index_name = _elasticsearch_index_prefix + parts[0] + "-" + parts[1];
return index_name;
}
class elasticsearch_plugin_impl
{
public:
@ -48,6 +58,9 @@ class elasticsearch_plugin_impl
return _self.database();
}
friend class graphene::elasticsearch::elasticsearch_plugin;
private:
elasticsearch_plugin& _self;
primary_index< operation_history_index >* _oho_index;
@ -75,6 +88,8 @@ class elasticsearch_plugin_impl
std::string bulk_line;
std::string index_name;
bool is_sync = false;
bool is_es_version_7_or_above = true;
private:
bool add_elasticsearch( const account_id_type account_id, const optional<operation_history_object>& oho, const uint32_t block_number );
const account_transaction_history_object& addNewEntry(const account_statistics_object& stats_obj,
@ -91,6 +106,7 @@ class elasticsearch_plugin_impl
void createBulkLine(const account_transaction_history_object& ath);
void prepareBulk(const account_transaction_history_id_type& ath_id);
void populateESstruct();
void init_program_options(const boost::program_options::variables_map& options);
};
elasticsearch_plugin_impl::~elasticsearch_plugin_impl()
@ -105,7 +121,7 @@ elasticsearch_plugin_impl::~elasticsearch_plugin_impl()
bool elasticsearch_plugin_impl::update_account_histories( const signed_block& b )
{
checkState(b.timestamp);
index_name = graphene::utilities::generateIndexName(b.timestamp, _elasticsearch_index_prefix);
index_name = generateIndexName(b.timestamp, _elasticsearch_index_prefix);
graphene::chain::database& db = database();
const vector<optional< operation_history_object > >& hist = db.get_applied_operations();
@ -229,6 +245,113 @@ void elasticsearch_plugin_impl::getOperationType(const optional <operation_histo
op_type = oho->op.which();
}
struct adaptor_struct
{
variant adapt(const variant_object& op)
{
fc::mutable_variant_object o(op);
vector<string> keys_to_rename;
for (auto& i : o)
{
auto& element = i.value();
if (element.is_object())
{
const string& name = i.key();
const auto& vo = element.get_object();
if (vo.contains(name.c_str()))
keys_to_rename.emplace_back(name);
element = adapt(vo);
}
else if (element.is_array())
adapt(element.get_array());
}
for (const auto& i : keys_to_rename)
{
string new_name = i + "_";
o[new_name] = variant(o[i]);
o.erase(i);
}
if (o.find("memo") != o.end())
{
auto& memo = o["memo"];
if (memo.is_string())
{
o["memo_"] = o["memo"];
o.erase("memo");
}
else if (memo.is_object())
{
fc::mutable_variant_object tmp(memo.get_object());
if (tmp.find("nonce") != tmp.end())
{
tmp["nonce"] = tmp["nonce"].as_string();
o["memo"] = tmp;
}
}
}
if (o.find("new_parameters") != o.end())
{
auto& tmp = o["new_parameters"];
if (tmp.is_object())
{
fc::mutable_variant_object tmp2(tmp.get_object());
if (tmp2.find("current_fees") != tmp2.end())
{
tmp2.erase("current_fees");
o["new_parameters"] = tmp2;
}
}
}
if (o.find("owner") != o.end() && o["owner"].is_string())
{
o["owner_"] = o["owner"].as_string();
o.erase("owner");
}
if (o.find("proposed_ops") != o.end())
{
o["proposed_ops"] = fc::json::to_string(o["proposed_ops"]);
}
if (o.find("initializer") != o.end())
{
o["initializer"] = fc::json::to_string(o["initializer"]);
}
if (o.find("policy") != o.end())
{
o["policy"] = fc::json::to_string(o["policy"]);
}
if (o.find("predicates") != o.end())
{
o["predicates"] = fc::json::to_string(o["predicates"]);
}
if (o.find("active_special_authority") != o.end())
{
o["active_special_authority"] = fc::json::to_string(o["active_special_authority"]);
}
if (o.find("owner_special_authority") != o.end())
{
o["owner_special_authority"] = fc::json::to_string(o["owner_special_authority"]);
}
variant v;
fc::to_variant(o, v, FC_PACK_MAX_DEPTH);
return v;
}
void adapt(fc::variants& v)
{
for (auto& array_element : v)
{
if (array_element.is_object())
array_element = adapt(array_element.get_object());
else if (array_element.is_array())
adapt(array_element.get_array());
else
array_element = array_element.as_string();
}
}
};
void elasticsearch_plugin_impl::doOperationHistory(const optional <operation_history_object>& oho)
{
os.trx_in_block = oho->trx_in_block;
@ -255,6 +378,61 @@ void elasticsearch_plugin_impl::doBlock(uint32_t trx_in_block, const signed_bloc
bs.trx_id = trx_id;
}
struct operation_visitor
{
using result_type = void;
share_type fee_amount;
asset_id_type fee_asset;
asset_id_type transfer_asset_id;
share_type transfer_amount;
account_id_type transfer_from;
account_id_type transfer_to;
void operator()( const graphene::chain::transfer_operation& o )
{
fee_asset = o.fee.asset_id;
fee_amount = o.fee.amount;
transfer_asset_id = o.amount.asset_id;
transfer_amount = o.amount.amount;
transfer_from = o.from;
transfer_to = o.to;
}
object_id_type fill_order_id;
account_id_type fill_account_id;
asset_id_type fill_pays_asset_id;
share_type fill_pays_amount;
asset_id_type fill_receives_asset_id;
share_type fill_receives_amount;
//double fill_fill_price;
//bool fill_is_maker;
void operator()( const graphene::chain::fill_order_operation& o )
{
fee_asset = o.fee.asset_id;
fee_amount = o.fee.amount;
fill_order_id = o.order_id;
fill_account_id = o.account_id;
fill_pays_asset_id = o.pays.asset_id;
fill_pays_amount = o.pays.amount;
fill_receives_asset_id = o.receives.asset_id;
fill_receives_amount = o.receives.amount;
//fill_fill_price = o.fill_price.to_real();
//fill_is_maker = o.is_maker;
}
template<typename T>
void operator()( const T& o )
{
fee_asset = o.fee.asset_id;
fee_amount = o.fee.amount;
}
};
void elasticsearch_plugin_impl::doVisitor(const optional <operation_history_object>& oho)
{
graphene::chain::database& db = database();
@ -380,7 +558,8 @@ void elasticsearch_plugin_impl::prepareBulk(const account_transaction_history_id
const std::string _id = fc::json::to_string(ath_id);
fc::mutable_variant_object bulk_header;
bulk_header["_index"] = index_name;
bulk_header["_type"] = "data";
if( !is_es_version_7_or_above )
bulk_header["_type"] = "_doc";
bulk_header["_id"] = fc::to_string(ath_id.space_id) + "." + fc::to_string(ath_id.type_id) + "."
+ fc::to_string(ath_id.instance.value);
prepare = graphene::utilities::createBulk(bulk_header, std::move(bulk_line));
@ -428,6 +607,43 @@ void elasticsearch_plugin_impl::populateESstruct()
es.query = "";
}
void elasticsearch_plugin_impl::init_program_options(const boost::program_options::variables_map& options)
{
if (options.count("elasticsearch-node-url")) {
_elasticsearch_node_url = options["elasticsearch-node-url"].as<std::string>();
}
if (options.count("elasticsearch-bulk-replay")) {
_elasticsearch_bulk_replay = options["elasticsearch-bulk-replay"].as<uint32_t>();
}
if (options.count("elasticsearch-bulk-sync")) {
_elasticsearch_bulk_sync = options["elasticsearch-bulk-sync"].as<uint32_t>();
}
if (options.count("elasticsearch-visitor")) {
_elasticsearch_visitor = options["elasticsearch-visitor"].as<bool>();
}
if (options.count("elasticsearch-basic-auth")) {
_elasticsearch_basic_auth = options["elasticsearch-basic-auth"].as<std::string>();
}
if (options.count("elasticsearch-index-prefix")) {
_elasticsearch_index_prefix = options["elasticsearch-index-prefix"].as<std::string>();
}
if (options.count("elasticsearch-operation-object")) {
_elasticsearch_operation_object = options["elasticsearch-operation-object"].as<bool>();
}
if (options.count("elasticsearch-start-es-after-block")) {
_elasticsearch_start_es_after_block = options["elasticsearch-start-es-after-block"].as<uint32_t>();
}
if (options.count("elasticsearch-operation-string")) {
_elasticsearch_operation_string = options["elasticsearch-operation-string"].as<bool>();
}
if (options.count("elasticsearch-mode")) {
const auto option_number = options["elasticsearch-mode"].as<uint16_t>();
if(option_number > mode::all)
FC_THROW_EXCEPTION(graphene::chain::plugin_exception, "Elasticsearch mode not valid");
_elasticsearch_mode = static_cast<mode>(options["elasticsearch-mode"].as<uint16_t>());
}
}
} // end namespace detail
elasticsearch_plugin::elasticsearch_plugin() :
@ -480,42 +696,12 @@ void elasticsearch_plugin::plugin_set_program_options(
void elasticsearch_plugin::plugin_initialize(const boost::program_options::variables_map& options)
{
ilog("elasticsearch ACCOUNT HISTORY: plugin_initialize() begin");
my->_oho_index = database().add_index< primary_index< operation_history_index > >();
database().add_index< primary_index< account_transaction_history_index > >();
if (options.count("elasticsearch-node-url")) {
my->_elasticsearch_node_url = options["elasticsearch-node-url"].as<std::string>();
}
if (options.count("elasticsearch-bulk-replay")) {
my->_elasticsearch_bulk_replay = options["elasticsearch-bulk-replay"].as<uint32_t>();
}
if (options.count("elasticsearch-bulk-sync")) {
my->_elasticsearch_bulk_sync = options["elasticsearch-bulk-sync"].as<uint32_t>();
}
if (options.count("elasticsearch-visitor")) {
my->_elasticsearch_visitor = options["elasticsearch-visitor"].as<bool>();
}
if (options.count("elasticsearch-basic-auth")) {
my->_elasticsearch_basic_auth = options["elasticsearch-basic-auth"].as<std::string>();
}
if (options.count("elasticsearch-index-prefix")) {
my->_elasticsearch_index_prefix = options["elasticsearch-index-prefix"].as<std::string>();
}
if (options.count("elasticsearch-operation-object")) {
my->_elasticsearch_operation_object = options["elasticsearch-operation-object"].as<bool>();
}
if (options.count("elasticsearch-start-es-after-block")) {
my->_elasticsearch_start_es_after_block = options["elasticsearch-start-es-after-block"].as<uint32_t>();
}
if (options.count("elasticsearch-operation-string")) {
my->_elasticsearch_operation_string = options["elasticsearch-operation-string"].as<bool>();
}
if (options.count("elasticsearch-mode")) {
const auto option_number = options["elasticsearch-mode"].as<uint16_t>();
if(option_number > mode::all)
FC_THROW_EXCEPTION(graphene::chain::plugin_exception, "Elasticsearch mode not valid");
my->_elasticsearch_mode = static_cast<mode>(options["elasticsearch-mode"].as<uint16_t>());
}
my->init_program_options( options );
if(my->_elasticsearch_mode != mode::only_query) {
if (my->_elasticsearch_mode == mode::all && !my->_elasticsearch_operation_string)
@ -528,10 +714,7 @@ void elasticsearch_plugin::plugin_initialize(const boost::program_options::varia
"Error populating ES database, we are going to keep trying.");
});
}
}
void elasticsearch_plugin::plugin_startup()
{
graphene::utilities::ES es;
es.curl = my->curl;
es.elasticsearch_url = my->_elasticsearch_node_url;
@ -539,7 +722,17 @@ void elasticsearch_plugin::plugin_startup()
if(!graphene::utilities::checkES(es))
FC_THROW_EXCEPTION(fc::exception, "ES database is not up in url ${url}", ("url", my->_elasticsearch_node_url));
graphene::utilities::checkESVersion7OrAbove(es, my->is_es_version_7_or_above);
ilog("elasticsearch ACCOUNT HISTORY: plugin_initialize() end");
}
void elasticsearch_plugin::plugin_startup()
{
ilog("elasticsearch ACCOUNT HISTORY: plugin_startup() begin");
// Nothing to do
ilog("elasticsearch ACCOUNT HISTORY: plugin_startup() end");
}
operation_history_object elasticsearch_plugin::get_operation_by_id(operation_history_id_type id)
@ -655,7 +848,7 @@ graphene::utilities::ES elasticsearch_plugin::prepareHistoryQuery(string query)
es.curl = curl;
es.elasticsearch_url = my->_elasticsearch_node_url;
es.index_prefix = my->_elasticsearch_index_prefix;
es.endpoint = es.index_prefix + "*/data/_search";
es.endpoint = es.index_prefix + "*/_doc/_search";
es.query = query;
return es;

View file

@ -79,62 +79,6 @@ class elasticsearch_plugin : public graphene::app::plugin
graphene::utilities::ES prepareHistoryQuery(string query);
};
struct operation_visitor
{
typedef void result_type;
share_type fee_amount;
asset_id_type fee_asset;
asset_id_type transfer_asset_id;
share_type transfer_amount;
account_id_type transfer_from;
account_id_type transfer_to;
void operator()( const graphene::chain::transfer_operation& o )
{
fee_asset = o.fee.asset_id;
fee_amount = o.fee.amount;
transfer_asset_id = o.amount.asset_id;
transfer_amount = o.amount.amount;
transfer_from = o.from;
transfer_to = o.to;
}
object_id_type fill_order_id;
account_id_type fill_account_id;
asset_id_type fill_pays_asset_id;
share_type fill_pays_amount;
asset_id_type fill_receives_asset_id;
share_type fill_receives_amount;
//double fill_fill_price;
//bool fill_is_maker;
void operator()( const graphene::chain::fill_order_operation& o )
{
fee_asset = o.fee.asset_id;
fee_amount = o.fee.amount;
fill_order_id = o.order_id;
fill_account_id = o.account_id;
fill_pays_asset_id = o.pays.asset_id;
fill_pays_amount = o.pays.amount;
fill_receives_asset_id = o.receives.asset_id;
fill_receives_amount = o.receives.amount;
//fill_fill_price = o.fill_price.to_real();
//fill_is_maker = o.is_maker;
}
template<typename T>
void operator()( const T& o )
{
fee_asset = o.fee.asset_id;
fee_amount = o.fee.amount;
}
};
struct operation_history_struct {
int trx_in_block;
int op_in_trx;
@ -197,113 +141,6 @@ struct bulk_struct {
optional<visitor_struct> additional_data;
};
struct adaptor_struct {
variant adapt(const variant_object& op)
{
fc::mutable_variant_object o(op);
vector<string> keys_to_rename;
for (auto i = o.begin(); i != o.end(); ++i)
{
auto& element = (*i).value();
if (element.is_object())
{
const string& name = (*i).key();
auto& vo = element.get_object();
if (vo.contains(name.c_str()))
keys_to_rename.emplace_back(name);
element = adapt(vo);
}
else if (element.is_array())
adapt(element.get_array());
}
for (const auto& i : keys_to_rename)
{
string new_name = i + "_";
o[new_name] = variant(o[i]);
o.erase(i);
}
if (o.find("memo") != o.end())
{
auto& memo = o["memo"];
if (memo.is_string())
{
o["memo_"] = o["memo"];
o.erase("memo");
}
else if (memo.is_object())
{
fc::mutable_variant_object tmp(memo.get_object());
if (tmp.find("nonce") != tmp.end())
{
tmp["nonce"] = tmp["nonce"].as_string();
o["memo"] = tmp;
}
}
}
if (o.find("new_parameters") != o.end())
{
auto& tmp = o["new_parameters"];
if (tmp.is_object())
{
fc::mutable_variant_object tmp2(tmp.get_object());
if (tmp2.find("current_fees") != tmp2.end())
{
tmp2.erase("current_fees");
o["new_parameters"] = tmp2;
}
}
}
if (o.find("owner") != o.end() && o["owner"].is_string())
{
o["owner_"] = o["owner"].as_string();
o.erase("owner");
}
if (o.find("proposed_ops") != o.end())
{
o["proposed_ops"] = fc::json::to_string(o["proposed_ops"]);
}
if (o.find("initializer") != o.end())
{
o["initializer"] = fc::json::to_string(o["initializer"]);
}
if (o.find("policy") != o.end())
{
o["policy"] = fc::json::to_string(o["policy"]);
}
if (o.find("predicates") != o.end())
{
o["predicates"] = fc::json::to_string(o["predicates"]);
}
if (o.find("active_special_authority") != o.end())
{
o["active_special_authority"] = fc::json::to_string(o["active_special_authority"]);
}
if (o.find("owner_special_authority") != o.end())
{
o["owner_special_authority"] = fc::json::to_string(o["owner_special_authority"]);
}
variant v;
fc::to_variant(o, v, FC_PACK_MAX_DEPTH);
return v;
}
void adapt(fc::variants& v)
{
for (auto& array_element : v)
{
if (array_element.is_object())
array_element = adapt(array_element.get_object());
else if (array_element.is_array())
adapt(array_element.get_array());
else
array_element = array_element.as_string();
}
}
};
} } //graphene::elasticsearch
FC_REFLECT_ENUM( graphene::elasticsearch::mode, (only_save)(only_query)(all) )

View file

@ -66,6 +66,9 @@ class es_objects_plugin_impl
bool genesis();
void remove_from_database(object_id_type id, std::string index);
friend class graphene::es_objects::es_objects_plugin;
private:
es_objects_plugin& _self;
std::string _es_objects_elasticsearch_url = "http://localhost:9200/";
std::string _es_objects_auth = "";
@ -97,10 +100,12 @@ class es_objects_plugin_impl
uint32_t block_number;
fc::time_point_sec block_time;
bool is_es_version_7_or_above = true;
private:
template<typename T>
void prepareTemplate(T blockchain_object, string index_name);
void init_program_options(const boost::program_options::variables_map& options);
};
bool es_objects_plugin_impl::genesis()
@ -523,7 +528,8 @@ void es_objects_plugin_impl::remove_from_database( object_id_type id, std::strin
fc::mutable_variant_object delete_line;
delete_line["_id"] = string(id);
delete_line["_index"] = _es_objects_index_prefix + index;
delete_line["_type"] = "data";
if( !is_es_version_7_or_above )
delete_line["_type"] = "_doc";
fc::mutable_variant_object final_delete_line;
final_delete_line["delete"] = delete_line;
prepare.push_back(fc::json::to_string(final_delete_line));
@ -537,7 +543,8 @@ void es_objects_plugin_impl::prepareTemplate(T blockchain_object, string index_n
{
fc::mutable_variant_object bulk_header;
bulk_header["_index"] = _es_objects_index_prefix + index_name;
bulk_header["_type"] = "data";
if( !is_es_version_7_or_above )
bulk_header["_type"] = "_doc";
if(_es_objects_keep_only_current)
{
bulk_header["_id"] = string(blockchain_object.id);
@ -567,6 +574,72 @@ es_objects_plugin_impl::~es_objects_plugin_impl()
}
return;
}
void es_objects_plugin_impl::init_program_options(const boost::program_options::variables_map& options)
{
if (options.count("es-objects-elasticsearch-url")) {
_es_objects_elasticsearch_url = options["es-objects-elasticsearch-url"].as<std::string>();
}
if (options.count("es-objects-auth")) {
_es_objects_auth = options["es-objects-auth"].as<std::string>();
}
if (options.count("es-objects-bulk-replay")) {
_es_objects_bulk_replay = options["es-objects-bulk-replay"].as<uint32_t>();
}
if (options.count("es-objects-bulk-sync")) {
_es_objects_bulk_sync = options["es-objects-bulk-sync"].as<uint32_t>();
}
if (options.count("es-objects-proposals")) {
_es_objects_proposals = options["es-objects-proposals"].as<bool>();
}
if (options.count("es-objects-accounts")) {
_es_objects_accounts = options["es-objects-accounts"].as<bool>();
}
if (options.count("es-objects-assets")) {
_es_objects_assets = options["es-objects-assets"].as<bool>();
}
if (options.count("es-objects-balances")) {
_es_objects_balances = options["es-objects-balances"].as<bool>();
}
if (options.count("es-objects-limit-orders")) {
_es_objects_limit_orders = options["es-objects-limit-orders"].as<bool>();
}
if (options.count("es-objects-asset-bitasset")) {
_es_objects_asset_bitasset = options["es-objects-asset-bitasset"].as<bool>();
}
if (options.count("es-objects-account-role")) {
_es_objects_balances = options["es-objects-account-role"].as<bool>();
}
if (options.count("es-objects-committee-member")) {
_es_objects_balances = options["es-objects-committee-member"].as<bool>();
}
if (options.count("es-objects-nft")) {
_es_objects_balances = options["es-objects-nft"].as<bool>();
}
if (options.count("es-objects-son")) {
_es_objects_balances = options["es-objects-son"].as<bool>();
}
if (options.count("es-objects-transaction")) {
_es_objects_balances = options["es-objects-transaction"].as<bool>();
}
if (options.count("es-objects-vesting-balance")) {
_es_objects_balances = options["es-objects-vesting-balance"].as<bool>();
}
if (options.count("es-objects-witness")) {
_es_objects_balances = options["es-objects-witness"].as<bool>();
}
if (options.count("es-objects-worker")) {
_es_objects_balances = options["es-objects-worker"].as<bool>();
}
if (options.count("es-objects-index-prefix")) {
_es_objects_index_prefix = options["es-objects-index-prefix"].as<std::string>();
}
if (options.count("es-objects-keep-only-current")) {
_es_objects_keep_only_current = options["es-objects-keep-only-current"].as<bool>();
}
if (options.count("es-objects-start-es-after-block")) {
_es_objects_start_es_after_block = options["es-objects-start-es-after-block"].as<uint32_t>();
}
}
} // end namespace detail
@ -627,69 +700,9 @@ void es_objects_plugin::plugin_set_program_options(
void es_objects_plugin::plugin_initialize(const boost::program_options::variables_map& options)
{
if (options.count("es-objects-elasticsearch-url")) {
my->_es_objects_elasticsearch_url = options["es-objects-elasticsearch-url"].as<std::string>();
}
if (options.count("es-objects-auth")) {
my->_es_objects_auth = options["es-objects-auth"].as<std::string>();
}
if (options.count("es-objects-bulk-replay")) {
my->_es_objects_bulk_replay = options["es-objects-bulk-replay"].as<uint32_t>();
}
if (options.count("es-objects-bulk-sync")) {
my->_es_objects_bulk_sync = options["es-objects-bulk-sync"].as<uint32_t>();
}
if (options.count("es-objects-proposals")) {
my->_es_objects_proposals = options["es-objects-proposals"].as<bool>();
}
if (options.count("es-objects-accounts")) {
my->_es_objects_accounts = options["es-objects-accounts"].as<bool>();
}
if (options.count("es-objects-assets")) {
my->_es_objects_assets = options["es-objects-assets"].as<bool>();
}
if (options.count("es-objects-balances")) {
my->_es_objects_balances = options["es-objects-balances"].as<bool>();
}
if (options.count("es-objects-limit-orders")) {
my->_es_objects_limit_orders = options["es-objects-limit-orders"].as<bool>();
}
if (options.count("es-objects-asset-bitasset")) {
my->_es_objects_asset_bitasset = options["es-objects-asset-bitasset"].as<bool>();
}
if (options.count("es-objects-account-role")) {
my->_es_objects_balances = options["es-objects-account-role"].as<bool>();
}
if (options.count("es-objects-committee-member")) {
my->_es_objects_balances = options["es-objects-committee-member"].as<bool>();
}
if (options.count("es-objects-nft")) {
my->_es_objects_balances = options["es-objects-nft"].as<bool>();
}
if (options.count("es-objects-son")) {
my->_es_objects_balances = options["es-objects-son"].as<bool>();
}
if (options.count("es-objects-transaction")) {
my->_es_objects_balances = options["es-objects-transaction"].as<bool>();
}
if (options.count("es-objects-vesting-balance")) {
my->_es_objects_balances = options["es-objects-vesting-balance"].as<bool>();
}
if (options.count("es-objects-witness")) {
my->_es_objects_balances = options["es-objects-witness"].as<bool>();
}
if (options.count("es-objects-worker")) {
my->_es_objects_balances = options["es-objects-worker"].as<bool>();
}
if (options.count("es-objects-index-prefix")) {
my->_es_objects_index_prefix = options["es-objects-index-prefix"].as<std::string>();
}
if (options.count("es-objects-keep-only-current")) {
my->_es_objects_keep_only_current = options["es-objects-keep-only-current"].as<bool>();
}
if (options.count("es-objects-start-es-after-block")) {
my->_es_objects_start_es_after_block = options["es-objects-start-es-after-block"].as<uint32_t>();
}
ilog("elasticsearch OBJECTS: plugin_initialize() begin");
my->init_program_options( options );
database().applied_block.connect([this](const signed_block &b) {
if(b.block_num() == 1 && my->_es_objects_start_es_after_block == 0) {
@ -721,10 +734,7 @@ void es_objects_plugin::plugin_initialize(const boost::program_options::variable
"Error deleting object from ES database, we are going to keep trying.");
}
});
}
void es_objects_plugin::plugin_startup()
{
graphene::utilities::ES es;
es.curl = my->curl;
es.elasticsearch_url = my->_es_objects_elasticsearch_url;
@ -733,7 +743,17 @@ void es_objects_plugin::plugin_startup()
if(!graphene::utilities::checkES(es))
FC_THROW_EXCEPTION(fc::exception, "ES database is not up in url ${url}", ("url", my->_es_objects_elasticsearch_url));
graphene::utilities::checkESVersion7OrAbove(es, my->is_es_version_7_or_above);
ilog("elasticsearch OBJECTS: plugin_initialize() end");
}
void es_objects_plugin::plugin_startup()
{
ilog("elasticsearch OBJECTS: plugin_startup() begin");
// Nothing to do
ilog("elasticsearch OBJECTS: plugin_startup() end");
}
} }

View file

@ -2,6 +2,7 @@ file(GLOB_RECURSE HEADERS "include/graphene/peerplays_sidechain/*.hpp")
add_library( peerplays_sidechain
peerplays_sidechain_plugin.cpp
sidechain_api.cpp
sidechain_net_manager.cpp
sidechain_net_handler.cpp
sidechain_net_handler_bitcoin.cpp
@ -23,19 +24,11 @@ add_library( peerplays_sidechain
)
if (ENABLE_DEV_FEATURES)
set(ENABLE_MULTIPLE_SONS 1)
set(ENABLE_PEERPLAYS_ASSET_DEPOSITS 1)
endif()
unset(ENABLE_DEV_FEATURES)
unset(ENABLE_DEV_FEATURES CACHE)
if (ENABLE_MULTIPLE_SONS)
message ("Multiple SONs per software instance are supported")
target_compile_definitions(peerplays_sidechain PRIVATE ENABLE_MULTIPLE_SONS)
endif()
unset(ENABLE_MULTIPLE_SONS)
unset(ENABLE_MULTIPLE_SONS CACHE)
if (ENABLE_PEERPLAYS_ASSET_DEPOSITS)
message ("Depositing Peerplays assets enabled")
target_compile_definitions(peerplays_sidechain PRIVATE ENABLE_PEERPLAYS_ASSET_DEPOSITS)

View file

@ -51,6 +51,7 @@ public:
fc::ecc::private_key get_private_key(chain::public_key_type public_key);
void log_son_proposal_retry(int op_type, object_id_type object_id);
bool can_son_participate(int op_type, object_id_type object_id);
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
};
}} // namespace graphene::peerplays_sidechain

View file

@ -0,0 +1,34 @@
#pragma once
#include <map>
#include <string>
#include <vector>
#include <fc/api.hpp>
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
namespace graphene { namespace app {
class application;
}} // namespace graphene::app
namespace graphene { namespace peerplays_sidechain {
namespace detail {
class sidechain_api_impl;
}
class sidechain_api {
public:
sidechain_api(app::application &_app);
virtual ~sidechain_api();
std::shared_ptr<detail::sidechain_api_impl> my;
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
};
}} // namespace graphene::peerplays_sidechain
FC_API(graphene::peerplays_sidechain::sidechain_api,
(get_son_listener_log))

View file

@ -47,6 +47,9 @@ public:
virtual std::string send_sidechain_transaction(const sidechain_transaction_object &sto) = 0;
virtual bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount) = 0;
void add_to_son_listener_log(std::string trx_id);
std::vector<std::string> get_son_listener_log();
protected:
peerplays_sidechain_plugin &plugin;
graphene::chain::database &database;
@ -56,6 +59,8 @@ protected:
std::map<std::string, std::string> private_keys;
std::vector<std::string> son_listener_log;
void on_applied_block(const signed_block &b);
private:

View file

@ -3,7 +3,7 @@
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
#include <string>
#include <zmq.hpp>
#include <zmq_addon.hpp>
#include <boost/signals2.hpp>
@ -22,6 +22,23 @@ public:
};
class bitcoin_rpc_client {
public:
enum class multi_type {
script,
address
};
struct multi_params {
multi_params(multi_type _type, const std::string &_address_or_script, const std::string &_label = "") :
type{_type},
address_or_script{_address_or_script},
label{_label} {
}
multi_type type;
std::string address_or_script;
std::string label;
};
public:
bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::string _user, std::string _password, std::string _wallet, std::string _wallet_password, bool _debug_rpc_calls);
@ -39,18 +56,20 @@ public:
std::string getaddressinfo(const std::string &address);
std::string getblock(const std::string &block_hash, int32_t verbosity = 2);
std::string getrawtransaction(const std::string &txid, const bool verbose = false);
std::string getnetworkinfo();
std::string gettransaction(const std::string &txid, const bool include_watch_only = false);
std::string getblockchaininfo();
void importaddress(const std::string &address_or_script, const std::string &label = "", const bool rescan = true, const bool p2sh = false);
void importmulti(const std::vector<multi_params> &address_or_script_array, const bool rescan = true);
std::vector<btc_txout> listunspent(const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
std::vector<btc_txout> listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
std::string loadwallet(const std::string &filename);
std::string sendrawtransaction(const std::string &tx_hex);
std::string signrawtransactionwithwallet(const std::string &tx_hash);
std::string unloadwallet(const std::string &filename);
//std::string walletlock();
std::string walletlock();
std::string walletprocesspsbt(std::string const &tx_psbt);
//bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60);
bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60);
private:
fc::http::reply send_post_request(std::string body, bool show_log);
@ -105,6 +124,7 @@ private:
std::string ip;
uint32_t zmq_port;
uint32_t rpc_port;
uint32_t bitcoin_major_version;
std::string rpc_user;
std::string rpc_password;
std::string wallet;

View file

@ -25,6 +25,8 @@ public:
void send_sidechain_transactions();
void settle_sidechain_transactions();
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
private:
peerplays_sidechain_plugin &plugin;
graphene::chain::database &database;

View file

@ -10,6 +10,7 @@
#include <graphene/chain/sidechain_address_object.hpp>
#include <graphene/chain/son_wallet_object.hpp>
#include <graphene/chain/son_wallet_withdraw_object.hpp>
#include <graphene/peerplays_sidechain/sidechain_api.hpp>
#include <graphene/peerplays_sidechain/sidechain_net_manager.hpp>
#include <graphene/utilities/key_conversion.hpp>
@ -44,6 +45,7 @@ public:
fc::ecc::private_key get_private_key(chain::public_key_type public_key);
void log_son_proposal_retry(int op_type, object_id_type object_id);
bool can_son_participate(int op_type, object_id_type object_id);
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
void schedule_heartbeat_loop();
void heartbeat_loop();
@ -176,12 +178,6 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt
}
config_ready_son = config_ready_son && !sons.empty();
#ifndef ENABLE_MULTIPLE_SONS
if (sons.size() > 1) {
FC_THROW("Invalid configuration, multiple SON IDs provided");
}
#endif
if (options.count("peerplays-private-key")) {
const std::vector<std::string> key_id_to_wif_pair_strings = options["peerplays-private-key"].as<std::vector<std::string>>();
for (const std::string &key_id_to_wif_pair_string : key_id_to_wif_pair_strings) {
@ -534,6 +530,10 @@ bool peerplays_sidechain_plugin_impl::can_son_participate(int op_type, object_id
return (itr == son_retry_count.end() || itr->second < retries_threshold);
}
std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin_impl::get_son_listener_log() {
return net_manager->get_son_listener_log();
}
void peerplays_sidechain_plugin_impl::approve_proposals() {
auto check_approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_object &proposal) {
@ -794,4 +794,8 @@ bool peerplays_sidechain_plugin::can_son_participate(int op_type, object_id_type
return my->can_son_participate(op_type, object_id);
}
std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin::get_son_listener_log() {
return my->get_son_listener_log();
}
}} // namespace graphene::peerplays_sidechain

View file

@ -0,0 +1,48 @@
#include <graphene/peerplays_sidechain/sidechain_api.hpp>
namespace graphene { namespace peerplays_sidechain {
namespace detail {
class sidechain_api_impl {
public:
sidechain_api_impl(app::application &app);
virtual ~sidechain_api_impl();
std::shared_ptr<graphene::peerplays_sidechain::peerplays_sidechain_plugin> get_plugin();
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
private:
app::application &app;
};
sidechain_api_impl::sidechain_api_impl(app::application &_app) :
app(_app) {
}
sidechain_api_impl::~sidechain_api_impl() {
}
std::shared_ptr<graphene::peerplays_sidechain::peerplays_sidechain_plugin> sidechain_api_impl::get_plugin() {
return app.get_plugin<graphene::peerplays_sidechain::peerplays_sidechain_plugin>("peerplays_sidechain");
}
std::map<sidechain_type, std::vector<std::string>> sidechain_api_impl::get_son_listener_log() {
return get_plugin()->get_son_listener_log();
}
} // namespace detail
sidechain_api::sidechain_api(graphene::app::application &_app) :
my(std::make_shared<detail::sidechain_api_impl>(_app)) {
}
sidechain_api::~sidechain_api() {
}
std::map<sidechain_type, std::vector<std::string>> sidechain_api::get_son_listener_log() {
return my->get_son_listener_log();
}
}} // namespace graphene::peerplays_sidechain

View file

@ -589,7 +589,7 @@ void sidechain_net_handler::settle_sidechain_transactions() {
if (settle_amount.amount != 0) {
if (sto.object_id.is<son_wallet_deposit_id_type>()) {
asset_issue_operation ai_op;
ai_op.fee = asset(2001000);
ai_op.fee = database.current_fee_schedule().calculate_fee(ai_op);
ai_op.issuer = gpo.parameters.son_account();
ai_op.asset_to_issue = settle_amount;
ai_op.issue_to_account = database.get<son_wallet_deposit_object>(sto.object_id).peerplays_from;
@ -598,7 +598,7 @@ void sidechain_net_handler::settle_sidechain_transactions() {
if (sto.object_id.is<son_wallet_withdraw_id_type>()) {
asset_reserve_operation ar_op;
ar_op.fee = asset(2001000);
ar_op.fee = database.current_fee_schedule().calculate_fee(ar_op);
ar_op.payer = gpo.parameters.son_account();
ar_op.amount_to_reserve = settle_amount;
proposal_op.proposed_ops.emplace_back(ar_op);
@ -618,6 +618,17 @@ void sidechain_net_handler::settle_sidechain_transactions() {
});
}
void sidechain_net_handler::add_to_son_listener_log(std::string trx_id) {
son_listener_log.insert(son_listener_log.begin(), trx_id);
if (son_listener_log.size() > 33) {
son_listener_log.erase(son_listener_log.end());
}
}
std::vector<std::string> sidechain_net_handler::get_son_listener_log() {
return son_listener_log;
}
void sidechain_net_handler::on_applied_block(const signed_block &b) {
const chain::global_property_object &gpo = plugin.database().get_global_properties();

View file

@ -41,6 +41,7 @@ bitcoin_rpc_client::bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::stri
std::string bitcoin_rpc_client::addmultisigaddress(const uint32_t nrequired, const std::vector<std::string> public_keys) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"addmultisigaddress\", "
"\"method\": \"addmultisigaddress\", \"params\": [");
try {
std::string params = std::to_string(nrequired) + ", [";
std::string pubkeys = "";
for (std::string pubkey : public_keys) {
@ -71,11 +72,16 @@ std::string bitcoin_rpc_client::addmultisigaddress(const uint32_t nrequired, con
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::combinepsbt(const vector<std::string> &psbts) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"combinepsbt\", \"method\": "
"\"combinepsbt\", \"params\": [[");
try {
std::string params = "";
for (std::string psbt : psbts) {
if (!params.empty()) {
@ -105,11 +111,16 @@ std::string bitcoin_rpc_client::combinepsbt(const vector<std::string> &psbts) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::createmultisig(const uint32_t nrequired, const std::vector<std::string> public_keys) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"createmultisig\", "
"\"method\": \"createmultisig\", \"params\": [");
try {
std::string params = std::to_string(nrequired) + ", [";
std::string pubkeys = "";
for (std::string pubkey : public_keys) {
@ -140,11 +151,16 @@ std::string bitcoin_rpc_client::createmultisig(const uint32_t nrequired, const s
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::createpsbt(const std::vector<btc_txout> &ins, const fc::flat_map<std::string, double> outs) {
std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createpsbt\", "
"\"method\": \"createpsbt\", \"params\": [");
try {
body += "[";
bool first = true;
for (const auto &entry : ins) {
@ -184,11 +200,16 @@ std::string bitcoin_rpc_client::createpsbt(const std::vector<btc_txout> &ins, co
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::createrawtransaction(const std::vector<btc_txout> &ins, const fc::flat_map<std::string, double> outs) {
std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createrawtransaction\", "
"\"method\": \"createrawtransaction\", \"params\": [");
try {
body += "[";
bool first = true;
for (const auto &entry : ins) {
@ -228,13 +249,17 @@ std::string bitcoin_rpc_client::createrawtransaction(const std::vector<btc_txout
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::createwallet(const std::string &wallet_name) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"createwallet\", \"method\": "
"\"createwallet\", \"params\": [\"" +
wallet_name + "\"] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
@ -256,13 +281,17 @@ std::string bitcoin_rpc_client::createwallet(const std::string &wallet_name) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::decodepsbt(std::string const &tx_psbt) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"decodepsbt\", \"method\": "
"\"decodepsbt\", \"params\": [\"" +
tx_psbt + "\"] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
@ -284,13 +313,17 @@ std::string bitcoin_rpc_client::decodepsbt(std::string const &tx_psbt) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::decoderawtransaction(std::string const &tx_hex) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"decoderawtransaction\", \"method\": "
"\"decoderawtransaction\", \"params\": [\"" +
tx_hex + "\"] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
@ -312,13 +345,17 @@ std::string bitcoin_rpc_client::decoderawtransaction(std::string const &tx_hex)
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::encryptwallet(const std::string &passphrase) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"encryptwallet\", \"method\": "
"\"encryptwallet\", \"params\": [\"" +
passphrase + "\"] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
@ -340,13 +377,17 @@ std::string bitcoin_rpc_client::encryptwallet(const std::string &passphrase) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) {
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"estimatesmartfee\", "
"\"method\": \"estimatesmartfee\", \"params\": [" +
std::to_string(conf_target) + std::string("] }"));
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
@ -377,13 +418,17 @@ uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return 20000;
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return 20000;
}
}
std::string bitcoin_rpc_client::finalizepsbt(std::string const &tx_psbt) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"finalizepsbt\", \"method\": "
"\"finalizepsbt\", \"params\": [\"" +
tx_psbt + "\"] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
@ -403,13 +448,17 @@ std::string bitcoin_rpc_client::finalizepsbt(std::string const &tx_psbt) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::getaddressinfo(const std::string &address) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getaddressinfo\", \"method\": "
"\"getaddressinfo\", \"params\": [\"" +
address + "\"] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
@ -431,13 +480,17 @@ std::string bitcoin_rpc_client::getaddressinfo(const std::string &address) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t verbosity) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getblock\", \"method\": "
"\"getblock\", \"params\": [\"" +
block_hash + "\", " + std::to_string(verbosity) + "] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
@ -459,12 +512,46 @@ std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::getnetworkinfo() {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getnetworkinfo\", \"method\": "
"\"getnetworkinfo\", \"params\": [] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
return "";
}
std::stringstream ss(std::string(reply.body.begin(), reply.body.end()));
boost::property_tree::ptree json;
boost::property_tree::read_json(ss, json);
if (reply.status == 200) {
return ss.str();
}
if (json.count("error") && !json.get_child("error").empty()) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::getrawtransaction(const std::string &txid, const bool verbose) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getrawtransaction\", \"method\": "
"\"getrawtransaction\", \"params\": [");
try {
std::string params = "\"" + txid + "\", " + (verbose ? "true" : "false");
body = body + params + "] }";
@ -487,12 +574,16 @@ std::string bitcoin_rpc_client::getrawtransaction(const std::string &txid, const
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::gettransaction(const std::string &txid, const bool include_watch_only) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"gettransaction\", \"method\": "
"\"gettransaction\", \"params\": [");
try {
std::string params = "\"" + txid + "\", " + (include_watch_only ? "true" : "false");
body = body + params + "] }";
@ -515,12 +606,16 @@ std::string bitcoin_rpc_client::gettransaction(const std::string &txid, const bo
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::getblockchaininfo() {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getblockchaininfo\", \"method\": "
"\"getblockchaininfo\", \"params\": [] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
@ -542,12 +637,16 @@ std::string bitcoin_rpc_client::getblockchaininfo() {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
void bitcoin_rpc_client::importaddress(const std::string &address_or_script, const std::string &label, const bool rescan, const bool p2sh) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"importaddress\", "
"\"method\": \"importaddress\", \"params\": [");
try {
std::string params = "\"" + address_or_script + "\", " +
"\"" + label + "\", " +
(rescan ? "true" : "false") + ", " +
@ -570,6 +669,63 @@ void bitcoin_rpc_client::importaddress(const std::string &address_or_script, con
} else if (json.count("error") && !json.get_child("error").empty()) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
}
}
void bitcoin_rpc_client::importmulti(const std::vector<multi_params> &address_or_script_array, const bool rescan) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"importmulti\", "
"\"method\": \"importmulti\", \"params\": [");
try {
std::string argument_1 = "[";
for (const auto &param : address_or_script_array) {
argument_1 += "{\"scriptPubKey\": ";
if (param.type == multi_type::address) {
argument_1 += "{\"address\": \"" + param.address_or_script + "\"}, \"label\": \"" + param.label + "\"";
} else if (param.type == multi_type::script) {
argument_1 += "\"" + param.address_or_script + "\", \"internal\": true";
} else {
FC_THROW("Invalid multi_type.");
}
argument_1 += ", \"timestamp\": " + std::to_string(fc::time_point_sec::from_iso_string("2022-01-01T00:00:00").sec_since_epoch()) + "}";
//! Note
/* Creation time of the key expressed in UNIX epoch time,
or the string "now" to substitute the current synced blockchain time. The timestamp of the oldest
key will determine how far back blockchain rescans need to begin for missing wallet transactions.
"now" can be specified to bypass scanning, for keys which are known to never have been used, and
0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key
creation time of all keys being imported by the importmulti call will be scanned.*/
if (&param != &address_or_script_array.back()) {
argument_1 += ", ";
}
}
argument_1 += "]";
std::string argument_2 = std::string{"{\"rescan\": "} + (rescan ? "true" : "false") + "}";
body += argument_1 + ", " + argument_2 + "]}";
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
return;
}
std::stringstream ss(std::string(reply.body.begin(), reply.body.end()));
boost::property_tree::ptree json;
boost::property_tree::read_json(ss, json);
if (reply.status == 200) {
return;
} else if (json.count("error") && !json.get_child("error").empty()) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
}
}
std::vector<btc_txout> bitcoin_rpc_client::listunspent(const uint32_t minconf, const uint32_t maxconf) {
@ -577,9 +733,9 @@ std::vector<btc_txout> bitcoin_rpc_client::listunspent(const uint32_t minconf, c
"\"listunspent\", \"params\": [" +
std::to_string(minconf) + "," + std::to_string(maxconf) + "] }");
const auto reply = send_post_request(body, debug_rpc_calls);
std::vector<btc_txout> result;
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
@ -606,6 +762,10 @@ std::vector<btc_txout> bitcoin_rpc_client::listunspent(const uint32_t minconf, c
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return result;
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return result;
}
}
std::vector<btc_txout> bitcoin_rpc_client::listunspent_by_address_and_amount(const std::string &address, double minimum_amount, const uint32_t minconf, const uint32_t maxconf) {
@ -618,9 +778,11 @@ std::vector<btc_txout> bitcoin_rpc_client::listunspent_by_address_and_amount(con
body += std::to_string(minimum_amount);
body += std::string("} ] }");
std::vector<btc_txout> result;
try {
const auto reply = send_post_request(body, debug_rpc_calls);
std::vector<btc_txout> result;
if (reply.body.empty()) {
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
return result;
@ -646,13 +808,17 @@ std::vector<btc_txout> bitcoin_rpc_client::listunspent_by_address_and_amount(con
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return result;
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return result;
}
}
std::string bitcoin_rpc_client::loadwallet(const std::string &filename) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"loadwallet\", \"method\": "
"\"loadwallet\", \"params\": [\"" +
filename + "\"] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
@ -674,13 +840,17 @@ std::string bitcoin_rpc_client::loadwallet(const std::string &filename) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) {
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"sendrawtransaction\", "
"\"method\": \"sendrawtransaction\", \"params\": [") +
std::string("\"") + tx_hex + std::string("\"") + std::string("] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
@ -700,6 +870,10 @@ std::string bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string &tx_hash) {
@ -708,6 +882,7 @@ std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string &
std::string params = "\"" + tx_hash + "\"";
body = body + params + std::string("]}");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
@ -727,13 +902,17 @@ std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string &
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::unloadwallet(const std::string &filename) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"unloadwallet\", \"method\": "
"\"unloadwallet\", \"params\": [\"" +
filename + "\"] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
@ -755,40 +934,48 @@ std::string bitcoin_rpc_client::unloadwallet(const std::string &filename) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
//std::string bitcoin_rpc_client::walletlock() {
// std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletlock\", \"method\": "
// "\"walletlock\", \"params\": [] }");
//
// const auto reply = send_post_request(body, debug_rpc_calls);
//
// if (reply.body.empty()) {
// wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
// return "";
// }
//
// std::stringstream ss(std::string(reply.body.begin(), reply.body.end()));
// boost::property_tree::ptree json;
// boost::property_tree::read_json(ss, json);
//
// if (reply.status == 200) {
// std::stringstream ss;
// boost::property_tree::json_parser::write_json(ss, json.get_child("result"));
// return ss.str();
// }
//
// if (json.count("error") && !json.get_child("error").empty()) {
// wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
// }
// return "";
//}
std::string bitcoin_rpc_client::walletlock() {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletlock\", \"method\": "
"\"walletlock\", \"params\": [] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
return "";
}
std::stringstream ss(std::string(reply.body.begin(), reply.body.end()));
boost::property_tree::ptree json;
boost::property_tree::read_json(ss, json);
if (reply.status == 200) {
std::stringstream ss;
boost::property_tree::json_parser::write_json(ss, json.get_child("result"));
return ss.str();
}
if (json.count("error") && !json.get_child("error").empty()) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
std::string bitcoin_rpc_client::walletprocesspsbt(std::string const &tx_psbt) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletprocesspsbt\", \"method\": "
"\"walletprocesspsbt\", \"params\": [\"" +
tx_psbt + "\"] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
@ -808,33 +995,41 @@ std::string bitcoin_rpc_client::walletprocesspsbt(std::string const &tx_psbt) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return "";
}
}
//bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_t timeout) {
// std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletpassphrase\", \"method\": "
// "\"walletpassphrase\", \"params\": [\"" +
// passphrase + "\", " + std::to_string(timeout) + "] }");
//
// const auto reply = send_post_request(body, debug_rpc_calls);
//
// if (reply.body.empty()) {
// wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
// return false;
// }
//
// std::stringstream ss(std::string(reply.body.begin(), reply.body.end()));
// boost::property_tree::ptree json;
// boost::property_tree::read_json(ss, json);
//
// if (reply.status == 200) {
// return true;
// }
//
// if (json.count("error") && !json.get_child("error").empty()) {
// wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
// }
// return false;
//}
bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_t timeout) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletpassphrase\", \"method\": "
"\"walletpassphrase\", \"params\": [\"" +
passphrase + "\", " + std::to_string(timeout) + "] }");
try {
const auto reply = send_post_request(body, debug_rpc_calls);
if (reply.body.empty()) {
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
return false;
}
std::stringstream ss(std::string(reply.body.begin(), reply.body.end()));
boost::property_tree::ptree json;
boost::property_tree::read_json(ss, json);
if (reply.status == 200) {
return true;
}
if (json.count("error") && !json.get_child("error").empty()) {
wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return false;
} catch (const boost::exception &ex) {
wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex)));
return false;
}
}
fc::http::reply bitcoin_rpc_client::send_post_request(std::string body, bool show_log) {
fc::http::connection conn;
@ -852,6 +1047,7 @@ fc::http::reply bitcoin_rpc_client::send_post_request(std::string body, bool sho
ilog("### Request URL: ${url}", ("url", url));
ilog("### Request: ${body}", ("body", body));
std::stringstream ss(std::string(reply.body.begin(), reply.body.end()));
ilog("### Response status: ${status}", ("status", reply.status));
ilog("### Response: ${ss}", ("ss", ss.str()));
}
@ -871,16 +1067,11 @@ zmq_listener::zmq_listener(std::string _ip, uint32_t _zmq) :
std::vector<zmq::message_t> zmq_listener::receive_multipart() {
std::vector<zmq::message_t> msgs;
int32_t more;
size_t more_size = sizeof(more);
while (true) {
zmq::message_t msg;
socket.recv(&msg, 0);
socket.getsockopt(ZMQ_RCVMORE, &more, &more_size);
if (!more)
break;
msgs.push_back(std::move(msg));
auto res = zmq::recv_multipart(socket, std::back_inserter(msgs));
FC_ASSERT(res);
if (3 != *res) {
elog("zmq::recv_multipart returned: ${res}", ("res", *res));
throw zmq::error_t();
}
return msgs;
@ -902,6 +1093,7 @@ void zmq_listener::handle_zmq() {
const auto block_hash = boost::algorithm::hex(std::string(static_cast<char *>(msg[1].data()), msg[1].size()));
event_received(block_hash);
} catch (zmq::error_t &e) {
elog("handle_zmq recv_multipart exception ${str}", ("str", e.what()));
}
}
}
@ -970,6 +1162,14 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain
}
}
std::string network_info_str = bitcoin_client->getnetworkinfo();
std::stringstream network_info_ss(network_info_str);
boost::property_tree::ptree network_info_json;
boost::property_tree::read_json(network_info_ss, network_info_json);
bitcoin_major_version = network_info_json.get<uint32_t>("result.version") / 10000;
ilog("Bitcoin major version is: '${version}'", ("version", bitcoin_major_version));
listener = std::unique_ptr<zmq_listener>(new zmq_listener(ip, zmq_port));
listener->event_received.connect([this](const std::string &event_data) {
std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach();
@ -1120,12 +1320,19 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
std::string tx_vout_s = input.second.get<std::string>("n");
tx_vout = std::stoll(tx_vout_s);
if (tx_vout == swdo_vout) {
if (bitcoin_major_version > 21) {
std::string address = input.second.get<std::string>("scriptPubKey.address");
if (address == swdo_address) {
tx_address = address;
}
} else {
for (auto &address : input.second.get_child("scriptPubKey.addresses")) {
if (address.second.data() == swdo_address) {
tx_address = address.second.data();
break;
}
}
}
std::string tx_amount_s = input.second.get<std::string>("value");
tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end());
tx_amount = std::stoll(tx_amount_s);
@ -1535,6 +1742,14 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain
if (tx_confirmations >= gpo.parameters.son_bitcoin_min_tx_confirmations()) {
if (sto.object_id.is<son_wallet_deposit_id_type>()) {
for (auto &input : tx_json.get_child("result.vout")) {
if (bitcoin_major_version > 21) {
std::string address = input.second.get<std::string>("scriptPubKey.address");
if (address == tx_address) {
std::string tx_amount_s = input.second.get<std::string>("value");
tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end());
tx_amount = std::stoll(tx_amount_s);
}
} else {
for (auto &address : input.second.get_child("scriptPubKey.addresses")) {
if (address.second.data() == tx_address) {
std::string tx_amount_s = input.second.get<std::string>("value");
@ -1544,6 +1759,7 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain
}
}
}
}
settle_amount = asset(tx_amount, database.get_global_properties().parameters.btc_asset());
return true;
}
@ -1617,7 +1833,7 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction(con
}
if (fee_rate >= total_amount) {
elog("Failed not enough BTC to transfer from ${fa}", ("fa", prev_pw_address));
elog("Not enough BTC to pay the transfer fee, address ${fa}", ("fa", prev_pw_address));
return "";
}
}
@ -1651,6 +1867,12 @@ std::string sidechain_net_handler_bitcoin::create_deposit_transaction(const son_
uint64_t fee_rate = bitcoin_client->estimatesmartfee();
uint64_t min_fee_rate = 1000;
fee_rate = std::max(fee_rate, min_fee_rate);
if (fee_rate >= deposit_amount) {
elog("Not enough BTC to pay the transfer fee, address ${da}", ("da", swdo.sidechain_from));
return "";
}
deposit_amount -= fee_rate; // Deduct minimum relay fee
double transfer_amount = (double)deposit_amount / 100000000.0;
@ -1701,7 +1923,7 @@ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const s
}
if ((swwo.withdraw_amount.value > total_amount) || (fee_rate >= swwo.withdraw_amount.value)) {
elog("Failed not enough BTC to spend for ${pw}", ("pw", pw_address));
elog("Not enough BTC to pay the transfer fee, address ${pw}", ("pw", pw_address));
return "";
}
}
@ -1808,6 +2030,8 @@ void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data)
if (block.empty())
return;
add_to_son_listener_log("BLOCK : " + event_data);
auto vins = extract_info_from_block(block);
scoped_lock interlock(event_handler_mutex);
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_address_and_expires>();
@ -1837,6 +2061,9 @@ void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data)
sed.peerplays_to = database.get_global_properties().parameters.son_account();
price btc_price = database.get<asset_object>(database.get_global_properties().parameters.btc_asset()).options.core_exchange_rate;
sed.peerplays_asset = asset(sed.sidechain_amount * btc_price.base.amount / btc_price.quote.amount);
add_to_son_listener_log("TRX : " + sed.sidechain_transaction_id);
sidechain_event_data_received(sed);
}
}
@ -1879,11 +2106,16 @@ std::vector<info_for_vin> sidechain_net_handler_bitcoin::extract_info_from_block
for (const auto &o : tx.get_child("vout")) {
const auto script = o.second.get_child("scriptPubKey");
if (bitcoin_major_version > 21) {
if (!script.count("address"))
continue;
} else {
if (!script.count("addresses"))
continue;
}
for (const auto &addr : script.get_child("addresses")) { // in which cases there can be more addresses?
const auto address_base58 = addr.second.get_value<std::string>();
auto sort_out_vin = [&](std::string address) {
const auto address_base58 = address;
info_for_vin vin;
vin.out.hash_tx = tx.get_child("txid").get_value<std::string>();
string amount = o.second.get_child("value").get_value<std::string>();
@ -1892,6 +2124,14 @@ std::vector<info_for_vin> sidechain_net_handler_bitcoin::extract_info_from_block
vin.out.n_vout = o.second.get_child("n").get_value<uint32_t>();
vin.address = address_base58;
result.push_back(vin);
};
if (bitcoin_major_version > 21) {
std::string address = script.get<std::string>("address");
sort_out_vin(address);
} else {
for (const auto &addr : script.get_child("addresses")) // in which cases there can be more addresses?
sort_out_vin(addr.second.get_value<std::string>());
}
}
}
@ -1912,6 +2152,8 @@ void sidechain_net_handler_bitcoin::on_changed_objects(const vector<object_id_ty
}
void sidechain_net_handler_bitcoin::on_changed_objects_cb(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts) {
std::vector<bitcoin_rpc_client::multi_params> address_or_script_array;
for (auto id : ids) {
if (id.is<son_wallet_object>()) {
const auto &swi = database.get_index_type<son_wallet_index>().indices().get<by_id>();
@ -1923,16 +2165,31 @@ void sidechain_net_handler_bitcoin::on_changed_objects_cb(const vector<object_id
if (pw_pt.count("address")) {
std::string pw_address = pw_pt.get<std::string>("address");
bitcoin_client->importaddress(pw_address);
address_or_script_array.emplace_back(bitcoin_rpc_client::multi_params{bitcoin_rpc_client::multi_type::address, pw_address});
}
if (pw_pt.count("redeemScript")) {
std::string pw_redeem_script = pw_pt.get<std::string>("redeemScript");
bitcoin_client->importaddress(pw_redeem_script, "", true, true);
address_or_script_array.emplace_back(bitcoin_rpc_client::multi_params{bitcoin_rpc_client::multi_type::script, pw_redeem_script});
}
}
}
}
//! importmulti all addreses in one bulk
if (!address_or_script_array.empty()) {
//! Unlock wallet
if (!wallet_password.empty()) {
if (!bitcoin_client->walletpassphrase(wallet_password))
return;
}
//! importmulti
bitcoin_client->importmulti(address_or_script_array);
//! Lock wallet
bitcoin_client->walletlock();
}
}
// =============================================================================

View file

@ -627,7 +627,7 @@ bool sidechain_net_handler_hive::process_deposit(const son_wallet_deposit_object
proposal_op.proposed_ops.emplace_back(swdp_op);
asset_issue_operation ai_op;
ai_op.fee = asset(2001000);
ai_op.fee = database.current_fee_schedule().calculate_fee(ai_op);
ai_op.issuer = gpo.parameters.son_account();
ai_op.asset_to_issue = asset_to_issue;
ai_op.issue_to_account = swdo.peerplays_from;
@ -846,6 +846,7 @@ void sidechain_net_handler_hive::hive_listener_loop() {
void sidechain_net_handler_hive::handle_event(const std::string &event_data) {
std::string block = node_rpc_client->block_api_get_block(std::atoll(event_data.c_str()));
if (block != "") {
add_to_son_listener_log("BLOCK : " + event_data);
std::stringstream ss(block);
boost::property_tree::ptree block_json;
boost::property_tree::read_json(ss, block_json);
@ -932,6 +933,9 @@ void sidechain_net_handler_hive::handle_event(const std::string &event_data) {
sed.peerplays_from = accn;
sed.peerplays_to = database.get_global_properties().parameters.son_account();
sed.peerplays_asset = asset(sed.sidechain_amount * sidechain_currency_price.base.amount / sidechain_currency_price.quote.amount);
add_to_son_listener_log("TRX : " + sed.sidechain_transaction_id);
sidechain_event_data_received(sed);
}
}

View file

@ -98,6 +98,14 @@ void sidechain_net_manager::settle_sidechain_transactions() {
}
}
std::map<sidechain_type, std::vector<std::string>> sidechain_net_manager::get_son_listener_log() {
std::map<sidechain_type, std::vector<std::string>> result;
for (size_t i = 0; i < net_handlers.size(); i++) {
result[net_handlers.at(i)->get_sidechain()] = net_handlers.at(i)->get_son_listener_log();
}
return result;
}
void sidechain_net_manager::on_applied_block(const signed_block &b) {
}

View file

@ -24,7 +24,6 @@
#include <graphene/utilities/elasticsearch.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string.hpp>
#include <fc/log/logger.hpp>
#include <fc/io/json.hpp>
@ -47,8 +46,36 @@ bool checkES(ES& es)
if(doCurl(curl_request).empty())
return false;
return true;
}
const std::string getESVersion(ES& es)
{
graphene::utilities::CurlRequest curl_request;
curl_request.handler = es.curl;
curl_request.url = es.elasticsearch_url;
curl_request.auth = es.auth;
curl_request.type = "GET";
fc::variant response = fc::json::from_string(doCurl(curl_request));
return response["version"]["number"].as_string();
}
void checkESVersion7OrAbove(ES& es, bool& result) noexcept
{
static const int64_t version_7 = 7;
try {
const auto es_version = graphene::utilities::getESVersion(es);
auto dot_pos = es_version.find('.');
result = ( std::stoi(es_version.substr(0,dot_pos)) >= version_7 );
}
catch( ... )
{
wlog( "Unable to get ES version, assuming it is 7 or above" );
result = true;
}
}
const std::string simpleQuery(ES& es)
{
graphene::utilities::CurlRequest curl_request;
@ -118,13 +145,13 @@ bool handleBulkResponse(long http_code, const std::string& CurlReadBuffer)
return true;
}
const std::vector<std::string> createBulk(const fc::mutable_variant_object& bulk_header, const std::string& data)
const std::vector<std::string> createBulk(const fc::mutable_variant_object& bulk_header, const std::string&& data)
{
std::vector<std::string> bulk;
fc::mutable_variant_object final_bulk_header;
final_bulk_header["index"] = bulk_header;
bulk.push_back(fc::json::to_string(final_bulk_header));
bulk.push_back(data);
bulk.emplace_back(std::move(data));
return bulk;
}
@ -154,15 +181,6 @@ const std::string getEndPoint(ES& es)
return doCurl(curl_request);
}
const std::string generateIndexName(const fc::time_point_sec& block_date, const std::string& _elasticsearch_index_prefix)
{
auto block_date_string = block_date.to_iso_string();
std::vector<std::string> parts;
boost::split(parts, block_date_string, boost::is_any_of("-"));
std::string index_name = _elasticsearch_index_prefix + parts[0] + "-" + parts[1];
return index_name;
}
const std::string doCurl(CurlRequest& curl)
{
std::string CurlReadBuffer;

View file

@ -54,13 +54,14 @@ namespace graphene { namespace utilities {
};
bool SendBulk(ES& es);
const std::vector<std::string> createBulk(const fc::mutable_variant_object& bulk_header, const std::string& data);
const std::vector<std::string> createBulk(const fc::mutable_variant_object& bulk_header, const std::string&& data);
bool checkES(ES& es);
const std::string getESVersion(ES& es);
void checkESVersion7OrAbove(ES& es, bool& result) noexcept;
const std::string simpleQuery(ES& es);
bool deleteAll(ES& es);
bool handleBulkResponse(long http_code, const std::string& CurlReadBuffer);
const std::string getEndPoint(ES& es);
const std::string generateIndexName(const fc::time_point_sec& block_date, const std::string& _elasticsearch_index_prefix);
const std::string doCurl(CurlRequest& curl);
const std::string joinBulkLines(const std::vector<std::string>& bulk);
long getResponseCode(CURL *handler);

View file

@ -1,4 +1,4 @@
# Doxyfile 1.8.9.1
# Doxyfile 1.8.17
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@ -17,11 +17,11 @@
# Project related configuration options
#---------------------------------------------------------------------------
# This tag specifies the encoding used for all characters in the config file
# that follow. The default is UTF-8 which is also the encoding used for all text
# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
# for the list of possible encodings.
# This tag specifies the encoding used for all characters in the configuration
# file that follow. The default is UTF-8 which is also the encoding used for all
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
# iconv built into libc) for the transcoding. See
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
# The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8
@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all generated output in the proper direction.
# Possible values are: None, LTR, RTL and Context.
# The default value is: None.
OUTPUT_TEXT_DIRECTION = None
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
# descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this.
@ -179,6 +187,16 @@ SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
# such as
# /***************
# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
# Javadoc-style will behave just like regular comments and it will not be
# interpreted by doxygen.
# The default value is: NO.
JAVADOC_BANNER = NO
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
# line (until the first dot) of a Qt-style comment as the brief description. If
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
@ -226,7 +244,12 @@ TAB_SIZE = 4
# will allow you to put the command \sideeffect (or @sideeffect) in the
# documentation, which will result in a user-defined paragraph with heading
# "Side Effects:". You can put \n's in the value part of an alias to insert
# newlines.
# newlines (in the resulting output). You can put ^^ in the value part of an
# alias to insert a newline as if a physical newline was in the original file.
# When you need a literal { or } or , in the value part of an alias you have to
# escape them by means of a backslash (\), this can lead to conflicts with the
# commands \{ and \} for these it is advised to use the version @{ and @} or use
# a double escape (\\{ and \\})
ALIASES =
@ -264,17 +287,26 @@ OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
# sources only. Doxygen will then generate output that is more tailored for that
# language. For instance, namespaces will be presented as modules, types will be
# separated into more groups, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_SLICE = NO
# Doxygen selects the parser to use depending on the extension of the files it
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
# Fortran. In the later case the parser tries to guess whether the code is fixed
# or free formatted code, this is the default for Fortran type files), VHDL. For
# instance to make doxygen treat .inc files as Fortran files (default is PHP),
# and .f files as C (default is Fortran), use: inc=Fortran f=C.
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
# tries to guess whether the code is fixed or free formatted code, this is the
# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
# .inc files as Fortran files (default is PHP), and .f files as C (default is
# Fortran), use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
@ -285,7 +317,7 @@ EXTENSION_MAPPING =
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable
# documentation. See http://daringfireball.net/projects/markdown/ for details.
# documentation. See https://daringfireball.net/projects/markdown/ for details.
# The output of markdown processing is further processed by doxygen, so you can
# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
# case of backward compatibilities issues.
@ -293,6 +325,15 @@ EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES
# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
# Minimum value: 0, maximum value: 99, default value: 5.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 5
# When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or
@ -318,7 +359,7 @@ BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
# will parse them like normal C++ but will assume all classes use public instead
# of private inheritance when no explicit protection keyword is present.
# The default value is: NO.
@ -343,6 +384,13 @@ IDL_PROPERTY_SUPPORT = YES
DISTRIBUTE_GROUP_DOC = NO
# If one adds a struct or class to a group and this option is enabled, then also
# any nested class or struct is added to the same group. By default this option
# is disabled and one has to add nested compounds explicitly via \ingroup.
# The default value is: NO.
GROUP_NESTED_COMPOUNDS = NO
# Set the SUBGROUPING tag to YES to allow class member groups of the same type
# (for instance a group of public functions) to be put as a subgroup of that
# type (e.g. under the Public Functions section). Set it to NO to prevent
@ -417,6 +465,12 @@ EXTRACT_ALL = YES
EXTRACT_PRIVATE = NO
# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
# methods of a class will be included in the documentation.
# The default value is: NO.
EXTRACT_PRIV_VIRTUAL = NO
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation.
# The default value is: NO.
@ -471,8 +525,8 @@ HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
# (class|struct|union) declarations. If set to NO, these declarations will be
# included in the documentation.
# declarations. If set to NO, these declarations will be included in the
# documentation.
# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = NO
@ -495,7 +549,7 @@ INTERNAL_DOCS = NO
# names in lower-case letters. If set to YES, upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# and Mac users are advised to set this option to NO.
# (including Cygwin) ands Mac users are advised to set this option to NO.
# The default value is: system dependent.
CASE_SENSE_NAMES = NO
@ -682,7 +736,7 @@ LAYOUT_FILE =
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib
# extension is automatically appended if omitted. This requires the bibtex tool
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. See also \cite for info how to create references.
@ -727,11 +781,18 @@ WARN_IF_DOC_ERROR = YES
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
# are documented, but have no documentation for their parameters or return
# value. If set to NO, doxygen will only warn about wrong or incomplete
# parameter documentation, but not about the absence of documentation.
# parameter documentation, but not about the absence of documentation. If
# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
# The default value is: NO.
WARN_NO_PARAMDOC = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered.
# The default value is: NO.
WARN_AS_ERROR = NO
# The WARN_FORMAT tag determines the format of the warning messages that doxygen
# can produce. The string should contain the $file, $line, and $text tags, which
# will be replaced by the file and line number from which the warning originated
@ -755,7 +816,7 @@ WARN_LOGFILE =
# The INPUT tag is used to specify the files and/or directories that contain
# documented source files. You may enter file names like myfile.cpp or
# directories like /usr/src/myproject. Separate the files or directories with
# spaces.
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = ${CMAKE_CURRENT_SOURCE_DIR}/include/graphene/wallet/wallet.hpp
@ -763,7 +824,7 @@ INPUT = ${CMAKE_CURRENT_SOURCE_DIR}/include/graphene/wallet/wal
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see: http://www.gnu.org/software/libiconv) for the list of
# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
# possible encodings.
# The default value is: UTF-8.
@ -771,12 +832,19 @@ INPUT_ENCODING = UTF-8
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories. If left blank the
# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
# *.qsf, *.as and *.js.
# *.h) to filter out the source-files in the directories.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# read by doxygen.
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS =
@ -862,6 +930,10 @@ IMAGE_PATH =
# Note that the filter must not add or remove lines; it is applied before the
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
INPUT_FILTER =
@ -871,6 +943,10 @@ INPUT_FILTER =
# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
# patterns match the file name, INPUT_FILTER is applied.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
FILTER_PATTERNS =
@ -923,7 +999,7 @@ INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
# function all documented functions referencing it will be listed.
# entity all documented functions referencing it will be listed.
# The default value is: NO.
REFERENCED_BY_RELATION = NO
@ -955,12 +1031,12 @@ SOURCE_TOOLTIPS = YES
# If the USE_HTAGS tag is set to YES then the references to source code will
# point to the HTML generated by the htags(1) tool instead of doxygen built-in
# source browser. The htags tool is part of GNU's global source tagging system
# (see http://www.gnu.org/software/global/global.html). You will need version
# (see https://www.gnu.org/software/global/global.html). You will need version
# 4.8.6 or higher.
#
# To use it do the following:
# - Install the latest version of global
# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
# - Make sure the INPUT points to the root of the source tree
# - Run doxygen as normal
#
@ -982,6 +1058,35 @@ USE_HTAGS = NO
VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
# cost of reduced performance. This can be particularly helpful with template
# rich C++ code for which doxygen's built-in parser lacks the necessary type
# information.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
# If clang assisted parsing is enabled you can provide the clang parser with the
# path to the compilation database (see:
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files
# were built. This is equivalent to specifying the "-p" option to a clang tool,
# such as clang-check. These options will then be passed to the parser.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
CLANG_DATABASE_PATH =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
@ -1100,7 +1205,7 @@ HTML_EXTRA_FILES =
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a colorwheel, see
# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again.
# Minimum value: 0, maximum value: 359, default value: 220.
@ -1129,12 +1234,24 @@ HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: YES.
# to YES can help to show when doxygen was last run and thus if the
# documentation is up to date.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = YES
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
# are dynamically created via JavaScript. If disabled, the navigation index will
# consists of multiple levels of tabs that are statically embedded in every HTML
# page. Disable this option to support browsers that do not have JavaScript,
# like the Qt help browser.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_MENUS = YES
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
# page has loaded.
@ -1158,13 +1275,13 @@ HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
# environment (see: http://developer.apple.com/tools/xcode/), introduced with
# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
# environment (see: https://developer.apple.com/xcode/), introduced with OSX
# 10.5 (Leopard). To create a documentation set, doxygen will generate a
# Makefile in the HTML output directory. Running make will produce the docset in
# that directory and running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
# for more information.
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
# genXcode/_index.html for more information.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
@ -1203,7 +1320,7 @@ DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
# Windows.
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
@ -1279,7 +1396,7 @@ QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
@ -1287,7 +1404,7 @@ QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
# folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
@ -1296,7 +1413,7 @@ QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
@ -1304,7 +1421,7 @@ QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
@ -1312,7 +1429,7 @@ QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
@ -1405,7 +1522,7 @@ EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANPARENT tag to determine whether or not the images
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers.
#
@ -1416,8 +1533,14 @@ FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
# to create new LaTeX commands to be used in formulas as building blocks. See
# the section "Including formulas" for details.
FORMULA_MACROFILE =
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# http://www.mathjax.org) which uses client side Javascript for the rendering
# https://www.mathjax.org) which uses client side JavaScript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
@ -1444,8 +1567,8 @@ MATHJAX_FORMAT = HTML-CSS
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from http://www.mathjax.org before deployment.
# The default value is: http://cdn.mathjax.org/mathjax/latest.
# MathJax from https://www.mathjax.org before deployment.
# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
@ -1487,7 +1610,7 @@ MATHJAX_CODEFILE =
SEARCHENGINE = YES
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
# implemented using a web server instead of a web client using Javascript. There
# implemented using a web server instead of a web client using JavaScript. There
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
@ -1506,7 +1629,7 @@ SERVER_BASED_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see: http://xapian.org/).
# Xapian (see: https://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
@ -1519,7 +1642,7 @@ EXTERNAL_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see: http://xapian.org/). See the section "External Indexing and
# Xapian (see: https://xapian.org/). See the section "External Indexing and
# Searching" for details.
# This tag requires that the tag SEARCHENGINE is set to YES.
@ -1571,21 +1694,35 @@ LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked.
#
# Note that when enabling USE_PDFLATEX this option is only used for generating
# bitmaps for formulas in the HTML output, but not in the Makefile that is
# written to the output directory.
# The default file is: latex.
# Note that when not enabling USE_PDFLATEX the default is latex when enabling
# USE_PDFLATEX the default is pdflatex and when in the later case latex is
# chosen this is overwritten by pdflatex. For specific output languages the
# default can have been set differently, this depends on the implementation of
# the output language.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
# index for LaTeX.
# Note: This tag is used in the Makefile / make.bat.
# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
# (.tex).
# The default file is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
# generate index for LaTeX. In case there is no backslash (\) as first character
# it will be automatically added in the LaTeX code.
# Note: This tag is used in the generated output file (.tex).
# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
# The default value is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_MAKEINDEX_CMD = makeindex
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
# documents. This may be useful for small projects and may help to save some
# trees in general.
@ -1604,9 +1741,12 @@ COMPACT_LATEX = NO
PAPER_TYPE = a4
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. To get the times font for
# instance you can specify
# EXTRA_PACKAGES=times
# that should be included in the LaTeX output. The package can be specified just
# by its name or with the correct syntax as to be used with the LaTeX
# \usepackage command. To get the times font for instance you can specify :
# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
# To use the option intlimits with the amsmath package you can specify:
# EXTRA_PACKAGES=[intlimits]{amsmath}
# If left blank no extra packages will be included.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@ -1703,12 +1843,28 @@ LATEX_SOURCE_CODE = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# The default value is: plain.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_TIMESTAMP = NO
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
# path from which the emoji images will be read. If a relative path is entered,
# it will be relative to the LATEX_OUTPUT directory. If left blank the
# LATEX_OUTPUT directory will be used.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EMOJI_DIRECTORY =
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
@ -1748,9 +1904,9 @@ COMPACT_RTF = NO
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's config
# file, i.e. a series of assignments. You only have to provide replacements,
# missing definitions are set to their default value.
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# configuration file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
#
# See also section "Doxygen usage" for information on how to generate the
# default style sheet that doxygen normally uses.
@ -1759,8 +1915,8 @@ RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an RTF document. Syntax is
# similar to doxygen's config file. A template extensions file can be generated
# using doxygen -e rtf extensionFile.
# similar to doxygen's configuration file. A template extensions file can be
# generated using doxygen -e rtf extensionFile.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
@ -1846,6 +2002,13 @@ XML_OUTPUT = xml
XML_PROGRAMLISTING = NO
# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
# namespace members in file scope as well, matching the HTML output.
# The default value is: NO.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_NS_MEMB_FILE_SCOPE = NO
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
@ -1878,9 +2041,9 @@ DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# AutoGen Definitions (see http://autogen.sf.net) file that captures the
# structure of the code including all documentation. Note that this feature is
# still experimental and incomplete at the moment.
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
# the structure of the code including all documentation. Note that this feature
# is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
@ -2047,12 +2210,6 @@ EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of 'which perl').
# The default file (with absolute path) is: /usr/bin/perl.
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
@ -2066,15 +2223,6 @@ PERL_PATH = /usr/bin/perl
CLASS_DIAGRAMS = YES
# You can define message sequence charts within doxygen comments using the \msc
# command. Doxygen will then run the mscgen tool (see:
# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
# documentation. The MSCGEN_PATH tag allows you to specify the directory where
# the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path.
MSCGEN_PATH =
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
@ -2093,7 +2241,7 @@ HIDE_UNDOC_RELATIONS = YES
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: NO.
# The default value is: YES.
HAVE_DOT = NO
@ -2207,7 +2355,8 @@ INCLUDED_BY_GRAPH = YES
#
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable call graphs for selected
# functions only using the \callgraph command.
# functions only using the \callgraph command. Disabling a call graph can be
# accomplished by means of the command \hidecallgraph.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
@ -2218,7 +2367,8 @@ CALL_GRAPH = NO
#
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable caller graphs for selected
# functions only using the \callergraph command.
# functions only using the \callergraph command. Disabling a caller graph can be
# accomplished by means of the command \hidecallergraph.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
@ -2241,11 +2391,17 @@ GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot.
# generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see:
# http://www.graphviz.org/)).
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
# Possible values are: png, jpg, gif and svg.
# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:gdiplus:gdiplus.
# The default value is: png.
# This tag requires that the tag HAVE_DOT is set to YES.
@ -2296,6 +2452,11 @@ DIAFILE_DIRS =
PLANTUML_JAR_PATH =
# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
# configuration file for plantuml.
PLANTUML_CFG_FILE =
# When using plantuml, the specified paths are searched for files specified by
# the !include statement in a plantuml block.

View file

@ -1346,6 +1346,21 @@ class wallet_api
*/
map<string, committee_member_id_type> list_committee_members(const string& lowerbound, uint32_t limit);
/** Lists all workers in the blockchain.
* This returns a list of all account names that own worker, and the associated worker id,
* sorted by name. This lists workers whether they are currently voted in or not.
*
* Use the \c lowerbound and limit parameters to page through the list. To retrieve all workers,
* start by setting \c lowerbound to the empty string \c "", and then each iteration, pass
* the last worker name returned as the \c lowerbound for the next \c list_workers() call.
*
* @param lowerbound the name of the first worker to return. If the named worker does not exist,
* the list will start at the worker that comes after \c lowerbound
* @param limit the maximum number of worker to return (max: 1000)
* @returns a list of worker mapping worker names to worker ids
*/
map<string, worker_id_type> list_workers(const string& lowerbound, uint32_t limit);
/** Returns information about the given SON.
* @param owner_account the name or id of the SON account owner, or the id of the SON
* @returns the information about the SON stored in the block chain
@ -1370,6 +1385,12 @@ class wallet_api
*/
committee_member_object get_committee_member(string owner_account);
/** Returns information about the given worker.
* @param owner_account the name or id of the worker account owner, or the id of the worker
* @returns the information about the workers stored in the block chain
*/
vector<worker_object> get_workers(string owner_account);
/** Creates a SON object owned by the given account.
*
@ -1765,6 +1786,55 @@ class wallet_api
uint16_t desired_number_of_sons,
bool broadcast = false);
/** Broadcast signed transaction for manually sidechain deposit
* @param son_name_or_id ID or name of the son account
* @param sidechain Sidechain type (bitcoin, HIVE, etc)
* @param transaction_id ID of transaction
* @param operation_index Index of operation
* @param sidechain_from Sidechain address transaction from
* @param sidechain_to Sidechain address transaction to
* @param sidechain_currency Sidechain currency
* @param sidechain_amount Sidechain amount to deposit
* @param peerplays_from_name_or_id ID or name of the account transaction from
* @param peerplays_to_name_or_id ID or name of the account transaction to
* @returns the signed transaction.
*/
signed_transaction sidechain_deposit_transaction( const string &son_name_or_id,
const sidechain_type& sidechain,
const string &transaction_id,
uint32_t operation_index,
const string &sidechain_from,
const string &sidechain_to,
const string &sidechain_currency,
int64_t sidechain_amount,
const string &peerplays_from_name_or_id,
const string &peerplays_to_name_or_id);
/** Broadcast signed transaction for manually sidechain withdrawal
* @param son_name_or_id ID or name of the son account
* @param block_num Block number where original withdrawal transaction is executed
* @param sidechain Sidechain type (bitcoin, HIVE, etc)
* @param peerplays_uid peerplays_uid
* @param peerplays_transaction_id ID of transaction
* @param peerplays_from Sidechain address transaction from
* @param withdraw_sidechain Withdraw sidechain
* @param withdraw_address Withdraw address
* @param withdraw_currency Withdraw currency
* @param withdraw_amount Withdraw amount
* @returns the signed transaction.
*/
signed_transaction sidechain_withdrawal_transaction(const string &son_name_or_id,
uint32_t block_num,
const sidechain_type& sidechain,
const std::string &peerplays_uid,
const std::string &peerplays_transaction_id,
const chain::account_id_type &peerplays_from,
const sidechain_type& withdraw_sidechain,
const std::string &withdraw_address,
const std::string &withdraw_currency,
const string &withdraw_amount);
/** Vote for a given witness.
*
* An account can publish a list of all witnesses they approve of. This
@ -2487,9 +2557,46 @@ class wallet_api
bool broadcast = false,
bool to_temp = false );
std::map<string,std::function<string(fc::variant,const fc::variants&)>> get_result_formatters() const;
/**
* @brief Get a list of vote_id_type that ID votes for
* @param account_name_or_id ID or name of the account to get votes for
* @return The list of vote_id_type ID votes for
*
*/
vector<vote_id_type> get_votes_ids(const string &account_name_or_id) const;
/**
* @brief Return the objects account_name_or_id votes for
* @param account_name_or_id ID or name of the account to get votes for
* @return The votes_info account_name_or_id votes for
*
*/
votes_info get_votes(const string &account_name_or_id) const;
/**
* @brief Get a list of accounts that votes for vote_id
* @param vote_id We search accounts that vote for this ID
* @return The accounts that votes for provided ID
*
*/
vector<account_object> get_voters_by_id(const vote_id_type &vote_id) const;
/**
* @brief Return the accounts that votes for account_name_or_id
* @param account_name_or_id ID or name of the account to get voters for
* @return The voters_info for account_name_or_id
*
*/
voters_info get_voters(const string &account_name_or_id) const;
/**
* @brief Demo plugin api
* @return The hello world string
*/
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log() const;
fc::signal<void(bool)> lock_changed;
std::shared_ptr<detail::wallet_api_impl> my;
void encrypt_keys();
@ -2626,8 +2733,10 @@ FC_API( graphene::wallet::wallet_api,
(get_witness)
(is_witness)
(get_committee_member)
(get_workers)
(list_witnesses)
(list_committee_members)
(list_workers)
(create_son)
(try_create_son)
(update_son)
@ -2643,6 +2752,7 @@ FC_API( graphene::wallet::wallet_api,
(get_son_wallets)
(add_sidechain_address)
(delete_sidechain_address)
(sidechain_withdrawal_transaction)
(get_sidechain_addresses_by_account)
(get_sidechain_addresses_by_sidechain)
(get_sidechain_address_by_account_and_sidechain)
@ -2657,6 +2767,7 @@ FC_API( graphene::wallet::wallet_api,
(vote_for_committee_member)
(vote_for_son)
(update_son_votes)
(sidechain_deposit_transaction)
(vote_for_witness)
(update_witness_votes)
(set_voting_proxy)
@ -2795,4 +2906,9 @@ FC_API( graphene::wallet::wallet_api,
(get_custom_account_authorities_by_permission_id)
(get_custom_account_authorities_by_permission_name)
(get_active_custom_account_authorities_by_operation)
(get_votes_ids)
(get_votes)
(get_voters_by_id)
(get_voters)
(get_son_listener_log)
)

View file

@ -84,6 +84,7 @@
#include <graphene/wallet/api_documentation.hpp>
#include <graphene/wallet/reflect_util.hpp>
#include <graphene/debug_witness/debug_api.hpp>
#include <graphene/peerplays_sidechain/sidechain_api.hpp>
#ifndef WIN32
# include <sys/types.h>
@ -749,7 +750,7 @@ public:
{
std::string account_id = account_id_to_string(id);
auto rec = _remote_db->get_accounts({account_id}).front();
FC_ASSERT(rec);
FC_ASSERT(rec, "Accout id: ${account_id} doesn't exist", ("account_id", account_id));
return *rec;
}
account_object get_account(string account_name_or_id) const
@ -762,7 +763,7 @@ public:
return get_account(*id);
} else {
auto rec = _remote_db->lookup_account_names({account_name_or_id}).front();
FC_ASSERT( rec && rec->name == account_name_or_id );
FC_ASSERT( rec && rec->name == account_name_or_id, "Account name or id: ${account_name_or_id} doesn't exist", ("account_name_or_id",account_name_or_id ) );
return *rec;
}
}
@ -1946,7 +1947,7 @@ public:
try
{
account_id_type owner_account_id = get_account_id(owner_account);
fc::optional<son_object> son = _remote_db->get_son_by_account(owner_account_id);
fc::optional<son_object> son = _remote_db->get_son_by_account_id(owner_account_id);
if (son)
return *son;
else
@ -1961,6 +1962,49 @@ public:
FC_CAPTURE_AND_RETHROW( (owner_account) )
}
vector<worker_object> get_workers(string owner_account)
{
try
{
fc::optional<worker_id_type> worker_id = maybe_id<worker_id_type>(owner_account);
if (worker_id)
{
std::vector<worker_id_type> ids_to_get;
ids_to_get.push_back(*worker_id);
std::vector<fc::optional<worker_object>> worker_objects = _remote_db->get_workers(ids_to_get);
if(!worker_objects.empty()) {
std::vector<worker_object> result;
for (const auto &worker_object : worker_objects) {
if (worker_object)
result.emplace_back(*worker_object);
}
return result;
}
else
FC_THROW("No workers is registered for id ${id}", ("id", owner_account));
}
else
{
// then maybe it's the owner account
try
{
std::string owner_account_id = account_id_to_string(get_account_id(owner_account));
auto workers = _remote_db->get_workers_by_account(owner_account_id);
if (!workers.empty())
return workers;
else
FC_THROW("No workers is registered for account ${account}", ("account", owner_account));
}
catch (const fc::exception&)
{
FC_THROW("No account or worker named ${account}", ("account", owner_account));
}
}
}
FC_CAPTURE_AND_RETHROW( (owner_account) )
}
bool is_witness(string owner_account)
{
try
@ -2075,7 +2119,7 @@ public:
son_create_op.pay_vb = pay_vb_id;
son_create_op.sidechain_public_keys = sidechain_public_keys;
if (_remote_db->get_son_by_account(son_create_op.owner_account))
if (_remote_db->get_son_by_account_id(son_create_op.owner_account))
FC_THROW("Account ${owner_account} is already a SON", ("owner_account", owner_account));
signed_transaction tx;
@ -2201,13 +2245,10 @@ public:
std::vector<fc::optional<son_object>> son_objects = _remote_db->get_sons(son_ids);
vector<std::string> owners;
for(auto obj: son_objects)
{
if (obj)
{
std::string acc_id = account_id_to_string(obj->son_account);
owners.push_back(acc_id);
}
}
vector< optional< account_object> > accs = _remote_db->get_accounts(owners);
std::remove_if(son_objects.begin(), son_objects.end(),
[](const fc::optional<son_object>& obj) -> bool { return obj.valid(); });
@ -2216,9 +2257,7 @@ public:
std::inserter(result, result.end()),
[](fc::optional<account_object>& acct, fc::optional<son_object> son) {
FC_ASSERT(acct, "Invalid active SONs list in global properties.");
if (son.valid() && son->status != son_status::deregistered)
return std::make_pair<string, son_id_type>(string(acct->name), std::move(son->id));
return std::make_pair<string, son_id_type>(string(acct->name), std::move(son_id_type()));
});
return result;
} FC_CAPTURE_AND_RETHROW() }
@ -2323,6 +2362,64 @@ public:
} FC_CAPTURE_AND_RETHROW() }
signed_transaction sidechain_withdrawal_transaction(const string &son_name_or_id,
uint32_t block_num,
const sidechain_type& sidechain,
const std::string &peerplays_uid,
const std::string &peerplays_transaction_id,
const chain::account_id_type &peerplays_from,
const sidechain_type& withdraw_sidechain,
const std::string &withdraw_address,
const std::string &withdraw_currency,
const string &withdraw_amount)
{ try{
const global_property_object& gpo = get_global_properties();
const auto dynamic_props = get_dynamic_global_properties();
const auto son_obj = get_son(son_name_or_id);
fc::optional<asset_object> peerplays_asset = get_asset(withdraw_currency);
FC_ASSERT(peerplays_asset, "Could not find asset matching ${asset}", ("asset", peerplays_asset));
const auto asset_val = peerplays_asset->amount_from_string(withdraw_amount);
const auto asset_price = peerplays_asset->options.core_exchange_rate;
price withdraw_currency_price = {};
if ("BTC" == withdraw_currency) {
fc::optional<asset_object> a = get_asset( gpo.parameters.btc_asset());
withdraw_currency_price = a->options.core_exchange_rate;
} else
if ("HBD" == withdraw_currency) {
fc::optional<asset_object> a = get_asset( gpo.parameters.hbd_asset());
withdraw_currency_price = a->options.core_exchange_rate;
} else
if ("HIVE" == withdraw_currency) {
fc::optional<asset_object> a = get_asset( gpo.parameters.hive_asset());
withdraw_currency_price = a->options.core_exchange_rate;
} else {
FC_THROW("withdraw_currency ${withdraw_currency}", ("withdraw_currency", withdraw_currency));
}
//! Create transaction
signed_transaction son_wallet_withdraw_create_transaction;
son_wallet_withdraw_create_operation op;
op.payer = son_obj.son_account;
op.son_id = son_obj.id;
op.timestamp = dynamic_props.time;
op.block_num = block_num;
op.sidechain = sidechain;
op.peerplays_uid = peerplays_uid;
op.peerplays_transaction_id = peerplays_transaction_id;
op.peerplays_from = peerplays_from;
op.peerplays_asset = asset(asset_val.amount * asset_price.base.amount / asset_price.quote.amount);
op.withdraw_sidechain = withdraw_sidechain;
op.withdraw_address = withdraw_address;
op.withdraw_currency = withdraw_currency;
op.withdraw_amount = asset_val.amount;
son_wallet_withdraw_create_transaction.operations.push_back(op);
return sign_transaction(son_wallet_withdraw_create_transaction, true);
} FC_CAPTURE_AND_RETHROW( (withdraw_currency) ) }
signed_transaction create_witness(string owner_account,
string url,
bool broadcast /* = false */)
@ -2717,7 +2814,7 @@ public:
account_object voting_account_object = get_account(voting_account);
account_id_type son_account_id = get_account_id(son);
fc::optional<son_object> son_obj = _remote_db->get_son_by_account(son_account_id);
fc::optional<son_object> son_obj = _remote_db->get_son_by_account_id(son_account_id);
if (!son_obj)
FC_THROW("Account ${son} is not registered as a son", ("son", son));
if (approve)
@ -2761,7 +2858,7 @@ public:
for (const std::string& son : sons_to_approve)
{
account_id_type son_owner_account_id = get_account_id(son);
fc::optional<son_object> son_obj = _remote_db->get_son_by_account(son_owner_account_id);
fc::optional<son_object> son_obj = _remote_db->get_son_by_account_id(son_owner_account_id);
if (!son_obj)
FC_THROW("Account ${son} is not registered as a SON", ("son", son));
auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id);
@ -2771,7 +2868,7 @@ public:
for (const std::string& son : sons_to_reject)
{
account_id_type son_owner_account_id = get_account_id(son);
fc::optional<son_object> son_obj = _remote_db->get_son_by_account(son_owner_account_id);
fc::optional<son_object> son_obj = _remote_db->get_son_by_account_id(son_owner_account_id);
if (!son_obj)
FC_THROW("Account ${son} is not registered as a SON", ("son", son));
unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id);
@ -2792,6 +2889,81 @@ public:
return sign_transaction( tx, broadcast );
} FC_CAPTURE_AND_RETHROW( (voting_account)(sons_to_approve)(sons_to_reject)(desired_number_of_sons)(broadcast) ) }
signed_transaction sidechain_deposit_transaction( const string &son_name_or_id,
const sidechain_type& sidechain,
const string &transaction_id,
uint32_t operation_index,
const string &sidechain_from,
const string &sidechain_to,
const string &sidechain_currency,
int64_t sidechain_amount,
const string &peerplays_from_name_or_id,
const string &peerplays_to_name_or_id )
{
//! Get data we need to procced transaction
const auto dynamic_global_props = get_dynamic_global_properties();
const auto global_props = get_global_properties();
const auto son_obj = get_son(son_name_or_id);
const std::string sidechain_str = [&sidechain](){
switch (sidechain) {
case sidechain_type::peerplays : return "peerplays";
case sidechain_type::bitcoin : return "bitcoin";
case sidechain_type::hive : return "hive";
default:
FC_THROW("Wrong sidechain type: ${sidechain}", ("sidechain", sidechain));
}
}();
const auto peerplays_from_obj = get_account(peerplays_from_name_or_id);
const auto peerplays_to_obj = get_account(peerplays_to_name_or_id);
const price sidechain_currency_price = [this, &sidechain_currency, &global_props](){
if(sidechain_currency == "BTC")
{
fc::optional<asset_object> asset_obj = get_asset(object_id_to_string(global_props.parameters.btc_asset()));
FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", "BTC"));
return asset_obj->options.core_exchange_rate;
}
else if(sidechain_currency == "HBD")
{
fc::optional<asset_object> asset_obj = get_asset(object_id_to_string(global_props.parameters.hbd_asset()));
FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", "HBD"));
return asset_obj->options.core_exchange_rate;
}
else if(sidechain_currency == "HIVE")
{
fc::optional<asset_object> asset_obj = get_asset(object_id_to_string(global_props.parameters.hive_asset()));
FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", "HIVE"));
return asset_obj->options.core_exchange_rate;
}
else
{
fc::optional<asset_object> asset_obj = get_asset(sidechain_currency);
FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", sidechain_currency));
return asset_obj->options.core_exchange_rate;
}
}();
//! Create transaction
signed_transaction son_wallet_deposit_create_transaction;
son_wallet_deposit_create_operation op;
op.payer = son_obj.son_account;
op.son_id = son_obj.id;
op.timestamp = dynamic_global_props.time;
op.block_num = dynamic_global_props.head_block_number;
op.sidechain = sidechain;
op.sidechain_uid = sidechain_str + "-" + transaction_id + "-" + std::to_string(operation_index);
op.sidechain_transaction_id = transaction_id;
op.sidechain_from = sidechain_from;
op.sidechain_to = sidechain_to;
op.sidechain_currency = sidechain_currency;
op.sidechain_amount = sidechain_amount;
op.peerplays_from = peerplays_from_obj.id;
op.peerplays_to = peerplays_to_obj.id;
op.peerplays_asset = asset(op.sidechain_amount * sidechain_currency_price.base.amount / sidechain_currency_price.quote.amount);
son_wallet_deposit_create_transaction.operations.push_back(op);
return sign_transaction(son_wallet_deposit_create_transaction, true);
}
signed_transaction vote_for_witness(string voting_account,
string witness,
bool approve,
@ -4007,6 +4179,26 @@ public:
}
}
void use_sidechain_api()
{
if( _remote_sidechain )
return;
try
{
_remote_sidechain = _remote_api->sidechain();
}
catch( const fc::exception& e )
{
std::cerr << "\nCouldn't get sidechain API. You probably are not configured\n"
"to access the sidechain API on the node you are connecting to.\n"
"\n"
"To fix this problem:\n"
"- Please ensure peerplays_sidechain plugin is enabled.\n"
"- Please follow the instructions in README.md to set up an apiaccess file.\n"
"\n";
}
}
void network_add_nodes( const vector<string>& nodes )
{
use_network_node_api();
@ -4087,6 +4279,52 @@ public:
return it->second;
}
vector<vote_id_type> get_votes_ids(const string &account_name_or_id) const
{
try
{
return _remote_db->get_votes_ids(account_name_or_id);
}
FC_CAPTURE_AND_RETHROW( (account_name_or_id) )
}
votes_info get_votes(const string &account_name_or_id) const
{
try
{
return _remote_db->get_votes(account_name_or_id);
}
FC_CAPTURE_AND_RETHROW( (account_name_or_id) )
}
vector<account_object> get_voters_by_id(const vote_id_type &vote_id) const
{
try
{
return _remote_db->get_voters_by_id(vote_id);
}
FC_CAPTURE_AND_RETHROW( (vote_id) )
}
voters_info get_voters(const string &account_name_or_id) const
{
try
{
return _remote_db->get_voters(account_name_or_id);
}
FC_CAPTURE_AND_RETHROW( (account_name_or_id) )
}
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log()
{
use_sidechain_api();
try
{
return (*_remote_sidechain)->get_son_listener_log();
}
FC_CAPTURE_AND_RETHROW()
}
string _wallet_filename;
wallet_data _wallet;
@ -4101,6 +4339,7 @@ public:
fc::api<bookie_api> _remote_bookie;
optional< fc::api<network_node_api> > _remote_net_node;
optional< fc::api<graphene::debug_witness::debug_api> > _remote_debug;
optional< fc::api<graphene::peerplays_sidechain::sidechain_api> > _remote_sidechain;
flat_map<string, operation> _prototype_ops;
@ -4184,6 +4423,8 @@ string operation_printer::operator()(const transfer_operation& op) const
std::string memo;
if( op.memo )
{
bool is_encrypted = ((op.memo->from != public_key_type()) && (op.memo->to != public_key_type()));
if (is_encrypted) {
if( wallet.is_locked() )
{
out << " -- Unlock wallet to see memo.";
@ -4206,6 +4447,10 @@ string operation_printer::operator()(const transfer_operation& op) const
elog("Error when decrypting memo: ${e}", ("e", e.to_detail_string()));
}
}
} else {
memo = op.memo->get_message(private_key_type(), public_key_type());
out << " -- Memo: " << memo;
}
}
fee(op.fee);
return memo;
@ -4434,6 +4679,7 @@ asset wallet_api::get_lottery_balance( asset_id_type lottery_id )const
vector<operation_detail> wallet_api::get_account_history(string name, int limit) const
{
FC_ASSERT(my->get_account(name).name == name, "Account name: ${account_name} doesn't exist", ("account_name", name));
vector<operation_detail> result;
while (limit > 0)
@ -4488,6 +4734,8 @@ vector<operation_detail> wallet_api::get_relative_account_history(string name, u
{
FC_ASSERT( start > 0 || limit <= 100 );
FC_ASSERT(my->get_account(name).name == name, "Account name: ${account_name} doesn't exist", ("account_name", name));
vector<operation_detail> result;
@ -4663,7 +4911,7 @@ account_object wallet_api::get_account(string account_name_or_id) const
asset_object wallet_api::get_asset(string asset_name_or_id) const
{
auto a = my->find_asset(asset_name_or_id);
FC_ASSERT(a);
FC_ASSERT(a, "Asset name or id: ${asset_name_or_id} doesn't exist", ("asset_name_or_id", asset_name_or_id) );
return *a;
}
@ -4686,7 +4934,7 @@ asset_id_type wallet_api::get_asset_id(string asset_symbol_or_id) const
bool wallet_api::import_key(string account_name_or_id, string wif_key)
{
FC_ASSERT(!is_locked());
FC_ASSERT(!is_locked(), "Wallet is locked, please unlock to get all operations available");
// backup wallet
fc::optional<fc::ecc::private_key> optional_private_key = wif_to_key(wif_key);
if (!optional_private_key)
@ -4705,7 +4953,7 @@ bool wallet_api::import_key(string account_name_or_id, string wif_key)
map<string, bool> wallet_api::import_accounts( string filename, string password )
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
FC_ASSERT( fc::exists( filename ) );
const auto imported_keys = fc::json::from_file<exported_keys>( filename );
@ -4775,7 +5023,7 @@ map<string, bool> wallet_api::import_accounts( string filename, string password
bool wallet_api::import_account_keys( string filename, string password, string src_account_name, string dest_account_name )
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
FC_ASSERT( fc::exists( filename ) );
bool is_my_account = false;
@ -4990,6 +5238,11 @@ map<string,committee_member_id_type> wallet_api::list_committee_members(const st
return my->_remote_db->lookup_committee_member_accounts(lowerbound, limit);
}
map<string, worker_id_type> wallet_api::list_workers(const string& lowerbound, uint32_t limit)
{
return my->_remote_db->lookup_worker_accounts(lowerbound, limit);
}
son_object wallet_api::get_son(string owner_account)
{
return my->get_son(owner_account);
@ -5010,6 +5263,11 @@ committee_member_object wallet_api::get_committee_member(string owner_account)
return my->get_committee_member(owner_account);
}
vector<worker_object> wallet_api::get_workers(string owner_account)
{
return my->get_workers(owner_account);
}
signed_transaction wallet_api::create_vesting_balance(string owner_account,
string amount,
string asset_symbol,
@ -5140,6 +5398,29 @@ signed_transaction wallet_api::delete_sidechain_address(string account,
return my->delete_sidechain_address(account, sidechain, broadcast);
}
signed_transaction wallet_api::sidechain_withdrawal_transaction(const string &son_name_or_id,
uint32_t block_num,
const sidechain_type& sidechain,
const std::string &peerplays_uid,
const std::string &peerplays_transaction_id,
const chain::account_id_type &peerplays_from,
const sidechain_type& withdraw_sidechain,
const std::string &withdraw_address,
const std::string &withdraw_currency,
const string &withdraw_amount)
{
return my->sidechain_withdrawal_transaction(son_name_or_id,
block_num,
sidechain,
peerplays_uid,
peerplays_transaction_id,
peerplays_from,
withdraw_sidechain,
withdraw_address,
withdraw_currency,
withdraw_amount);
}
vector<optional<sidechain_address_object>> wallet_api::get_sidechain_addresses_by_account(string account)
{
account_id_type account_id = get_account_id(account);
@ -5248,6 +5529,29 @@ signed_transaction wallet_api::update_son_votes(string voting_account,
return my->update_son_votes(voting_account, sons_to_approve, sons_to_reject, desired_number_of_sons, broadcast);
}
signed_transaction wallet_api::sidechain_deposit_transaction( const string &son_name_or_id,
const sidechain_type& sidechain,
const string &transaction_id,
uint32_t operation_index,
const string &sidechain_from,
const string &sidechain_to,
const string &sidechain_currency,
int64_t sidechain_amount,
const string &peerplays_from_name_or_id,
const string &peerplays_to_name_or_id)
{
return my->sidechain_deposit_transaction(son_name_or_id,
sidechain,
transaction_id,
operation_index,
sidechain_from,
sidechain_to,
sidechain_currency,
sidechain_amount,
peerplays_from_name_or_id,
peerplays_to_name_or_id);
}
signed_transaction wallet_api::vote_for_witness(string voting_account,
string witness,
bool approve,
@ -5314,13 +5618,13 @@ operation wallet_api::get_prototype_operation(string operation_name)
void wallet_api::dbg_make_uia(string creator, string symbol)
{
FC_ASSERT(!is_locked());
FC_ASSERT(!is_locked(), "Wallet is locked, please unlock to get all operations available");
my->dbg_make_uia(creator, symbol);
}
void wallet_api::dbg_make_mia(string creator, string symbol)
{
FC_ASSERT(!is_locked());
FC_ASSERT(!is_locked(), "Wallet is locked, please unlock to get all operations available");
my->dbg_make_mia(creator, symbol);
}
@ -5356,7 +5660,7 @@ vector< variant > wallet_api::network_get_connected_peers()
void wallet_api::flood_network(string prefix, uint32_t number_of_transactions)
{
FC_ASSERT(!is_locked());
FC_ASSERT(!is_locked(), "Wallet is locked, please unlock to get all operations available");
my->flood_network(prefix, number_of_transactions);
}
@ -5835,13 +6139,13 @@ signed_transaction wallet_api::buy( string buyer_account,
signed_transaction wallet_api::borrow_asset(string seller_name, string amount_to_sell,
string asset_symbol, string amount_of_collateral, bool broadcast)
{
FC_ASSERT(!is_locked());
FC_ASSERT(!is_locked(), "Wallet is locked, please unlock to get all operations available");
return my->borrow_asset(seller_name, amount_to_sell, asset_symbol, amount_of_collateral, broadcast);
}
signed_transaction wallet_api::cancel_order(object_id_type order_id, bool broadcast)
{
FC_ASSERT(!is_locked());
FC_ASSERT(!is_locked(), "Wallet is locked, please unlock to get all operations available");
return my->cancel_order(order_id, broadcast);
}
@ -5891,7 +6195,7 @@ map<string,public_key_type> wallet_api::get_blind_accounts()const
}
map<string,public_key_type> wallet_api::get_my_blind_accounts()const
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
map<string,public_key_type> result;
for( const auto& item : my->_wallet.labeled_keys )
{
@ -5903,7 +6207,7 @@ map<string,public_key_type> wallet_api::get_my_blind_accounts()const
public_key_type wallet_api::create_blind_account( string label, string brain_key )
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
auto label_itr = my->_wallet.labeled_keys.get<by_label>().find(label);
if( label_itr != my->_wallet.labeled_keys.get<by_label>().end() )
@ -6031,7 +6335,7 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label,
blind_confirmation confirm;
try {
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
public_key_type from_key = get_public_key(from_key_or_label);
public_key_type to_key = get_public_key(to_key_or_label);
@ -6200,7 +6504,7 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name
vector<pair<string, string>> to_amounts,
bool broadcast )
{ try {
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
idump((to_amounts));
blind_confirmation confirm;
@ -6280,7 +6584,7 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name
blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo )
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
stealth_confirmation conf(confirmation_receipt);
FC_ASSERT( conf.to );
@ -6422,7 +6726,7 @@ signed_transaction wallet_api::propose_create_sport(
internationalized_string_type name,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
sport_create_operation sport_create_op;
@ -6450,7 +6754,7 @@ signed_transaction wallet_api::propose_update_sport(
fc::optional<internationalized_string_type> name,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
sport_update_operation sport_update_op;
@ -6478,7 +6782,7 @@ signed_transaction wallet_api::propose_delete_sport(
sport_id_type sport_id,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
sport_delete_operation sport_delete_op;
@ -6506,7 +6810,7 @@ signed_transaction wallet_api::propose_create_event_group(
sport_id_type sport_id,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
event_group_create_operation event_group_create_op;
@ -6536,7 +6840,7 @@ signed_transaction wallet_api::propose_update_event_group(
fc::optional<internationalized_string_type> name,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
event_group_update_operation event_group_update_op;
@ -6565,7 +6869,7 @@ signed_transaction wallet_api::propose_delete_event_group(
event_group_id_type event_group,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
event_group_delete_operation event_group_delete_op;
@ -6595,7 +6899,7 @@ signed_transaction wallet_api::propose_create_event(
event_group_id_type event_group_id,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
event_create_operation event_create_op;
@ -6630,7 +6934,7 @@ signed_transaction wallet_api::propose_update_event(
fc::optional<time_point_sec> start_time,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
event_update_operation event_update_op;
@ -6663,7 +6967,7 @@ signed_transaction wallet_api::propose_create_betting_market_rules(
internationalized_string_type description,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
betting_market_rules_create_operation betting_market_rules_create_op;
@ -6693,7 +6997,7 @@ signed_transaction wallet_api::propose_update_betting_market_rules(
fc::optional<internationalized_string_type> description,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
betting_market_rules_update_operation betting_market_rules_update_op;
@ -6725,7 +7029,7 @@ signed_transaction wallet_api::propose_create_betting_market_group(
asset_id_type asset_id,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
betting_market_group_create_operation betting_market_group_create_op;
@ -6758,7 +7062,7 @@ signed_transaction wallet_api::propose_update_betting_market_group(
fc::optional<betting_market_group_status> status,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
betting_market_group_update_operation betting_market_group_update_op;
@ -6790,7 +7094,7 @@ signed_transaction wallet_api::propose_create_betting_market(
internationalized_string_type payout_condition,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
betting_market_create_operation betting_market_create_op;
@ -6822,7 +7126,7 @@ signed_transaction wallet_api::propose_update_betting_market(
fc::optional<internationalized_string_type> payout_condition,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
betting_market_update_operation betting_market_update_op;
@ -6854,7 +7158,7 @@ signed_transaction wallet_api::place_bet(string betting_account,
double backer_multiplier,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
fc::optional<asset_object> asset_obj = get_asset(asset_symbol);
FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol));
@ -6879,7 +7183,7 @@ signed_transaction wallet_api::cancel_bet(string betting_account,
bet_id_type bet_id,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
@ -6902,7 +7206,7 @@ signed_transaction wallet_api::propose_resolve_betting_market_group(
const std::map<betting_market_id_type, betting_market_resolution_type>& resolutions,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
betting_market_group_resolve_operation betting_market_group_resolve_op;
@ -6930,7 +7234,7 @@ signed_transaction wallet_api::propose_cancel_betting_market_group(
betting_market_group_id_type betting_market_group_id,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
const chain_parameters& current_params = get_global_properties().parameters;
betting_market_group_cancel_unmatched_bets_operation betting_market_group_cancel_unmatched_bets_op;
@ -6953,7 +7257,7 @@ signed_transaction wallet_api::propose_cancel_betting_market_group(
signed_transaction wallet_api::tournament_create( string creator, tournament_options options, bool broadcast )
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
account_object creator_account_obj = get_account(creator);
signed_transaction tx;
@ -6974,7 +7278,7 @@ signed_transaction wallet_api::tournament_join( string payer_account,
string buy_in_asset_symbol,
bool broadcast )
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
account_object payer_account_obj = get_account(payer_account);
account_object player_account_obj = get_account(player_account);
//graphene::chain::tournament_object tournament_obj = my->get_object<graphene::chain::tournament_object>(tournament_id);
@ -7001,7 +7305,7 @@ signed_transaction wallet_api::tournament_leave( string canceling_account,
tournament_id_type tournament_id,
bool broadcast)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
account_object player_account_obj = get_account(player_account);
account_object canceling_account_obj = get_account(canceling_account);
//graphene::chain::tournament_object tournament_obj = my->get_object<graphene::chain::tournament_object>(tournament_id);
@ -7046,7 +7350,7 @@ signed_transaction wallet_api::rps_throw(game_id_type game_id,
rock_paper_scissors_gesture gesture,
bool broadcast)
{
FC_ASSERT( !is_locked() );
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
// check whether the gesture is appropriate for the game we're playing
graphene::chain::game_object game_obj = my->get_object<graphene::chain::game_object>(game_id);
@ -7582,6 +7886,31 @@ std::vector<matched_bet_object> wallet_api::get_all_matched_bets_for_bettor(acco
return( my->_remote_bookie->get_all_matched_bets_for_bettor(bettor_id, start, limit) );
}
vector<vote_id_type> wallet_api::get_votes_ids(const string &account_name_or_id) const
{
return my->get_votes_ids(account_name_or_id);
}
votes_info wallet_api::get_votes(const string &account_name_or_id) const
{
return my->get_votes(account_name_or_id);
}
vector<account_object> wallet_api::get_voters_by_id(const vote_id_type &vote_id) const
{
return my->get_voters_by_id(vote_id);
}
voters_info wallet_api::get_voters(const string &account_name_or_id) const
{
return my->get_voters(account_name_or_id);
}
std::map<sidechain_type, std::vector<std::string>> wallet_api::get_son_listener_log() const
{
return my->get_son_listener_log();
}
// default ctor necessary for FC_REFLECT
signed_block_with_info::signed_block_with_info( const signed_block& block )
: signed_block( block )

View file

@ -3,7 +3,7 @@
import json
import os
import re
import xml.etree.ElementTree as etree
import defusedxml.ElementTree as etree
def process_node(path, node):
"""

View file

@ -27,8 +27,18 @@
#include <iostream>
#include <iterator>
#include <boost/algorithm/string/replace.hpp>
#include <boost/program_options.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/version.hpp>
#include <fc/interprocess/signals.hpp>
#include <fc/io/json.hpp>
#include <fc/io/stdio.hpp>
#include <fc/log/console_appender.hpp>
#include <fc/log/file_appender.hpp>
#include <fc/log/logger.hpp>
#include <fc/log/logger_config.hpp>
#include <fc/network/http/server.hpp>
#include <fc/network/http/websocket.hpp>
#include <fc/rpc/cli.hpp>
@ -40,25 +50,14 @@
#include <graphene/chain/config.hpp>
#include <graphene/chain/protocol/protocol.hpp>
#include <graphene/egenesis/egenesis.hpp>
#include <graphene/utilities/git_revision.hpp>
#include <graphene/utilities/key_conversion.hpp>
#include <graphene/wallet/wallet.hpp>
#include <fc/interprocess/signals.hpp>
#include <boost/program_options.hpp>
#include <fc/log/console_appender.hpp>
#include <fc/log/file_appender.hpp>
#include <fc/log/logger.hpp>
#include <fc/log/logger_config.hpp>
#include <graphene/utilities/git_revision.hpp>
#include <boost/version.hpp>
#include <boost/algorithm/string/replace.hpp>
#ifdef WIN32
# include <signal.h>
#include <signal.h>
#else
# include <csignal>
#include <csignal>
#endif
using namespace graphene::app;
@ -68,44 +67,54 @@ using namespace graphene::wallet;
using namespace std;
namespace bpo = boost::program_options;
int main( int argc, char** argv )
{
int main(int argc, char **argv) {
try {
boost::program_options::options_description opts;
opts.add_options()
("help,h", "Print this help message and exit.")
("version", "Display the version info and exit")
("server-rpc-endpoint,s", bpo::value<string>()->implicit_value("ws://127.0.0.1:8090"), "Server websocket RPC endpoint")
("server-rpc-user,u", bpo::value<string>(), "Server Username")
("server-rpc-password,p", bpo::value<string>(), "Server Password")
("rpc-endpoint,r", bpo::value<string>()->implicit_value("127.0.0.1:8091"), "Endpoint for wallet websocket RPC to listen on")
("rpc-tls-endpoint,t", bpo::value<string>()->implicit_value("127.0.0.1:8092"), "Endpoint for wallet websocket TLS RPC to listen on")
("rpc-tls-certificate,c", bpo::value<string>()->implicit_value("server.pem"), "PEM certificate for wallet websocket TLS RPC")
("rpc-http-endpoint,H", bpo::value<string>()->implicit_value("127.0.0.1:8093"), "Endpoint for wallet HTTP RPC to listen on")
("daemon,d", "Run the wallet in daemon mode" )
("wallet-file,w", bpo::value<string>()->implicit_value("wallet.json"), "wallet to load")
("chain-id", bpo::value<string>(), "chain ID to connect to");
opts.add_options()("help,h", "Print this help message and exit.");
opts.add_options()("version,v", "Display the version info and exit");
opts.add_options()("server-rpc-endpoint,s", bpo::value<string>()->implicit_value("ws://127.0.0.1:8090"), "Server websocket RPC endpoint");
opts.add_options()("server-rpc-user,u", bpo::value<string>(), "Server Username");
opts.add_options()("server-rpc-password,p", bpo::value<string>(), "Server Password");
opts.add_options()("rpc-endpoint,r", bpo::value<string>()->implicit_value("127.0.0.1:8091"), "Endpoint for wallet websocket RPC to listen on");
opts.add_options()("rpc-tls-endpoint,t", bpo::value<string>()->implicit_value("127.0.0.1:8092"), "Endpoint for wallet websocket TLS RPC to listen on");
opts.add_options()("rpc-tls-certificate,c", bpo::value<string>()->implicit_value("server.pem"), "PEM certificate for wallet websocket TLS RPC");
opts.add_options()("rpc-http-endpoint,H", bpo::value<string>()->implicit_value("127.0.0.1:8093"), "Endpoint for wallet HTTP RPC to listen on");
opts.add_options()("daemon,d", "Run the wallet in daemon mode");
opts.add_options()("wallet-file,w", bpo::value<string>()->implicit_value("wallet.json"), "wallet to load");
opts.add_options()("chain-id", bpo::value<string>(), "chain ID to connect to");
bpo::variables_map options;
bpo::store( bpo::parse_command_line(argc, argv, opts), options );
try {
bpo::parsed_options po = bpo::command_line_parser(argc, argv).options(opts).allow_unregistered().run();
std::vector<std::string> unrecognized = bpo::collect_unrecognized(po.options, bpo::include_positional);
if (unrecognized.size() > 0) {
std::cout << "Unknown parameter(s): " << std::endl;
for (auto s : unrecognized) {
std::cout << " " << s << std::endl;
}
return 0;
}
bpo::store(po, options);
} catch (const boost::program_options::invalid_command_line_syntax &e) {
std::cout << e.what() << std::endl;
return 0;
}
if( options.count("help") )
{
if (options.count("help")) {
std::cout << opts << "\n";
return 0;
}
if (options.count("version"))
{
if (options.count("version")) {
std::string wallet_version(graphene::utilities::git_revision_description);
const size_t pos = wallet_version.find('/');
if( pos != std::string::npos && wallet_version.size() > pos )
wallet_version = wallet_version.substr( pos + 1 );
std::cerr << "Version: " << wallet_version << "\n";
std::cerr << "Git Revision: " << graphene::utilities::git_revision_sha << "\n";
std::cerr << "Built: " << __DATE__ " at " __TIME__ << "\n";
if (pos != std::string::npos && wallet_version.size() > pos)
wallet_version = wallet_version.substr(pos + 1);
std::cout << "Version: " << wallet_version << "\n";
std::cout << "Git Revision: " << graphene::utilities::git_revision_sha << "\n";
std::cout << "Built: " << __DATE__ " at " __TIME__ << "\n";
std::cout << "SSL: " << OPENSSL_VERSION_TEXT << "\n";
std::cout << "Boost: " << boost::replace_all_copy(std::string(BOOST_LIB_VERSION), "_", ".") << "\n";
return 0;
@ -119,30 +128,30 @@ int main( int argc, char** argv )
ac.filename = log_dir / "rpc" / "rpc.log";
ac.flush = true;
ac.rotate = true;
ac.rotation_interval = fc::hours( 1 );
ac.rotation_limit = fc::days( 1 );
ac.rotation_interval = fc::hours(1);
ac.rotation_limit = fc::days(1);
std::cout << "Logging RPC to file: " << (data_dir / ac.filename).preferred_string() << "\n";
cfg.appenders.push_back(fc::appender_config( "default", "console", fc::variant(fc::console_appender::config(), 20)));
cfg.appenders.push_back(fc::appender_config( "rpc", "file", fc::variant(ac, 5)));
cfg.appenders.push_back(fc::appender_config("default", "console", fc::variant(fc::console_appender::config(), 20)));
cfg.appenders.push_back(fc::appender_config("rpc", "file", fc::variant(ac, 5)));
cfg.loggers = { fc::logger_config("default"), fc::logger_config( "rpc") };
cfg.loggers = {fc::logger_config("default"), fc::logger_config("rpc")};
cfg.loggers.front().level = fc::log_level::warn;
cfg.loggers.front().appenders = {"default"};
cfg.loggers.back().level = fc::log_level::info;
cfg.loggers.back().appenders = {"rpc"};
fc::configure_logging( cfg );
fc::configure_logging(cfg);
fc::ecc::private_key committee_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")));
idump( (key_to_wif( committee_private_key ) ) );
idump((key_to_wif(committee_private_key)));
fc::ecc::private_key nathan_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan")));
public_key_type nathan_pub_key = nathan_private_key.get_public_key();
idump( (nathan_pub_key) );
idump( (key_to_wif( nathan_private_key ) ) );
idump((nathan_pub_key));
idump((key_to_wif(nathan_private_key)));
//
// TODO: We read wallet_data twice, once in main() to grab the
@ -152,153 +161,136 @@ int main( int argc, char** argv )
//
wallet_data wdata;
fc::path wallet_file( options.count("wallet-file") ? options.at("wallet-file").as<string>() : "wallet.json");
if( fc::exists( wallet_file ) )
{
wdata = fc::json::from_file( wallet_file ).as<wallet_data>( GRAPHENE_MAX_NESTED_OBJECTS );
if( options.count("chain-id") )
{
fc::path wallet_file(options.count("wallet-file") ? options.at("wallet-file").as<string>() : "wallet.json");
if (fc::exists(wallet_file)) {
wdata = fc::json::from_file(wallet_file).as<wallet_data>(GRAPHENE_MAX_NESTED_OBJECTS);
if (options.count("chain-id")) {
// the --chain-id on the CLI must match the chain ID embedded in the wallet file
if( chain_id_type(options.at("chain-id").as<std::string>()) != wdata.chain_id )
{
if (chain_id_type(options.at("chain-id").as<std::string>()) != wdata.chain_id) {
std::cout << "Chain ID in wallet file does not match specified chain ID\n";
return 1;
}
}
}
else
{
if( options.count("chain-id") )
{
} else {
if (options.count("chain-id")) {
wdata.chain_id = chain_id_type(options.at("chain-id").as<std::string>());
std::cout << "Starting a new wallet with chain ID " << wdata.chain_id.str() << " (from CLI)\n";
}
else
{
} else {
wdata.chain_id = graphene::egenesis::get_egenesis_chain_id();
std::cout << "Starting a new wallet with chain ID " << wdata.chain_id.str() << " (from egenesis)\n";
}
}
// but allow CLI to override
if( options.count("server-rpc-endpoint") )
if (options.count("server-rpc-endpoint"))
wdata.ws_server = options.at("server-rpc-endpoint").as<std::string>();
if( options.count("server-rpc-user") )
if (options.count("server-rpc-user"))
wdata.ws_user = options.at("server-rpc-user").as<std::string>();
if( options.count("server-rpc-password") )
if (options.count("server-rpc-password"))
wdata.ws_password = options.at("server-rpc-password").as<std::string>();
fc::http::websocket_client client;
idump((wdata.ws_server));
auto con = client.connect( wdata.ws_server );
auto con = client.connect(wdata.ws_server);
auto apic = std::make_shared<fc::rpc::websocket_api_connection>(con, GRAPHENE_MAX_NESTED_OBJECTS);
auto remote_api = apic->get_remote_api< login_api >(1);
edump((wdata.ws_user)(wdata.ws_password) );
FC_ASSERT( remote_api->login( wdata.ws_user, wdata.ws_password ), "Failed to log in to API server" );
auto remote_api = apic->get_remote_api<login_api>(1);
edump((wdata.ws_user)(wdata.ws_password));
FC_ASSERT(remote_api->login(wdata.ws_user, wdata.ws_password), "Failed to log in to API server");
auto wapiptr = std::make_shared<wallet_api>( wdata, remote_api );
wapiptr->set_wallet_filename( wallet_file.generic_string() );
auto wapiptr = std::make_shared<wallet_api>(wdata, remote_api);
wapiptr->set_wallet_filename(wallet_file.generic_string());
wapiptr->load_wallet_file();
fc::api<wallet_api> wapi(wapiptr);
auto wallet_cli = std::make_shared<fc::rpc::cli>( GRAPHENE_MAX_NESTED_OBJECTS );
for( auto& name_formatter : wapiptr->get_result_formatters() )
wallet_cli->format_result( name_formatter.first, name_formatter.second );
auto wallet_cli = std::make_shared<fc::rpc::cli>(GRAPHENE_MAX_NESTED_OBJECTS);
for (auto &name_formatter : wapiptr->get_result_formatters())
wallet_cli->format_result(name_formatter.first, name_formatter.second);
boost::signals2::scoped_connection closed_connection(con->closed.connect([wallet_cli]{
boost::signals2::scoped_connection closed_connection(con->closed.connect([wallet_cli] {
cerr << "Server has disconnected us.\n";
wallet_cli->stop();
}));
(void)(closed_connection);
if( wapiptr->is_new() )
{
if (wapiptr->is_new()) {
std::cout << "Please use the set_password method to initialize a new wallet before continuing\n";
wallet_cli->set_prompt( "new >>> " );
wallet_cli->set_prompt("new >>> ");
} else
wallet_cli->set_prompt( "locked >>> " );
wallet_cli->set_prompt("locked >>> ");
boost::signals2::scoped_connection locked_connection(wapiptr->lock_changed.connect([&](bool locked) {
wallet_cli->set_prompt( locked ? "locked >>> " : "unlocked >>> " );
wallet_cli->set_prompt(locked ? "locked >>> " : "unlocked >>> ");
}));
std::shared_ptr<fc::http::websocket_server> _websocket_server;
if( options.count("rpc-endpoint") )
{
if (options.count("rpc-endpoint")) {
_websocket_server = std::make_shared<fc::http::websocket_server>();
_websocket_server->on_connection([&wapi]( const fc::http::websocket_connection_ptr& c ){
_websocket_server->on_connection([&wapi](const fc::http::websocket_connection_ptr &c) {
std::cout << "here... \n";
wlog("." );
wlog(".");
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(c, GRAPHENE_MAX_NESTED_OBJECTS);
wsc->register_api(wapi);
c->set_session_data( wsc );
c->set_session_data(wsc);
});
ilog( "Listening for incoming RPC requests on ${p}", ("p", options.at("rpc-endpoint").as<string>() ));
_websocket_server->listen( fc::ip::endpoint::from_string(options.at("rpc-endpoint").as<string>()) );
ilog("Listening for incoming RPC requests on ${p}", ("p", options.at("rpc-endpoint").as<string>()));
_websocket_server->listen(fc::ip::endpoint::from_string(options.at("rpc-endpoint").as<string>()));
_websocket_server->start_accept();
}
string cert_pem = "server.pem";
if( options.count( "rpc-tls-certificate" ) )
if (options.count("rpc-tls-certificate"))
cert_pem = options.at("rpc-tls-certificate").as<string>();
std::shared_ptr<fc::http::websocket_tls_server> _websocket_tls_server;
if( options.count("rpc-tls-endpoint") )
{
if (options.count("rpc-tls-endpoint")) {
_websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>(cert_pem);
_websocket_tls_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){
_websocket_tls_server->on_connection([&](const fc::http::websocket_connection_ptr &c) {
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(c, GRAPHENE_MAX_NESTED_OBJECTS);
wsc->register_api(wapi);
c->set_session_data( wsc );
c->set_session_data(wsc);
});
ilog( "Listening for incoming TLS RPC requests on ${p}", ("p", options.at("rpc-tls-endpoint").as<string>() ));
_websocket_tls_server->listen( fc::ip::endpoint::from_string(options.at("rpc-tls-endpoint").as<string>()) );
ilog("Listening for incoming TLS RPC requests on ${p}", ("p", options.at("rpc-tls-endpoint").as<string>()));
_websocket_tls_server->listen(fc::ip::endpoint::from_string(options.at("rpc-tls-endpoint").as<string>()));
_websocket_tls_server->start_accept();
}
auto _http_server = std::make_shared<fc::http::server>();
if( options.count("rpc-http-endpoint" ) )
{
ilog( "Listening for incoming HTTP RPC requests on ${p}", ("p", options.at("rpc-http-endpoint").as<string>() ) );
_http_server->listen( fc::ip::endpoint::from_string( options.at( "rpc-http-endpoint" ).as<string>() ) );
if (options.count("rpc-http-endpoint")) {
ilog("Listening for incoming HTTP RPC requests on ${p}", ("p", options.at("rpc-http-endpoint").as<string>()));
_http_server->listen(fc::ip::endpoint::from_string(options.at("rpc-http-endpoint").as<string>()));
//
// due to implementation, on_request() must come AFTER listen()
//
_http_server->on_request(
[&wapi]( const fc::http::request& req, const fc::http::server::response& resp )
{
std::shared_ptr< fc::rpc::http_api_connection > conn =
std::make_shared< fc::rpc::http_api_connection >( GRAPHENE_MAX_NESTED_OBJECTS );
conn->register_api( wapi );
conn->on_request( req, resp );
} );
[&wapi](const fc::http::request &req, const fc::http::server::response &resp) {
std::shared_ptr<fc::rpc::http_api_connection> conn =
std::make_shared<fc::rpc::http_api_connection>(GRAPHENE_MAX_NESTED_OBJECTS);
conn->register_api(wapi);
conn->on_request(req, resp);
});
}
if( !options.count( "daemon" ) )
{
wallet_cli->register_api( wapi );
if (!options.count("daemon")) {
wallet_cli->register_api(wapi);
wallet_cli->start();
wallet_cli->wait();
}
else
{
} else {
fc::promise<int>::ptr exit_promise = new fc::promise<int>("UNIX Signal Handler");
fc::set_signal_handler([&exit_promise](int signal) {
exit_promise->set_value(signal);
}, SIGINT);
},
SIGINT);
ilog( "Entering Daemon Mode, ^C to exit" );
ilog("Entering Daemon Mode, ^C to exit");
exit_promise->wait();
}
wapi->save_wallet_file(wallet_file.generic_string());
locked_connection.disconnect();
closed_connection.disconnect();
}
catch ( const fc::exception& e )
{
} catch (const fc::exception &e) {
std::cout << e.to_detail_string() << "\n";
return -1;
}

View file

@ -27,7 +27,7 @@
#include <fc/crypto/digest.hpp>
#include <boost/test/auto_unit_test.hpp>
#include <boost/test/unit_test.hpp>
using namespace graphene::chain;

View file

@ -60,8 +60,14 @@ public:
create_tx = fixture_.con.wallet_api_ptr->create_account_with_brain_key(
bki.brain_priv_key, account_name, "nathan", "nathan", true
);
fixture_.generate_block();
// save the private key for this new account in the wallet file
BOOST_CHECK(fixture_.con.wallet_api_ptr->import_key(account_name, bki.wif_priv_key));
fixture_.generate_block();
fixture_.con.wallet_api_ptr->save_wallet_file(fixture_.con.wallet_filename);
// attempt to give son account some CORE tokens
@ -244,6 +250,28 @@ BOOST_AUTO_TEST_CASE( son_voting )
son2_end_votes = son2_obj.total_votes;
BOOST_CHECK(son2_end_votes > son2_start_votes);
//! Get nathan account
const auto nathan_account_object = con.wallet_api_ptr->get_account("nathan");
//! Check son1account voters
auto voters_for_son1account = con.wallet_api_ptr->get_voters("son1account");
BOOST_REQUIRE(voters_for_son1account.voters_for_son);
BOOST_CHECK_EQUAL(voters_for_son1account.voters_for_son->voters.size(), 1);
BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account.voters_for_son->voters[0].instance, nathan_account_object.id.instance());
//! Check son2account voters
auto voters_for_son2account = con.wallet_api_ptr->get_voters("son2account");
BOOST_REQUIRE(voters_for_son2account.voters_for_son);
BOOST_CHECK_EQUAL(voters_for_son2account.voters_for_son->voters.size(), 1);
BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account.voters_for_son->voters[0].instance, nathan_account_object.id.instance());
//! Check votes of nathan
auto nathan_votes = con.wallet_api_ptr->get_votes("nathan");
BOOST_REQUIRE(nathan_votes.votes_for_sons);
BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->size(), 2);
BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->at(0).id.instance(), son1_obj.id.instance());
BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->at(1).id.instance(), son2_obj.id.instance());
// Withdraw vote for a son1account
BOOST_TEST_MESSAGE("Withdraw vote for a son1account");
vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", false, true);
@ -254,6 +282,17 @@ BOOST_AUTO_TEST_CASE( son_voting )
son1_end_votes = son1_obj.total_votes;
BOOST_CHECK(son1_end_votes == son1_start_votes);
//! Check son1account voters
voters_for_son1account = con.wallet_api_ptr->get_voters("son1account");
BOOST_REQUIRE(voters_for_son1account.voters_for_son);
BOOST_CHECK_EQUAL(voters_for_son1account.voters_for_son->voters.size(), 0);
//! Check votes of nathan
nathan_votes = con.wallet_api_ptr->get_votes("nathan");
BOOST_REQUIRE(nathan_votes.votes_for_sons);
BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->size(), 1);
BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->at(0).id.instance(), son2_obj.id.instance());
// Withdraw vote for a son2account
BOOST_TEST_MESSAGE("Withdraw vote for a son2account");
vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", false, true);
@ -264,6 +303,15 @@ BOOST_AUTO_TEST_CASE( son_voting )
son2_end_votes = son2_obj.total_votes;
BOOST_CHECK(son2_end_votes == son2_start_votes);
//! Check son2account voters
voters_for_son2account = con.wallet_api_ptr->get_voters("son2account");
BOOST_REQUIRE(voters_for_son2account.voters_for_son);
BOOST_CHECK_EQUAL(voters_for_son2account.voters_for_son->voters.size(), 0);
//! Check votes of nathan
nathan_votes = con.wallet_api_ptr->get_votes("nathan");
BOOST_CHECK(!nathan_votes.votes_for_sons.valid());
} catch( fc::exception& e ) {
BOOST_TEST_MESSAGE("SON cli wallet tests exception");
edump((e.to_detail_string()));

View file

@ -38,6 +38,10 @@ using namespace graphene::chain;
using namespace graphene::chain::test;
using namespace graphene::app;
const std::string g_es_url = "http://localhost:9200/";
const std::string g_es_index_prefix = "peerplays-";
const std::string g_es_ppobjects_prefix = "ppobjects-";
BOOST_FIXTURE_TEST_SUITE( elasticsearch_tests, database_fixture )
BOOST_AUTO_TEST_CASE(elasticsearch_account_history) {
@ -48,8 +52,8 @@ BOOST_AUTO_TEST_CASE(elasticsearch_account_history) {
graphene::utilities::ES es;
es.curl = curl;
es.elasticsearch_url = "http://localhost:9200/";
es.index_prefix = "peerplays-";
es.elasticsearch_url = g_es_url;
es.index_prefix = g_es_index_prefix;
//es.auth = "elastic:changeme";
// delete all first
@ -71,7 +75,7 @@ BOOST_AUTO_TEST_CASE(elasticsearch_account_history) {
//int account_create_op_id = operation::tag<account_create_operation>::value;
string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }";
es.endpoint = es.index_prefix + "*/data/_count";
es.endpoint = es.index_prefix + "*/_doc/_count";
es.query = query;
auto res = graphene::utilities::simpleQuery(es);
@ -79,7 +83,7 @@ BOOST_AUTO_TEST_CASE(elasticsearch_account_history) {
auto total = j["count"].as_string();
BOOST_CHECK_EQUAL(total, "5");
es.endpoint = es.index_prefix + "*/data/_search";
es.endpoint = es.index_prefix + "*/_doc/_search";
res = graphene::utilities::simpleQuery(es);
j = fc::json::from_string(res);
auto first_id = j["hits"]["hits"][size_t(0)]["_id"].as_string();
@ -91,7 +95,7 @@ BOOST_AUTO_TEST_CASE(elasticsearch_account_history) {
fc::usleep(fc::milliseconds(1000)); // index.refresh_interval
es.endpoint = es.index_prefix + "*/data/_count";
es.endpoint = es.index_prefix + "*/_doc/_count";
res = graphene::utilities::simpleQuery(es);
j = fc::json::from_string(res);
@ -114,9 +118,9 @@ BOOST_AUTO_TEST_CASE(elasticsearch_account_history) {
// check the visitor data
auto block_date = db.head_block_time();
std::string index_name = graphene::utilities::generateIndexName(block_date, "peerplays-");
std::string index_name = g_es_index_prefix + block_date.to_iso_string().substr( 0, 7 ); // yyyy-mm
es.endpoint = index_name + "/data/2.9.12"; // we know last op is a transfer of amount 300
es.endpoint = index_name + "/_doc/2.9.12"; // we know last op is a transfer of amount 300
res = graphene::utilities::getEndPoint(es);
j = fc::json::from_string(res);
auto last_transfer_amount = j["_source"]["operation_history"]["op_object"]["amount_"]["amount"].as_string();
@ -137,8 +141,8 @@ BOOST_AUTO_TEST_CASE(elasticsearch_objects) {
graphene::utilities::ES es;
es.curl = curl;
es.elasticsearch_url = "http://localhost:9200/";
es.index_prefix = "ppobjects-";
es.elasticsearch_url = g_es_url;
es.index_prefix = g_es_ppobjects_prefix;
//es.auth = "elastic:changeme";
// delete all first
@ -155,7 +159,7 @@ BOOST_AUTO_TEST_CASE(elasticsearch_objects) {
fc::usleep(fc::milliseconds(1000));
string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }";
es.endpoint = es.index_prefix + "*/data/_count";
es.endpoint = es.index_prefix + "*/_doc/_count";
es.query = query;
auto res = graphene::utilities::simpleQuery(es);
@ -163,14 +167,14 @@ BOOST_AUTO_TEST_CASE(elasticsearch_objects) {
auto total = j["count"].as_string();
BOOST_CHECK_EQUAL(total, "2");
es.endpoint = es.index_prefix + "asset/data/_search";
es.endpoint = es.index_prefix + "asset/_doc/_search";
res = graphene::utilities::simpleQuery(es);
j = fc::json::from_string(res);
auto first_id = j["hits"]["hits"][size_t(0)]["_source"]["symbol"].as_string();
BOOST_CHECK_EQUAL(first_id, "USD");
auto bitasset_data_id = j["hits"]["hits"][size_t(0)]["_source"]["bitasset_data_id"].as_string();
es.endpoint = es.index_prefix + "bitasset/data/_search";
es.endpoint = es.index_prefix + "bitasset/_doc/_search";
es.query = "{ \"query\" : { \"bool\": { \"must\" : [{ \"term\": { \"object_id\": \""+bitasset_data_id+"\"}}] } } }";
res = graphene::utilities::simpleQuery(es);
j = fc::json::from_string(res);
@ -192,11 +196,11 @@ BOOST_AUTO_TEST_CASE(elasticsearch_suite) {
graphene::utilities::ES es;
es.curl = curl;
es.elasticsearch_url = "http://localhost:9200/";
es.index_prefix = "peerplays-";
es.elasticsearch_url = g_es_url;
es.index_prefix = g_es_index_prefix;
auto delete_account_history = graphene::utilities::deleteAll(es);
fc::usleep(fc::milliseconds(1000));
es.index_prefix = "ppobjects-";
es.index_prefix = g_es_ppobjects_prefix;
auto delete_objects = graphene::utilities::deleteAll(es);
fc::usleep(fc::milliseconds(1000));
@ -218,8 +222,8 @@ BOOST_AUTO_TEST_CASE(elasticsearch_history_api) {
graphene::utilities::ES es;
es.curl = curl;
es.elasticsearch_url = "http://localhost:9200/";
es.index_prefix = "peerplays-";
es.elasticsearch_url = g_es_url;
es.index_prefix = g_es_index_prefix;
auto delete_account_history = graphene::utilities::deleteAll(es);

View file

@ -934,6 +934,125 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture )
BOOST_CHECK_EQUAL(db.head_block_time().sec_since_epoch() - past_time, 2);
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE( prevent_missconfiguration_blockchain_param, database_fixture )
{ try {
db.modify(db.get_global_properties(), [](global_property_object& p) {
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
});
// remeber global properties before we try to change
const global_property_object gpo = db.get_global_properties();
BOOST_TEST_MESSAGE( "Creating a proposal to try with changing some global parameters" );
{
proposal_create_operation cop = proposal_create_operation::committee_proposal(db.get_global_properties().parameters, db.head_block_time());
cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;
cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10;
committee_member_update_global_parameters_operation uop;
// this is allowed values and the proposal change should be visible after approving
*uop.new_parameters.extensions.value.son_heartbeat_frequency = 100;
*uop.new_parameters.extensions.value.son_deregister_time = 120;
*uop.new_parameters.extensions.value.son_down_time = 140;
*uop.new_parameters.extensions.value.son_pay_time = 160;
// this change should not be applied. Changing gpos_period_start is not allowed
*uop.new_parameters.extensions.value.gpos_period_start = 50;
// the following changes should not be applied. Changing the assets is not allowed
*uop.new_parameters.extensions.value.btc_asset = db.get_global_properties().parameters.hbd_asset();
*uop.new_parameters.extensions.value.hbd_asset = db.get_global_properties().parameters.hive_asset();
*uop.new_parameters.extensions.value.hive_asset = db.get_global_properties().parameters.btc_asset();
// this change should not be applied. Changing the son_acccount is not allowed
*uop.new_parameters.extensions.value.son_account = GRAPHENE_TEMP_ACCOUNT;
cop.proposed_ops.emplace_back(uop);
trx.operations.push_back(cop);
db.push_transaction(trx);
}
BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" );
{
proposal_update_operation uop;
uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;
uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(),
get_account("init2").get_id(), get_account("init3").get_id(),
get_account("init4").get_id(), get_account("init5").get_id(),
get_account("init6").get_id(), get_account("init7").get_id()};
trx.operations.push_back(uop);
sign( trx, init_account_priv_key );
db.push_transaction(trx);
BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(db));
}
BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" );
generate_blocks(proposal_id_type()(db).expiration_time + 5);
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
generate_block(); // get the maintenance skip slots out of the way*/
BOOST_CHECK_EQUAL( db.get_global_properties().parameters.son_heartbeat_frequency(), 100 );
BOOST_CHECK_EQUAL( db.get_global_properties().parameters.son_deregister_time(), 120 );
BOOST_CHECK_EQUAL( db.get_global_properties().parameters.son_down_time(), 140 );
BOOST_CHECK_EQUAL( db.get_global_properties().parameters.son_pay_time(), 160 );
BOOST_CHECK_NE( db.get_global_properties().parameters.gpos_period_start(), 50 );
BOOST_CHECK( db.get_global_properties().parameters.son_account() != GRAPHENE_TEMP_ACCOUNT );
BOOST_CHECK( gpo.parameters.hbd_asset() == db.get_global_properties().parameters.hbd_asset() );
BOOST_CHECK( gpo.parameters.hive_asset() == db.get_global_properties().parameters.hive_asset() );
BOOST_CHECK( gpo.parameters.btc_asset() == db.get_global_properties().parameters.btc_asset() );
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE( hardfork_son2_time, database_fixture )
{ try {
db.modify(db.get_global_properties(), [](global_property_object& p) {
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
});
generate_block();
// check that maximum_son_count are not yet updated on 7, it should
// be updated on HARDFORK_SON2_TIME
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), GRAPHENE_DEFAULT_MAX_SONS);
generate_blocks(HARDFORK_SON2_TIME);
// check that flags for assets are set after hardfork_son2_time
asset_object btc_asset = get_asset("BTC");
uint16_t check_flags{0};
check_flags |= asset_issuer_permission_flags::charge_market_fee | asset_issuer_permission_flags::override_authority;
uint16_t result = btc_asset.options.flags & check_flags;
BOOST_CHECK_EQUAL( result, check_flags);
// move on next maintenance interval and check that maximum_son_count is updated to 7
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
generate_block(); // get the maintenance skip slots out of the way*/
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), 7);
generate_blocks(HARDFORK_SON3_TIME);
// after this hardfork maximum son account should not reset the value
// on 7 after maintenance interval anymore. So change the global parameters
// and check the value after maintenance interval
db.modify(db.get_global_properties(), [](global_property_object& p) {
p.parameters.extensions.value.maximum_son_count = 13;
});
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
generate_block();
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), 13);
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture )
{
try

View file

@ -322,6 +322,18 @@ BOOST_AUTO_TEST_CASE(track_votes_witnesses_enabled)
auto witness1_object = db_api1.get_witness_by_account(witness1_id(db).name);
BOOST_CHECK_EQUAL(witness1_object->total_votes, 111);
//! Check witness1 voters
const auto voters_for_witness1 = db_api1.get_voters("witness1");
BOOST_REQUIRE(voters_for_witness1.voters_for_witness);
BOOST_CHECK_EQUAL(voters_for_witness1.voters_for_witness->voters.size(), 1);
BOOST_CHECK_EQUAL((uint32_t)voters_for_witness1.voters_for_witness->voters[0].instance, 18);
//! Check votes of account
const auto account_votes = db_api1.get_votes("1.2.18");
BOOST_REQUIRE(account_votes.votes_for_witnesses);
BOOST_CHECK_EQUAL(account_votes.votes_for_witnesses->size(), 1);
BOOST_CHECK_EQUAL(account_votes.votes_for_witnesses->at(0).id.instance(), witness1_object->id.instance());
} FC_LOG_AND_RETHROW()
}
@ -501,6 +513,17 @@ BOOST_AUTO_TEST_CASE(track_votes_committee_enabled)
auto committee1_object = db_api1.get_committee_member_by_account(committee1_id(db).name);
BOOST_CHECK_EQUAL(committee1_object->total_votes, 111);
//! Check committee1 voters
const auto voters_for_committee1 = db_api1.get_voters("committee1");
BOOST_REQUIRE(voters_for_committee1.voters_for_committee_member);
BOOST_CHECK_EQUAL(voters_for_committee1.voters_for_committee_member->voters.size(), 1);
BOOST_CHECK_EQUAL((uint32_t)voters_for_committee1.voters_for_committee_member->voters[0].instance, 18);
//! Check votes of account
const auto account_votes = db_api1.get_votes("1.2.18");
BOOST_REQUIRE(account_votes.votes_for_committee_members);
BOOST_CHECK_EQUAL(account_votes.votes_for_committee_members->back().id.instance(), committee1_object->id.instance());
} FC_LOG_AND_RETHROW()
}