Merge branch 'develop' into 'beatrice'

Merge develop to beatrice 2023-02

See merge request PBSA/peerplays!206
This commit is contained in:
serkixenos 2023-02-10 13:56:17 +00:00
commit 142cf5b903
62 changed files with 2414 additions and 891 deletions

View file

@ -1,5 +1,4 @@
FROM ubuntu:20.04 FROM ubuntu:20.04
MAINTAINER Peerplays Blockchain Standards Association
#=============================================================================== #===============================================================================
# Ubuntu setup # Ubuntu setup
@ -51,69 +50,102 @@ RUN echo 'peerplays:peerplays' | chpasswd
# SSH # SSH
EXPOSE 22 EXPOSE 22
WORKDIR /home/peerplays/src
#=============================================================================== #===============================================================================
# Boost setup # Boost setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz && \ wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz && \
tar -xzvf boost_1_72_0.tar.gz boost_1_72_0 && \ tar -xzf boost_1_72_0.tar.gz && \
cd boost_1_72_0/ && \ cd boost_1_72_0 && \
./bootstrap.sh && \ ./bootstrap.sh && \
./b2 install ./b2 install && \
ldconfig && \
rm -rf /home/peerplays/src/*
#=============================================================================== #===============================================================================
# cmake setup # cmake setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
wget -c 'https://cmake.org/files/v3.23/cmake-3.23.1-linux-x86_64.sh' -O cmake-3.23.1-linux-x86_64.sh && \ wget https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2-linux-x86_64.sh && \
chmod 755 ./cmake-3.23.1-linux-x86_64.sh && \ chmod 755 ./cmake-3.24.2-linux-x86_64.sh && \
./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license && \ ./cmake-3.24.2-linux-x86_64.sh --prefix=/usr --skip-license && \
cmake --version cmake --version && \
rm -rf /home/peerplays/src/*
#=============================================================================== #===============================================================================
# libzmq setup # libzmq setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip && \ wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.tar.gz && \
unzip v4.3.4.zip && \ tar -xzvf v4.3.4.tar.gz && \
cd libzmq-4.3.4 && \ cd libzmq-4.3.4 && \
mkdir build && \ mkdir build && \
cd build && \ cd build && \
cmake .. && \ cmake .. && \
make -j$(nproc) install && \ make -j$(nproc) && \
ldconfig make install && \
ldconfig && \
rm -rf /home/peerplays/src/*
#=============================================================================== #===============================================================================
# cppzmq setup # cppzmq setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip && \ wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.9.0.tar.gz && \
unzip v4.8.1.zip && \ tar -xzvf v4.9.0.tar.gz && \
cd cppzmq-4.8.1 && \ cd cppzmq-4.9.0 && \
mkdir build && \ mkdir build && \
cd build && \ cd build && \
cmake .. && \ cmake .. && \
make -j$(nproc) install && \ make -j$(nproc) && \
ldconfig make install && \
ldconfig && \
rm -rf /home/peerplays/src/*
#===============================================================================
# gsl setup
#===============================================================================
RUN \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
libpcre3-dev
RUN \
wget https://github.com/imatix/gsl/archive/refs/tags/v4.1.4.tar.gz && \
tar -xzvf v4.1.4.tar.gz && \
cd gsl-4.1.4 && \
make -j$(nproc) && \
make install && \
rm -rf /home/peerplays/src/*
#===============================================================================
# libbitcoin-build setup
# libbitcoin-explorer setup
#===============================================================================
RUN \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
libsodium-dev
RUN \
git clone https://github.com/libbitcoin/libbitcoin-build.git && \
cd libbitcoin-build && \
./generate3.sh && \
cd ../libbitcoin-explorer && \
./install.sh && \
ldconfig && \
rm -rf /home/peerplays/src/*
#=============================================================================== #===============================================================================
# Doxygen setup # Doxygen setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
sudo apt install -y bison flex && \ sudo apt install -y bison flex && \
wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_8_17.tar.gz && \ wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_8_17.tar.gz && \
@ -129,8 +161,6 @@ RUN \
# Perl setup # Perl setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
wget https://github.com/Perl/perl5/archive/refs/tags/v5.30.0.tar.gz && \ wget https://github.com/Perl/perl5/archive/refs/tags/v5.30.0.tar.gz && \
tar -xvf v5.30.0.tar.gz && \ tar -xvf v5.30.0.tar.gz && \
@ -143,8 +173,6 @@ RUN \
# Peerplays setup # Peerplays setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
## Clone Peerplays ## Clone Peerplays
#RUN \ #RUN \
# git clone https://gitlab.com/PBSA/peerplays.git && \ # git clone https://gitlab.com/PBSA/peerplays.git && \
@ -176,8 +204,8 @@ WORKDIR /home/peerplays/peerplays-network
# Setup Peerplays runimage # Setup Peerplays runimage
RUN \ RUN \
ln -s /home/peerplays/peerplays/build/programs/cli_wallet/cli_wallet ./ && \ ln -s /home/peerplays/src/peerplays/build/programs/cli_wallet/cli_wallet ./ && \
ln -s /home/peerplays/peerplays/build/programs/witness_node/witness_node ./ ln -s /home/peerplays/src/peerplays/build/programs/witness_node/witness_node ./
RUN ./witness_node --create-genesis-json genesis.json && \ RUN ./witness_node --create-genesis-json genesis.json && \
rm genesis.json rm genesis.json

View file

@ -1,5 +1,4 @@
FROM ubuntu:18.04 FROM ubuntu:18.04
MAINTAINER Peerplays Blockchain Standards Association
#=============================================================================== #===============================================================================
# Ubuntu setup # Ubuntu setup
@ -51,69 +50,102 @@ RUN echo 'peerplays:peerplays' | chpasswd
# SSH # SSH
EXPOSE 22 EXPOSE 22
WORKDIR /home/peerplays/src
#=============================================================================== #===============================================================================
# Boost setup # Boost setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz && \ wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz && \
tar -xzvf boost_1_72_0.tar.gz boost_1_72_0 && \ tar -xzf boost_1_72_0.tar.gz && \
cd boost_1_72_0/ && \ cd boost_1_72_0 && \
./bootstrap.sh && \ ./bootstrap.sh && \
./b2 install ./b2 install && \
ldconfig && \
rm -rf /home/peerplays/src/*
#=============================================================================== #===============================================================================
# cmake setup # cmake setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
wget -c 'https://cmake.org/files/v3.23/cmake-3.23.1-linux-x86_64.sh' -O cmake-3.23.1-linux-x86_64.sh && \ wget https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2-linux-x86_64.sh && \
chmod 755 ./cmake-3.23.1-linux-x86_64.sh && \ chmod 755 ./cmake-3.24.2-linux-x86_64.sh && \
./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license && \ ./cmake-3.24.2-linux-x86_64.sh --prefix=/usr --skip-license && \
cmake --version cmake --version && \
rm -rf /home/peerplays/src/*
#=============================================================================== #===============================================================================
# libzmq setup # libzmq setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip && \ wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.tar.gz && \
unzip v4.3.4.zip && \ tar -xzvf v4.3.4.tar.gz && \
cd libzmq-4.3.4 && \ cd libzmq-4.3.4 && \
mkdir build && \ mkdir build && \
cd build && \ cd build && \
cmake .. && \ cmake .. && \
make -j$(nproc) install && \ make -j$(nproc) && \
ldconfig make install && \
ldconfig && \
rm -rf /home/peerplays/src/*
#=============================================================================== #===============================================================================
# cppzmq setup # cppzmq setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip && \ wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.9.0.tar.gz && \
unzip v4.8.1.zip && \ tar -xzvf v4.9.0.tar.gz && \
cd cppzmq-4.8.1 && \ cd cppzmq-4.9.0 && \
mkdir build && \ mkdir build && \
cd build && \ cd build && \
cmake .. && \ cmake .. && \
make -j$(nproc) install && \ make -j$(nproc) && \
ldconfig make install && \
ldconfig && \
rm -rf /home/peerplays/src/*
#===============================================================================
# gsl setup
#===============================================================================
RUN \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
libpcre3-dev
RUN \
wget https://github.com/imatix/gsl/archive/refs/tags/v4.1.4.tar.gz && \
tar -xzvf v4.1.4.tar.gz && \
cd gsl-4.1.4 && \
make -j$(nproc) && \
make install && \
rm -rf /home/peerplays/src/*
#===============================================================================
# libbitcoin-build setup
# libbitcoin-explorer setup
#===============================================================================
RUN \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
libsodium-dev
RUN \
git clone https://github.com/libbitcoin/libbitcoin-build.git && \
cd libbitcoin-build && \
./generate3.sh && \
cd ../libbitcoin-explorer && \
./install.sh && \
ldconfig && \
rm -rf /home/peerplays/src/*
#=============================================================================== #===============================================================================
# Doxygen setup # Doxygen setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
sudo apt install -y bison flex && \ sudo apt install -y bison flex && \
wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_8_17.tar.gz && \ wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_8_17.tar.gz && \
@ -129,8 +161,6 @@ RUN \
# Perl setup # Perl setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
RUN \ RUN \
wget https://github.com/Perl/perl5/archive/refs/tags/v5.30.0.tar.gz && \ wget https://github.com/Perl/perl5/archive/refs/tags/v5.30.0.tar.gz && \
tar -xvf v5.30.0.tar.gz && \ tar -xvf v5.30.0.tar.gz && \
@ -143,8 +173,6 @@ RUN \
# Peerplays setup # Peerplays setup
#=============================================================================== #===============================================================================
WORKDIR /home/peerplays/
## Clone Peerplays ## Clone Peerplays
#RUN \ #RUN \
# git clone https://gitlab.com/PBSA/peerplays.git && \ # git clone https://gitlab.com/PBSA/peerplays.git && \
@ -176,8 +204,8 @@ WORKDIR /home/peerplays/peerplays-network
# Setup Peerplays runimage # Setup Peerplays runimage
RUN \ RUN \
ln -s /home/peerplays/peerplays/build/programs/cli_wallet/cli_wallet ./ && \ ln -s /home/peerplays/src/peerplays/build/programs/cli_wallet/cli_wallet ./ && \
ln -s /home/peerplays/peerplays/build/programs/witness_node/witness_node ./ ln -s /home/peerplays/src/peerplays/build/programs/witness_node/witness_node ./
RUN ./witness_node --create-genesis-json genesis.json && \ RUN ./witness_node --create-genesis-json genesis.json && \
rm genesis.json rm genesis.json

152
README.md
View file

@ -8,100 +8,41 @@ This is a quick introduction to get new developers and witnesses up to speed on
Officially supported OS are Ubuntu 20.04 and Ubuntu 18.04. Officially supported OS are Ubuntu 20.04 and Ubuntu 18.04.
## Ubuntu 20.04 ## Ubuntu 20.04 and 18.04
Following dependencies are needed for a clean install of Ubuntu 20.04: Following dependencies are needed for a clean install of Ubuntu 20.04 and Ubuntu 18.04:
``` ```
sudo apt-get install \ sudo apt-get install \
apt-utils autoconf bash build-essential ca-certificates clang-format cmake \ autoconf bash bison build-essential ca-certificates dnsutils expect flex git \
dnsutils doxygen expect git graphviz libboost-all-dev libbz2-dev \ graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev libpcre3-dev \
libcurl4-openssl-dev libncurses-dev libsnappy-dev \ libsnappy-dev libsodium-dev libssl-dev libtool libzip-dev locales lsb-release \
libssl-dev libtool libzip-dev locales lsb-release mc nano net-tools ntp \ mc nano net-tools ntp openssh-server pkg-config python3 python3-jinja2 sudo \
openssh-server pkg-config perl python3 python3-jinja2 sudo \
systemd-coredump wget systemd-coredump wget
``` ```
Install libzmq from source: Boost libraries setup:
``` ```
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz
unzip v4.3.4.zip tar -xzf boost_1_72_0.tar.gz boost_1_72_0
cd libzmq-4.3.4 cd boost_1_72_0
mkdir build
cd build
cmake ..
make -j$(nproc)
sudo make install
sudo ldconfig
```
Install cppzmq from source:
```
wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip
unzip v4.8.1.zip
cd cppzmq-4.8.1
mkdir build
cd build
cmake ..
make -j$(nproc)
sudo make install
sudo ldconfig
```
Building Peerplays
```
git clone https://gitlab.com/PBSA/peerplays.git
cd peerplays
git submodule update --init --recursive
# If you want to build Mainnet node
cmake -DCMAKE_BUILD_TYPE=Release
# If you want to build Testnet node
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)
sudo make install # this can install the executable files under /usr/local
```
## Ubuntu 18.04
Following dependencies are needed for a clean install of Ubuntu 18.04:
```
sudo apt-get install \
apt-utils autoconf bash build-essential ca-certificates clang-format \
dnsutils doxygen expect git graphviz libbz2-dev \
libcurl4-openssl-dev libncurses-dev libsnappy-dev \
libssl-dev libtool libzip-dev locales lsb-release mc nano net-tools ntp \
openssh-server pkg-config perl python3 python3-jinja2 sudo \
systemd-coredump wget
```
Install Boost libraries from source
```
wget -c 'https://boostorg.jfrog.io/artifactory/main/release/1.71.0/source/boost_1_71_0.tar.bz2' -O boost_1_71_0.tar.bz2
tar xjf boost_1_71_0.tar.bz2
cd boost_1_71_0/
./bootstrap.sh ./bootstrap.sh
./b2
sudo ./b2 install sudo ./b2 install
sudo ldconfig
``` ```
Install cmake cmake setup:
``` ```
wget -c 'https://cmake.org/files/v3.23/cmake-3.23.1-linux-x86_64.sh' -O cmake-3.23.1-linux-x86_64.sh wget https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2-linux-x86_64.sh
chmod 755 ./cmake-3.23.1-linux-x86_64.sh chmod 755 ./cmake-3.24.2-linux-x86_64.sh
sudo ./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license sudo ./cmake-3.24.2-linux-x86_64.sh --prefix=/usr --skip-license
cmake --version
``` ```
Install libzmq from source: libzmq setup:
``` ```
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.tar.gz
unzip v4.3.4.zip tar -xzvf v4.3.4.tar.gz
cd libzmq-4.3.4 cd libzmq-4.3.4
mkdir build mkdir build
cd build cd build
@ -111,11 +52,11 @@ sudo make install
sudo ldconfig sudo ldconfig
``` ```
Install cppzmq from source: cppzmq setup:
``` ```
wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.9.0.tar.gz
unzip v4.8.1.zip tar -xzvf v4.9.0.tar.gz
cd cppzmq-4.8.1 cd cppzmq-4.9.0
mkdir build mkdir build
cd build cd build
cmake .. cmake ..
@ -124,6 +65,50 @@ sudo make install
sudo ldconfig sudo ldconfig
``` ```
gsl setup:
```
wget https://github.com/imatix/gsl/archive/refs/tags/v4.1.4.tar.gz
tar -xzvf v4.1.4.tar.gz
cd gsl-4.1.4
make -j$(nproc)
sudo make install
sudo ldconfig
```
libbitcoin-explorer setup:
```
git clone https://github.com/libbitcoin/libbitcoin-build.git
cd libbitcoin-build
./generate3.sh
cd ../libbitcoin-explorer
sudo ./install.sh
sudo ldconfig
```
Doxygen setup:
```
wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_8_17.tar.gz
tar -xvf Release_1_8_17.tar.gz
cd doxygen-Release_1_8_17
mkdir build
cd build
cmake ..
make -j$(nproc)
sudo make install
sudo ldconfig
```
Perl setup:
```
wget https://github.com/Perl/perl5/archive/refs/tags/v5.30.0.tar.gz
tar -xvf v5.30.0.tar.gz
cd perl5-5.30.0
./Configure -des
make -j$(nproc)
sudo make install
sudo ldconfig
```
Building Peerplays Building Peerplays
``` ```
git clone https://gitlab.com/PBSA/peerplays.git git clone https://gitlab.com/PBSA/peerplays.git
@ -146,7 +131,6 @@ make -j$(nproc)
sudo make install # this can install the executable files under /usr/local sudo make install # this can install the executable files under /usr/local
``` ```
## Docker images ## Docker images
Install docker, and add current user to docker group. Install docker, and add current user to docker group.

View file

@ -196,8 +196,8 @@ public:
fc::optional<son_object> get_son_by_account(const std::string account_id_or_name) 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; map<string, son_id_type> lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const;
uint64_t get_son_count() const; uint64_t get_son_count() const;
flat_map<sidechain_type, vector<son_info>> get_active_sons(); flat_map<sidechain_type, vector<son_sidechain_info>> get_active_sons();
vector<son_info> get_active_sons_by_sidechain(sidechain_type sidechain); vector<son_sidechain_info> get_active_sons_by_sidechain(sidechain_type sidechain);
map<sidechain_type, map<son_id_type, string>> get_son_network_status(); map<sidechain_type, map<son_id_type, string>> get_son_network_status();
map<son_id_type, string> get_son_network_status_by_sidechain(sidechain_type sidechain); map<son_id_type, string> get_son_network_status_by_sidechain(sidechain_type sidechain);
@ -1877,22 +1877,22 @@ uint64_t database_api_impl::get_son_count() const {
return _db.get_index_type<son_index>().indices().size(); return _db.get_index_type<son_index>().indices().size();
} }
flat_map<sidechain_type, vector<son_info>> database_api::get_active_sons() { flat_map<sidechain_type, vector<son_sidechain_info>> database_api::get_active_sons() {
return my->get_active_sons(); return my->get_active_sons();
} }
flat_map<sidechain_type, vector<son_info>> database_api_impl::get_active_sons() { flat_map<sidechain_type, vector<son_sidechain_info>> database_api_impl::get_active_sons() {
return get_global_properties().active_sons; return get_global_properties().active_sons;
} }
vector<son_info> database_api::get_active_sons_by_sidechain(sidechain_type sidechain) { vector<son_sidechain_info> database_api::get_active_sons_by_sidechain(sidechain_type sidechain) {
return my->get_active_sons_by_sidechain(sidechain); return my->get_active_sons_by_sidechain(sidechain);
} }
vector<son_info> database_api_impl::get_active_sons_by_sidechain(sidechain_type sidechain) { vector<son_sidechain_info> database_api_impl::get_active_sons_by_sidechain(sidechain_type sidechain) {
const global_property_object &gpo = get_global_properties(); const global_property_object &gpo = get_global_properties();
vector<son_info> result; vector<son_sidechain_info> result;
if (gpo.active_sons.find(sidechain) != gpo.active_sons.end()) { if (gpo.active_sons.find(sidechain) != gpo.active_sons.end()) {
result = gpo.active_sons.at(sidechain); result = gpo.active_sons.at(sidechain);
@ -1908,7 +1908,7 @@ map<sidechain_type, map<son_id_type, string>> database_api::get_son_network_stat
map<sidechain_type, map<son_id_type, string>> database_api_impl::get_son_network_status() { map<sidechain_type, map<son_id_type, string>> database_api_impl::get_son_network_status() {
map<sidechain_type, map<son_id_type, string>> result; map<sidechain_type, map<son_id_type, string>> result;
for (auto active_sidechain_type : active_sidechain_types) { for (auto active_sidechain_type : active_sidechain_types(_db.head_block_time())) {
result[active_sidechain_type] = get_son_network_status_by_sidechain(active_sidechain_type); result[active_sidechain_type] = get_son_network_status_by_sidechain(active_sidechain_type);
} }
@ -2340,7 +2340,9 @@ votes_info database_api_impl::get_votes(const string &account_name_or_id) const
votes_for_sons[sidechain].reserve(sidechain_ids.size()); votes_for_sons[sidechain].reserve(sidechain_ids.size());
for (const auto &son : sidechain_ids) { for (const auto &son : sidechain_ids) {
const auto &son_obj = son.as<son_object>(6); const auto &son_obj = son.as<son_object>(6);
votes_for_sons[sidechain].emplace_back(votes_info_object{son_obj.get_sidechain_vote_id(sidechain), son_obj.id}); if (son_obj.get_sidechain_vote_id(sidechain).valid()) {
votes_for_sons[sidechain].emplace_back(votes_info_object{*son_obj.get_sidechain_vote_id(sidechain), son_obj.id});
}
} }
} }
result.votes_for_sons = std::move(votes_for_sons); result.votes_for_sons = std::move(votes_for_sons);

View file

@ -695,14 +695,14 @@ public:
* @brief Get list of active sons * @brief Get list of active sons
* @return List of active SONs * @return List of active SONs
*/ */
flat_map<sidechain_type, vector<son_info>> get_active_sons(); flat_map<sidechain_type, vector<son_sidechain_info>> get_active_sons();
/** /**
* @brief Get list of active sons * @brief Get list of active sons
* @param sidechain Sidechain type [bitcoin|ethereum|hive] * @param sidechain Sidechain type [bitcoin|ethereum|hive]
* @return List of active SONs * @return List of active SONs
*/ */
vector<son_info> get_active_sons_by_sidechain(sidechain_type sidechain); vector<son_sidechain_info> get_active_sons_by_sidechain(sidechain_type sidechain);
/** /**
* @brief Get SON network status * @brief Get SON network status

View file

@ -69,12 +69,14 @@ void verify_account_votes( const database& db, const account_options& options )
FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count, FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count,
"Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) ); "Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) );
FC_ASSERT( chain_params.extensions.value.maximum_son_count.valid() , "Invalid maximum son count" ); FC_ASSERT( chain_params.extensions.value.maximum_son_count.valid() , "Invalid maximum son count" );
FC_ASSERT( options.extensions.value.num_son.valid() , "Invalid son number" ); if ( options.extensions.value.num_son.valid() )
{
for(const auto& num_sons : *options.extensions.value.num_son) for(const auto& num_sons : *options.extensions.value.num_son)
{ {
FC_ASSERT( num_sons.second <= *chain_params.extensions.value.maximum_son_count, FC_ASSERT( num_sons.second <= *chain_params.extensions.value.maximum_son_count,
"Voted for more sons than currently allowed (${c})", ("c", *chain_params.extensions.value.maximum_son_count) ); "Voted for more sons than currently allowed (${c})", ("c", *chain_params.extensions.value.maximum_son_count) );
} }
}
FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." ); FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." );
uint32_t max_vote_id = gpo.next_available_vote_id; uint32_t max_vote_id = gpo.next_available_vote_id;
@ -188,6 +190,12 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio
obj.owner = o.owner; obj.owner = o.owner;
obj.active = o.active; obj.active = o.active;
obj.options = o.options; obj.options = o.options;
if (!obj.options.extensions.value.num_son.valid())
{
obj.options.extensions.value = account_options::ext();
}
obj.statistics = d.create<account_statistics_object>([&obj](account_statistics_object& s){ obj.statistics = d.create<account_statistics_object>([&obj](account_statistics_object& s){
s.owner = obj.id; s.owner = obj.id;
s.name = obj.name; s.name = obj.name;

View file

@ -784,7 +784,7 @@ void database::_apply_block( const signed_block& next_block )
update_witness_schedule(); update_witness_schedule();
bool need_update_son_schedule = false; bool need_update_son_schedule = false;
for(const auto& active_sidechain_type : active_sidechain_types) { for(const auto& active_sidechain_type : active_sidechain_types(dynamic_global_props.time)) {
if(global_props.active_sons.at(active_sidechain_type).size() > 0) { if(global_props.active_sons.at(active_sidechain_type).size() > 0) {
need_update_son_schedule = true; need_update_son_schedule = true;
} }

View file

@ -342,7 +342,7 @@ bool database::is_son_active( sidechain_type type, son_id_type son_id )
active_son_ids.reserve(gpo_as.size()); active_son_ids.reserve(gpo_as.size());
std::transform(gpo_as.cbegin(), gpo_as.cend(), std::transform(gpo_as.cbegin(), gpo_as.cend(),
std::inserter(active_son_ids, active_son_ids.end()), std::inserter(active_son_ids, active_son_ids.end()),
[](const son_info& swi) { [](const son_sidechain_info& swi) {
return swi.son_id; return swi.son_id;
}); });

View file

@ -365,7 +365,7 @@ void database::initialize_hardforks()
_hardfork_times.emplace_back(HARDFORK_SON_FOR_HIVE_TIME); _hardfork_times.emplace_back(HARDFORK_SON_FOR_HIVE_TIME);
_hardfork_times.emplace_back(HARDFORK_SON_TIME); _hardfork_times.emplace_back(HARDFORK_SON_TIME);
_hardfork_times.emplace_back(HARDFORK_SON2_TIME); _hardfork_times.emplace_back(HARDFORK_SON2_TIME);
_hardfork_times.emplace_back(HARDFORK_SON3_TIME); _hardfork_times.emplace_back(HARDFORK_SON_FOR_ETHEREUM_TIME);
_hardfork_times.emplace_back(HARDFORK_SWEEPS_TIME); _hardfork_times.emplace_back(HARDFORK_SWEEPS_TIME);
std::sort(_hardfork_times.begin(), _hardfork_times.end()); std::sort(_hardfork_times.begin(), _hardfork_times.end());
@ -1168,6 +1168,11 @@ void database::init_genesis(const genesis_state_type& genesis_state)
}); });
assert( ssohive.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::hive)) ); assert( ssohive.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::hive)) );
// Enable fees
modify(get_global_properties(), [&genesis_state](global_property_object& p) {
p.parameters.current_fees = genesis_state.initial_parameters.current_fees;
});
// Create FBA counters // Create FBA counters
create<fba_accumulator_object>([&]( fba_accumulator_object& acc ) create<fba_accumulator_object>([&]( fba_accumulator_object& acc )
{ {

View file

@ -84,7 +84,7 @@ vector<std::reference_wrapper<const son_object>> database::sort_votable_objects<
std::vector<std::reference_wrapper<const son_object>> refs; std::vector<std::reference_wrapper<const son_object>> refs;
for( auto& son : all_sons ) for( auto& son : all_sons )
{ {
if(son.has_valid_config(head_block_time()) && son.statuses.at(sidechain) != son_status::deregistered) if(son.has_valid_config(head_block_time(), sidechain) && son.statuses.at(sidechain) != son_status::deregistered)
{ {
refs.push_back(std::cref(son)); refs.push_back(std::cref(son));
} }
@ -97,8 +97,10 @@ vector<std::reference_wrapper<const son_object>> database::sort_votable_objects<
sidechain == sidechain_type::hive, sidechain == sidechain_type::hive,
"Unexpected sidechain type"); "Unexpected sidechain type");
const share_type oa_vote = _vote_tally_buffer[a.get_sidechain_vote_id(sidechain)]; FC_ASSERT(a.get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", a));
const share_type ob_vote = _vote_tally_buffer[b.get_sidechain_vote_id(sidechain)]; FC_ASSERT(b.get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", b));
const share_type oa_vote = _vote_tally_buffer.size() > *a.get_sidechain_vote_id(sidechain) ? _vote_tally_buffer[*a.get_sidechain_vote_id(sidechain)] : 0;
const share_type ob_vote = _vote_tally_buffer.size() > *b.get_sidechain_vote_id(sidechain) ? _vote_tally_buffer[*b.get_sidechain_vote_id(sidechain)] : 0;
if( oa_vote != ob_vote ) if( oa_vote != ob_vote )
return oa_vote > ob_vote; return oa_vote > ob_vote;
@ -190,7 +192,7 @@ void database::pay_sons()
// Current requirement is that we have to pay every 24 hours, so the following check // Current requirement is that we have to pay every 24 hours, so the following check
if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time())))
{ {
for(const auto& active_sidechain_type : active_sidechain_types) for(const auto& active_sidechain_type : active_sidechain_types(now))
{ {
assert( _son_count_histogram_buffer.at(active_sidechain_type).size() > 0 ); assert( _son_count_histogram_buffer.at(active_sidechain_type).size() > 0 );
const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer.at(active_sidechain_type)[0]) / 2; const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer.at(active_sidechain_type)[0]) / 2;
@ -207,7 +209,14 @@ void database::pay_sons()
} }
} }
const auto sons = sort_votable_objects<son_index>(active_sidechain_type, const sidechain_type st = [&now, &active_sidechain_type]{
if( now < HARDFORK_SON_FOR_ETHEREUM_TIME )
return sidechain_type::bitcoin;
else
return active_sidechain_type;
}();
const auto sons = sort_votable_objects<son_index>(st,
(std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count)) (std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count))
); );
@ -215,7 +224,8 @@ void database::pay_sons()
uint64_t total_votes = 0; uint64_t total_votes = 0;
for( const son_object& son : sons ) for( const son_object& son : sons )
{ {
total_votes += _vote_tally_buffer[son.sidechain_vote_ids.at(active_sidechain_type)]; FC_ASSERT(son.get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", son));
total_votes += _vote_tally_buffer[*son.get_sidechain_vote_id(st)];
} }
const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
auto get_weight = [&bits_to_drop]( uint64_t son_votes ) { auto get_weight = [&bits_to_drop]( uint64_t son_votes ) {
@ -230,35 +240,37 @@ void database::pay_sons()
}; };
uint64_t weighted_total_txs_signed = 0; uint64_t weighted_total_txs_signed = 0;
const share_type son_budget = dpo.son_budget; const share_type son_budget = dpo.son_budget;
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf, &active_sidechain_type](const object& o) { get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf, &active_sidechain_type, &st](const object& o) {
const son_statistics_object& s = static_cast<const son_statistics_object&>(o); const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
const auto& idx = get_index_type<son_index>().indices().get<by_id>(); const auto& idx = get_index_type<son_index>().indices().get<by_id>();
const auto son_obj = idx.find( s.owner ); const auto son_obj = idx.find( s.owner );
uint16_t son_weight = 0; uint16_t son_weight = 0;
FC_ASSERT(son_obj->get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", *son_obj));
if( now >= HARDFORK_SON2_TIME ) { if( now >= HARDFORK_SON2_TIME ) {
son_weight += get_weight(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); son_weight += get_weight(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]);
} }
else { else {
son_weight += get_weight_before_son2_hf(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); son_weight += get_weight_before_son2_hf(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]);
} }
const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0; const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0;
weighted_total_txs_signed += (txs_signed * son_weight); weighted_total_txs_signed += (txs_signed * son_weight);
}); });
// Now pay off each SON proportional to the number of transactions signed. // Now pay off each SON proportional to the number of transactions signed.
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now, &active_sidechain_type](const object& o) { get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now, &active_sidechain_type, &st](const object& o) {
const son_statistics_object& s = static_cast<const son_statistics_object&>(o); const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0; const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0;
if(txs_signed > 0){ if(txs_signed > 0) {
const auto& idx = get_index_type<son_index>().indices().get<by_id>(); const auto& idx = get_index_type<son_index>().indices().get<by_id>();
auto son_obj = idx.find( s.owner ); auto son_obj = idx.find( s.owner );
uint16_t son_weight = 0; uint16_t son_weight = 0;
FC_ASSERT(son_obj->get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", *son_obj));
if( now >= HARDFORK_SON2_TIME ) { if( now >= HARDFORK_SON2_TIME ) {
son_weight += get_weight(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); son_weight += get_weight(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]);
} }
else { else {
son_weight += get_weight_before_son2_hf(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); son_weight += get_weight_before_son2_hf(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]);
} }
const share_type pay = (txs_signed * son_weight * son_budget.value)/weighted_total_txs_signed; const share_type pay = (txs_signed * son_weight * son_budget.value)/weighted_total_txs_signed;
modify( *son_obj, [&]( son_object& _son_obj) modify( *son_obj, [&]( son_object& _son_obj)
@ -287,7 +299,7 @@ void database::pay_sons()
} }
} }
void database::update_son_metrics(const flat_map<sidechain_type, vector<son_info> >& curr_active_sons) void database::update_son_metrics(const flat_map<sidechain_type, vector<son_sidechain_info> >& curr_active_sons)
{ {
for(const auto& curr_active_sidechain_sons : curr_active_sons) { for(const auto& curr_active_sidechain_sons : curr_active_sons) {
const auto& sidechain = curr_active_sidechain_sons.first; const auto& sidechain = curr_active_sidechain_sons.first;
@ -298,7 +310,7 @@ void database::update_son_metrics(const flat_map<sidechain_type, vector<son_info
current_sons.reserve(_curr_active_sidechain_sons.size()); current_sons.reserve(_curr_active_sidechain_sons.size());
std::transform(_curr_active_sidechain_sons.cbegin(), _curr_active_sidechain_sons.cend(), std::transform(_curr_active_sidechain_sons.cbegin(), _curr_active_sidechain_sons.cend(),
std::inserter(current_sons, current_sons.end()), std::inserter(current_sons, current_sons.end()),
[](const son_info &swi) { [](const son_sidechain_info &swi) {
return swi.son_id; return swi.son_id;
}); });
@ -321,8 +333,8 @@ void database::update_son_metrics(const flat_map<sidechain_type, vector<son_info
} }
} }
void database::update_son_statuses( const flat_map<sidechain_type, vector<son_info> >& curr_active_sons, void database::update_son_statuses( const flat_map<sidechain_type, vector<son_sidechain_info> >& curr_active_sons,
const flat_map<sidechain_type, vector<son_info> >& new_active_sons ) const flat_map<sidechain_type, vector<son_sidechain_info> >& new_active_sons )
{ {
for(const auto& new_active_sidechain_sons : new_active_sons) { for(const auto& new_active_sidechain_sons : new_active_sons) {
const auto& sidechain = new_active_sidechain_sons.first; const auto& sidechain = new_active_sidechain_sons.first;
@ -335,7 +347,7 @@ void database::update_son_statuses( const flat_map<sidechain_type, vector<son_in
current_sons.reserve(curr_active_sons.at(sidechain).size()); current_sons.reserve(curr_active_sons.at(sidechain).size());
std::transform(curr_active_sons.at(sidechain).cbegin(), curr_active_sons.at(sidechain).cend(), std::transform(curr_active_sons.at(sidechain).cbegin(), curr_active_sons.at(sidechain).cend(),
std::inserter(current_sons, current_sons.end()), std::inserter(current_sons, current_sons.end()),
[](const son_info &swi) { [](const son_sidechain_info &swi) {
return swi.son_id; return swi.son_id;
}); });
} }
@ -343,7 +355,7 @@ void database::update_son_statuses( const flat_map<sidechain_type, vector<son_in
new_sons.reserve(new_active_sons.at(sidechain).size()); new_sons.reserve(new_active_sons.at(sidechain).size());
std::transform(new_active_sons.at(sidechain).cbegin(), new_active_sons.at(sidechain).cend(), std::transform(new_active_sons.at(sidechain).cbegin(), new_active_sons.at(sidechain).cend(),
std::inserter(new_sons, new_sons.end()), std::inserter(new_sons, new_sons.end()),
[](const son_info &swi) { [](const son_sidechain_info &swi) {
return swi.son_id; return swi.son_id;
}); });
@ -379,7 +391,7 @@ void database::update_son_statuses( const flat_map<sidechain_type, vector<son_in
for (const auto &sid : sons_to_add) { for (const auto &sid : sons_to_add) {
auto son = idx.find(sid); auto son = idx.find(sid);
FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", sid)); FC_ASSERT(son != idx.end(), "Invalid SON in active list, id = ${sonid}.", ("sonid", sid));
// keep maintenance status for new nodes // keep maintenance status for new nodes
if (son->statuses.at(sidechain) == son_status::inactive) { if (son->statuses.at(sidechain) == son_status::inactive) {
modify(*son, [&](son_object &obj) { modify(*son, [&](son_object &obj) {
@ -414,7 +426,7 @@ void database::update_son_statuses( const flat_map<sidechain_type, vector<son_in
} }
} }
void database::update_son_wallet(const flat_map<sidechain_type, vector<son_info> >& new_active_sons) void database::update_son_wallet(const flat_map<sidechain_type, vector<son_sidechain_info> >& new_active_sons)
{ {
bool should_recreate_pw = true; bool should_recreate_pw = true;
@ -729,39 +741,22 @@ void database::update_active_sons()
} }
#endif #endif
const flat_map<sidechain_type, share_type> stake_target = [this]{ const auto supported_active_sidechain_types = active_sidechain_types(head_block_time());
flat_map<sidechain_type, share_type> stake_target; flat_map<sidechain_type, size_t> son_count;
for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){ for(const auto& active_sidechain_type : supported_active_sidechain_types)
const auto sidechain = son_count_histogram_buffer.first; {
stake_target[sidechain] = (_total_voting_stake-son_count_histogram_buffer.second[0]) / 2; const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer.at(active_sidechain_type)[0]) / 2;
}
return stake_target;
}();
/// accounts that vote for 0 or 1 son do not get to express an opinion on /// accounts that vote for 0 or 1 son do not get to express an opinion on
/// the number of sons to have (they abstain and are non-voting accounts) /// the number of sons to have (they abstain and are non-voting accounts)
flat_map<sidechain_type, share_type> stake_tally = []{ share_type stake_tally = 0;
flat_map<sidechain_type, share_type> stake_tally;
for(const auto& active_sidechain_type : active_sidechain_types){
stake_tally[active_sidechain_type] = 0;
}
return stake_tally;
}();
flat_map<sidechain_type, size_t> son_count = []{
flat_map<sidechain_type, size_t> son_count;
for(const auto& active_sidechain_type : active_sidechain_types){
son_count[active_sidechain_type] = 0; son_count[active_sidechain_type] = 0;
} if( stake_target > 0 )
return son_count;
}();
for( const auto& stake_target_sidechain : stake_target ){
const auto sidechain = stake_target_sidechain.first;
if( stake_target_sidechain.second > 0 )
{ {
while( (son_count[sidechain] < _son_count_histogram_buffer.at(sidechain).size() - 1) while( (son_count.at(active_sidechain_type) < _son_count_histogram_buffer.at(active_sidechain_type).size() - 1)
&& (stake_tally[sidechain] <= stake_target_sidechain.second) ) && (stake_tally <= stake_target) )
{ {
stake_tally[sidechain] += _son_count_histogram_buffer.at(sidechain)[ ++son_count[sidechain] ]; stake_tally += _son_count_histogram_buffer.at(active_sidechain_type)[ ++son_count[active_sidechain_type] ];
} }
} }
} }
@ -770,18 +765,17 @@ void database::update_active_sons()
const chain_property_object& cpo = get_chain_properties(); const chain_property_object& cpo = get_chain_properties();
const auto& all_sons = get_index_type<son_index>().indices(); const auto& all_sons = get_index_type<son_index>().indices();
flat_map<sidechain_type, vector<std::reference_wrapper<const son_object> > > sons; flat_map<sidechain_type, vector<std::reference_wrapper<const son_object> > > sons;
for(const auto& active_sidechain_type : active_sidechain_types) for(const auto& active_sidechain_type : supported_active_sidechain_types)
{ {
if(head_block_time() >= HARDFORK_SON3_TIME) { if(head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) {
sons[active_sidechain_type] = sort_votable_objects<son_index>(active_sidechain_type, sons[active_sidechain_type] = sort_votable_objects<son_index>(active_sidechain_type,
(std::max(son_count.at(active_sidechain_type) * 2 + 1, (size_t)cpo.immutable_parameters.min_son_count))); (std::max(son_count.at(active_sidechain_type) * 2 + 1, (size_t)cpo.immutable_parameters.min_son_count)));
} }
else { else {
sons[active_sidechain_type] = sort_votable_objects<son_index>(active_sidechain_type, get_global_properties().parameters.maximum_son_count()); sons[active_sidechain_type] = sort_votable_objects<son_index>(sidechain_type::bitcoin, get_global_properties().parameters.maximum_son_count());
} }
} }
auto& local_vote_buffer_ref = _vote_tally_buffer;
for( const son_object& son : all_sons ) for( const son_object& son : all_sons )
{ {
for(const auto& status: son.statuses) for(const auto& status: son.statuses)
@ -796,9 +790,9 @@ void database::update_active_sons()
} }
} }
modify( son, [local_vote_buffer_ref]( son_object& obj ){ modify( son, [this]( son_object& obj ){
for(const auto& sidechain_vote_id : obj.sidechain_vote_ids ){ for(const auto& sidechain_vote_id : obj.sidechain_vote_ids ){
obj.total_votes[sidechain_vote_id.first] = local_vote_buffer_ref[sidechain_vote_id.second]; obj.total_votes[sidechain_vote_id.first] = _vote_tally_buffer.size() > sidechain_vote_id.second ? _vote_tally_buffer[sidechain_vote_id.second] : 0;
} }
for(auto& status: obj.statuses) for(auto& status: obj.statuses)
{ {
@ -853,7 +847,7 @@ void database::update_active_sons()
// Compare current and to-be lists of active sons // Compare current and to-be lists of active sons
const auto cur_active_sons = gpo.active_sons; const auto cur_active_sons = gpo.active_sons;
flat_map<sidechain_type, vector<son_info> > new_active_sons; flat_map<sidechain_type, vector<son_sidechain_info> > new_active_sons;
const auto &acc = get(gpo.parameters.son_account()); const auto &acc = get(gpo.parameters.son_account());
for( const auto& sidechain_sons : sons ){ for( const auto& sidechain_sons : sons ){
const auto& sidechain = sidechain_sons.first; const auto& sidechain = sidechain_sons.first;
@ -861,10 +855,11 @@ void database::update_active_sons()
new_active_sons[sidechain].reserve(sons_array.size()); new_active_sons[sidechain].reserve(sons_array.size());
for( const son_object& son : sons_array ) { for( const son_object& son : sons_array ) {
son_info swi; son_sidechain_info swi;
swi.son_id = son.id; swi.son_id = son.id;
swi.weight = acc.active.account_auths.at(son.son_account); swi.weight = acc.active.account_auths.at(son.son_account);
swi.signing_key = son.signing_key; swi.signing_key = son.signing_key;
if (son.sidechain_public_keys.find(sidechain) != son.sidechain_public_keys.end())
swi.public_key = son.sidechain_public_keys.at(sidechain); swi.public_key = son.sidechain_public_keys.at(sidechain);
new_active_sons[sidechain].push_back(swi); new_active_sons[sidechain].push_back(swi);
} }
@ -905,7 +900,7 @@ void database::update_active_sons()
} }
}); });
for(const auto& active_sidechain_type : active_sidechain_types) for(const auto& active_sidechain_type : supported_active_sidechain_types)
{ {
const son_schedule_object& sidechain_sso = son_schedule_id_type(get_son_schedule_id(active_sidechain_type))(*this); const son_schedule_object& sidechain_sso = son_schedule_id_type(get_son_schedule_id(active_sidechain_type))(*this);
modify(sidechain_sso, [&](son_schedule_object& _sso) modify(sidechain_sso, [&](son_schedule_object& _sso)
@ -914,12 +909,13 @@ void database::update_active_sons()
active_sons.reserve(gpo.active_sons.at(active_sidechain_type).size()); active_sons.reserve(gpo.active_sons.at(active_sidechain_type).size());
std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(), std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(),
std::inserter(active_sons, active_sons.end()), std::inserter(active_sons, active_sons.end()),
[](const son_info& swi) { [](const son_sidechain_info& swi) {
return swi.son_id; return swi.son_id;
}); });
_sso.scheduler.update(active_sons); _sso.scheduler.update(active_sons);
// similar to witness, produce schedule for sons // similar to witness, produce schedule for sons
if(cur_active_sons.at(active_sidechain_type).size() == 0 && new_active_sons.at(active_sidechain_type).size() > 0) if( ((cur_active_sons.contains(active_sidechain_type) && cur_active_sons.at(active_sidechain_type).size() == 0) ||
!cur_active_sons.contains(active_sidechain_type)) && new_active_sons.at(active_sidechain_type).size() > 0 )
{ {
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
for( size_t i=0; i<new_active_sons.at(active_sidechain_type).size(); ++i ) for( size_t i=0; i<new_active_sons.at(active_sidechain_type).size(); ++i )
@ -2189,6 +2185,7 @@ void database::perform_son_tasks()
gpo.pending_parameters->extensions.value.hive_asset = hive_asset.get_id(); gpo.pending_parameters->extensions.value.hive_asset = hive_asset.get_id();
}); });
} }
// Pay the SONs // Pay the SONs
if (head_block_time() >= HARDFORK_SON_TIME) if (head_block_time() >= HARDFORK_SON_TIME)
{ {
@ -2197,18 +2194,57 @@ void database::perform_son_tasks()
// and modify the global son funds accordingly, whatever is left is passed on to next budget // and modify the global son funds accordingly, whatever is left is passed on to next budget
pay_sons(); pay_sons();
} }
// Split vote_ids
if (head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) {
// Get SON 1.33.0 and check if it has HIVE vote_id
const son_id_type sid = son_id_type(0);
const auto p_son = find(sid);
if(p_son != nullptr) {
if (p_son->sidechain_vote_ids.find(sidechain_type::hive) == p_son->sidechain_vote_ids.end()) {
// Add vote_ids for HIVE and ETHEREUM to all existing SONs
const auto &all_sons = get_index_type<son_index>().indices().get<by_id>();
for (const son_object &son : all_sons) {
vote_id_type existing_vote_id_bitcoin;
vote_id_type new_vote_id_hive;
vote_id_type new_vote_id_eth;
modify(gpo, [&new_vote_id_hive, &new_vote_id_eth](global_property_object &p) {
new_vote_id_hive = get_next_vote_id(p, vote_id_type::son_hive);
new_vote_id_eth = get_next_vote_id(p, vote_id_type::son_ethereum);
});
modify(son, [new_vote_id_hive, new_vote_id_eth](son_object &obj) {
obj.sidechain_vote_ids[sidechain_type::hive] = new_vote_id_hive;
obj.sidechain_vote_ids[sidechain_type::ethereum] = new_vote_id_eth;
});
// Duplicate all votes from bitcoin to hive
const auto &all_accounts = get_index_type<account_index>().indices().get<by_id>();
for (const auto &account : all_accounts) {
if (account.options.votes.count(existing_vote_id_bitcoin) != 0) {
modify(account, [new_vote_id_hive](account_object &a) {
a.options.votes.insert(new_vote_id_hive);
});
}
}
}
}
}
}
} }
void update_son_params(database& db) void update_son_params(database& db)
{ {
if( (db.head_block_time() >= HARDFORK_SON2_TIME) && (db.head_block_time() < HARDFORK_SON3_TIME) ) if( (db.head_block_time() >= HARDFORK_SON2_TIME) && (db.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME) )
{ {
const auto& gpo = db.get_global_properties(); const auto& gpo = db.get_global_properties();
db.modify( gpo, []( global_property_object& gpo ) { db.modify( gpo, []( global_property_object& gpo ) {
gpo.parameters.extensions.value.maximum_son_count = 7; gpo.parameters.extensions.value.maximum_son_count = 7;
}); });
} }
else
if( (db.head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) )
{ {
const auto& gpo = db.get_global_properties(); const auto& gpo = db.get_global_properties();
db.modify( gpo, []( global_property_object& gpo ) { db.modify( gpo, []( global_property_object& gpo ) {
@ -2343,7 +2379,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
// same rationale as for witnesses // same rationale as for witnesses
d._committee_count_histogram_buffer[offset] += voting_stake; d._committee_count_histogram_buffer[offset] += voting_stake;
} }
FC_ASSERT( opinion_account.options.extensions.value.num_son.valid() , "Invalid son number" );
if ( opinion_account.options.extensions.value.num_son.valid() )
{
for(const auto& num_sidechain_son : *opinion_account.options.extensions.value.num_son) { for(const auto& num_sidechain_son : *opinion_account.options.extensions.value.num_son) {
const auto sidechain = num_sidechain_son.first; const auto sidechain = num_sidechain_son.first;
const auto& num_son = num_sidechain_son.second; const auto& num_son = num_sidechain_son.second;
@ -2359,6 +2397,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
d._son_count_histogram_buffer.at(sidechain)[offset] += voting_stake; d._son_count_histogram_buffer.at(sidechain)[offset] += voting_stake;
} }
} }
}
d._total_voting_stake += voting_stake; d._total_voting_stake += voting_stake;
} }

View file

@ -112,7 +112,6 @@ void database::reindex( fc::path data_dir )
uint32_t undo_point = last_block_num < 50 ? 0 : last_block_num - 50; uint32_t undo_point = last_block_num < 50 ? 0 : last_block_num - 50;
ilog( "Replaying blocks, starting at ${next}...", ("next",head_block_num() + 1) ); ilog( "Replaying blocks, starting at ${next}...", ("next",head_block_num() + 1) );
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
auto_undo_enabler undo(_slow_replays, _undo_db); auto_undo_enabler undo(_slow_replays, _undo_db);
if( head_block_num() >= undo_point ) if( head_block_num() >= undo_point )
{ {

View file

@ -204,17 +204,18 @@ void database::update_son_schedule()
{ {
const global_property_object& gpo = get_global_properties(); const global_property_object& gpo = get_global_properties();
for(const auto& active_sidechain_type : active_sidechain_types) for(const auto& active_sidechain_type : active_sidechain_types(head_block_time()))
{ {
const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type))); const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type)));
if( head_block_num() % gpo.active_sons.at(active_sidechain_type).size() == 0) if( gpo.active_sons.at(active_sidechain_type).size() != 0 &&
head_block_num() % gpo.active_sons.at(active_sidechain_type).size() == 0)
{ {
modify( sidechain_sso, [&]( son_schedule_object& _sso ) modify( sidechain_sso, [&]( son_schedule_object& _sso )
{ {
_sso.current_shuffled_sons.clear(); _sso.current_shuffled_sons.clear();
_sso.current_shuffled_sons.reserve( gpo.active_sons.at(active_sidechain_type).size() ); _sso.current_shuffled_sons.reserve( gpo.active_sons.at(active_sidechain_type).size() );
for ( const son_info &w : gpo.active_sons.at(active_sidechain_type) ) { for ( const auto &w : gpo.active_sons.at(active_sidechain_type) ) {
_sso.current_shuffled_sons.push_back(w.son_id); _sso.current_shuffled_sons.push_back(w.son_id);
} }
@ -350,7 +351,7 @@ void database::update_son_schedule(const signed_block& next_block)
assert( dpo.random.data_size() == witness_scheduler_rng::seed_length ); assert( dpo.random.data_size() == witness_scheduler_rng::seed_length );
assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() ); assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() );
for(const auto& active_sidechain_type : active_sidechain_types) for(const auto& active_sidechain_type : active_sidechain_types(head_block_time()))
{ {
const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type))); const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type)));
son_id_type first_son; son_id_type first_son;

View file

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

View file

@ -1,7 +1,7 @@
#ifndef HARDFORK_SON_FOR_ETHEREUM_TIME #ifndef HARDFORK_SON_FOR_ETHEREUM_TIME
#ifdef BUILD_PEERPLAYS_TESTNET #ifdef BUILD_PEERPLAYS_TESTNET
#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2022-07-01T00:00:00")) #define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-01-24T00:00:00"))
#else #else
#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2022-07-01T00:00:00")) #define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-03-24T00:00:00"))
#endif #endif
#endif #endif

View file

@ -584,14 +584,14 @@ namespace graphene { namespace chain {
void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props); void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props);
void update_active_witnesses(); void update_active_witnesses();
void update_active_committee_members(); void update_active_committee_members();
void update_son_metrics( const flat_map<sidechain_type, vector<son_info> >& curr_active_sons ); void update_son_metrics( const flat_map<sidechain_type, vector<son_sidechain_info> >& curr_active_sons );
void update_active_sons(); void update_active_sons();
void remove_son_proposal( const proposal_object& proposal ); void remove_son_proposal( const proposal_object& proposal );
void remove_inactive_son_down_proposals( const vector<son_id_type>& son_ids_to_remove ); void remove_inactive_son_down_proposals( const vector<son_id_type>& son_ids_to_remove );
void remove_inactive_son_proposals( const vector<son_id_type>& son_ids_to_remove ); void remove_inactive_son_proposals( const vector<son_id_type>& son_ids_to_remove );
void update_son_statuses( const flat_map<sidechain_type, vector<son_info> >& curr_active_sons, void update_son_statuses( const flat_map<sidechain_type, vector<son_sidechain_info> >& curr_active_sons,
const flat_map<sidechain_type, vector<son_info> >& new_active_sons ); const flat_map<sidechain_type, vector<son_sidechain_info> >& new_active_sons );
void update_son_wallet( const flat_map<sidechain_type, vector<son_info> >& new_active_sons ); void update_son_wallet( const flat_map<sidechain_type, vector<son_sidechain_info> >& new_active_sons );
void update_worker_votes(); void update_worker_votes();
public: public:
@ -636,7 +636,7 @@ namespace graphene { namespace chain {
vector<uint64_t> _committee_count_histogram_buffer; vector<uint64_t> _committee_count_histogram_buffer;
flat_map<sidechain_type, vector<uint64_t> > _son_count_histogram_buffer = []{ flat_map<sidechain_type, vector<uint64_t> > _son_count_histogram_buffer = []{
flat_map<sidechain_type, vector<uint64_t> > son_count_histogram_buffer; flat_map<sidechain_type, vector<uint64_t> > son_count_histogram_buffer;
for(const auto& active_sidechain_type : active_sidechain_types){ for(const auto& active_sidechain_type : all_sidechain_types){
son_count_histogram_buffer[active_sidechain_type] = vector<uint64_t>{}; son_count_histogram_buffer[active_sidechain_type] = vector<uint64_t>{};
} }
return son_count_histogram_buffer; return son_count_histogram_buffer;

View file

@ -27,7 +27,7 @@
#include <graphene/chain/protocol/chain_parameters.hpp> #include <graphene/chain/protocol/chain_parameters.hpp>
#include <graphene/chain/protocol/types.hpp> #include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/database.hpp> #include <graphene/chain/database.hpp>
#include <graphene/chain/son_info.hpp> #include <graphene/chain/son_sidechain_info.hpp>
#include <graphene/db/object.hpp> #include <graphene/db/object.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
@ -52,12 +52,12 @@ namespace graphene { namespace chain {
uint32_t next_available_vote_id = 0; uint32_t next_available_vote_id = 0;
vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval
flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval
flat_map<sidechain_type, vector<son_info> > active_sons = []() // updated once per maintenance interval flat_map<sidechain_type, vector<son_sidechain_info> > active_sons = []() // updated once per maintenance interval
{ {
flat_map<sidechain_type, vector<son_info> > active_sons; flat_map<sidechain_type, vector<son_sidechain_info> > active_sons;
for(const auto& active_sidechain_type : active_sidechain_types) for(const auto& active_sidechain_type : all_sidechain_types)
{ {
active_sons[active_sidechain_type] = vector<son_info>(); active_sons[active_sidechain_type] = vector<son_sidechain_info>();
} }
return active_sons; return active_sons;
}(); }();

View file

@ -44,7 +44,7 @@ namespace graphene { namespace chain {
/// Must not exceed the actual number of son members voted for in @ref votes /// Must not exceed the actual number of son members voted for in @ref votes
optional< flat_map<sidechain_type, uint16_t> > num_son = []{ optional< flat_map<sidechain_type, uint16_t> > num_son = []{
flat_map<sidechain_type, uint16_t> num_son; flat_map<sidechain_type, uint16_t> num_son;
for(const auto& active_sidechain_type : active_sidechain_types){ for(const auto& active_sidechain_type : all_sidechain_types){
num_son[active_sidechain_type] = 0; num_son[active_sidechain_type] = 0;
} }
return num_son; return num_son;

View file

@ -71,7 +71,7 @@ FC_REFLECT( graphene::chain::sidechain_transaction_create_operation, (fee)(payer
(sidechain) (sidechain)
(object_id) (object_id)
(transaction) (transaction)
(signers) ) (signers))
FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation, (fee)(signer)(payer) FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation, (fee)(signer)(payer)

View file

@ -1,17 +1,23 @@
#pragma once #pragma once
#include <graphene/chain/protocol/base.hpp> #include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/son_info.hpp> #include <graphene/chain/son_info.hpp>
#include <graphene/chain/son_sidechain_info.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
struct son_wallet_recreate_operation : public base_operation struct son_wallet_recreate_operation : public base_operation
{ {
struct fee_parameters_type { uint64_t fee = 0; }; struct fee_parameters_type { uint64_t fee = 0; };
struct ext
{
optional<flat_map<sidechain_type, vector<son_sidechain_info> > > sidechain_sons;
};
asset fee; asset fee;
account_id_type payer; account_id_type payer;
flat_map<sidechain_type, vector<son_info> > sons; vector<son_info> sons;
extension< ext > extensions;
account_id_type fee_payer()const { return payer; } account_id_type fee_payer()const { return payer; }
share_type calculate_fee(const fee_parameters_type& k)const { return 0; } share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
@ -35,6 +41,7 @@ namespace graphene { namespace chain {
} } // namespace graphene::chain } } // namespace graphene::chain
FC_REFLECT(graphene::chain::son_wallet_recreate_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_recreate_operation::fee_parameters_type, (fee) )
FC_REFLECT(graphene::chain::son_wallet_recreate_operation, (fee)(payer)(sons) ) FC_REFLECT(graphene::chain::son_wallet_recreate_operation::ext, (sidechain_sons))
FC_REFLECT(graphene::chain::son_wallet_recreate_operation, (fee)(payer)(sons)(extensions) )
FC_REFLECT(graphene::chain::son_wallet_update_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_update_operation::fee_parameters_type, (fee) )
FC_REFLECT(graphene::chain::son_wallet_update_operation, (fee)(payer)(son_wallet_id)(sidechain)(address) ) FC_REFLECT(graphene::chain::son_wallet_update_operation, (fee)(payer)(son_wallet_id)(sidechain)(address) )

View file

@ -1,7 +1,11 @@
#pragma once #pragma once
#include <set> #include <set>
#include <graphene/chain/hardfork.hpp>
#include <fc/reflect/reflect.hpp> #include <fc/reflect/reflect.hpp>
#include <fc/time.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
@ -14,9 +18,23 @@ enum class sidechain_type {
hive hive
}; };
static const std::set<sidechain_type> active_sidechain_types = {sidechain_type::bitcoin, sidechain_type::ethereum, sidechain_type::hive}; static const std::set<sidechain_type> all_sidechain_types = {sidechain_type::bitcoin, sidechain_type::ethereum, sidechain_type::hive};
} } inline std::set<sidechain_type> active_sidechain_types(const fc::time_point_sec block_time) {
std::set<sidechain_type> active_sidechain_types{};
if (block_time >= HARDFORK_SON_TIME)
active_sidechain_types.insert(sidechain_type::bitcoin);
if (block_time >= HARDFORK_SON_FOR_HIVE_TIME)
active_sidechain_types.insert(sidechain_type::hive);
if (block_time >= HARDFORK_SON_FOR_ETHEREUM_TIME)
active_sidechain_types.insert(sidechain_type::ethereum);
return active_sidechain_types;
}
} // namespace chain
} // namespace graphene
FC_REFLECT_ENUM(graphene::chain::sidechain_type, FC_REFLECT_ENUM(graphene::chain::sidechain_type,
(unknown) (unknown)

View file

@ -2,7 +2,7 @@
#include <boost/multi_index/composite_key.hpp> #include <boost/multi_index/composite_key.hpp>
#include <graphene/chain/protocol/types.hpp> #include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/sidechain_defs.hpp> #include <graphene/chain/sidechain_defs.hpp>
#include <graphene/chain/son_info.hpp> #include <graphene/chain/son_sidechain_info.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
using namespace graphene::db; using namespace graphene::db;
@ -30,7 +30,7 @@ namespace graphene { namespace chain {
sidechain_type sidechain = sidechain_type::unknown; sidechain_type sidechain = sidechain_type::unknown;
object_id_type object_id; object_id_type object_id;
std::string transaction; std::string transaction;
std::vector<son_info> signers; std::vector<son_sidechain_info> signers;
std::vector<std::pair<son_id_type, std::string>> signatures; std::vector<std::pair<son_id_type, std::string>> signatures;
std::string sidechain_transaction; std::string sidechain_transaction;

View file

@ -3,7 +3,6 @@
#include <graphene/chain/sidechain_defs.hpp> #include <graphene/chain/sidechain_defs.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
using namespace graphene::db;
/** /**
* @class son_info * @class son_info
@ -14,23 +13,30 @@ namespace graphene { namespace chain {
son_id_type son_id; son_id_type son_id;
weight_type weight = 0; weight_type weight = 0;
public_key_type signing_key; public_key_type signing_key;
string public_key; flat_map<sidechain_type, string> sidechain_public_keys;
bool operator==(const son_info& rhs) const { bool operator==(const son_info& rhs) {
bool son_sets_equal = bool son_sets_equal =
(son_id == rhs.son_id) && (son_id == rhs.son_id) &&
(weight == rhs.weight) && (weight == rhs.weight) &&
(signing_key == rhs.signing_key) && (signing_key == rhs.signing_key) &&
(public_key == rhs.public_key); (sidechain_public_keys.size() == rhs.sidechain_public_keys.size());
if (son_sets_equal) {
bool sidechain_public_keys_equal = true;
for (size_t i = 0; i < sidechain_public_keys.size(); i++) {
const auto lhs_scpk = sidechain_public_keys.nth(i);
const auto rhs_scpk = rhs.sidechain_public_keys.nth(i);
sidechain_public_keys_equal = sidechain_public_keys_equal &&
(lhs_scpk->first == rhs_scpk->first) &&
(lhs_scpk->second == rhs_scpk->second);
}
son_sets_equal = son_sets_equal && sidechain_public_keys_equal;
}
return son_sets_equal; return son_sets_equal;
} }
}; };
} } } }
FC_REFLECT( graphene::chain::son_info, FC_REFLECT( graphene::chain::son_info, (son_id) (weight) (signing_key) (sidechain_public_keys) )
(son_id)
(weight)
(signing_key)
(public_key) )

View file

@ -65,7 +65,15 @@ namespace graphene { namespace chain {
account_id_type son_account; account_id_type son_account;
flat_map<sidechain_type, vote_id_type> sidechain_vote_ids; flat_map<sidechain_type, vote_id_type> sidechain_vote_ids;
flat_map<sidechain_type, uint64_t> total_votes = []()
{
flat_map<sidechain_type, uint64_t> total_votes; flat_map<sidechain_type, uint64_t> total_votes;
for(const auto& active_sidechain_type : all_sidechain_types)
{
total_votes[active_sidechain_type] = 0;
}
return total_votes;
}();
string url; string url;
vesting_balance_id_type deposit; vesting_balance_id_type deposit;
public_key_type signing_key; public_key_type signing_key;
@ -74,7 +82,7 @@ namespace graphene { namespace chain {
flat_map<sidechain_type, son_status> statuses = []() flat_map<sidechain_type, son_status> statuses = []()
{ {
flat_map<sidechain_type, son_status> statuses; flat_map<sidechain_type, son_status> statuses;
for(const auto& active_sidechain_type : active_sidechain_types) for(const auto& active_sidechain_type : all_sidechain_types)
{ {
statuses[active_sidechain_type] = son_status::inactive; statuses[active_sidechain_type] = son_status::inactive;
} }
@ -83,13 +91,15 @@ namespace graphene { namespace chain {
flat_map<sidechain_type, string> sidechain_public_keys; flat_map<sidechain_type, string> sidechain_public_keys;
void pay_son_fee(share_type pay, database& db); void pay_son_fee(share_type pay, database& db);
bool has_valid_config()const; bool has_valid_config(time_point_sec head_block_time, sidechain_type sidechain) const;
bool has_valid_config(time_point_sec head_block_time)const;
inline vote_id_type get_sidechain_vote_id(sidechain_type sidechain) const { return sidechain_vote_ids.at(sidechain); } inline optional<vote_id_type> get_sidechain_vote_id(sidechain_type sidechain) const { return sidechain_vote_ids.contains(sidechain) ? sidechain_vote_ids.at(sidechain) : optional<vote_id_type>{}; }
inline vote_id_type get_bitcoin_vote_id() const { return get_sidechain_vote_id(sidechain_type::bitcoin); } inline optional<vote_id_type> get_bitcoin_vote_id() const { return get_sidechain_vote_id(sidechain_type::bitcoin); }
inline vote_id_type get_hive_vote_id() const { return get_sidechain_vote_id(sidechain_type::hive); } inline optional<vote_id_type> get_hive_vote_id() const { return get_sidechain_vote_id(sidechain_type::hive); }
inline vote_id_type get_ethereum_vote_id() const { return get_sidechain_vote_id(sidechain_type::ethereum); } inline optional<vote_id_type> get_ethereum_vote_id() const { return get_sidechain_vote_id(sidechain_type::ethereum); }
private:
bool has_valid_config(sidechain_type sidechain) const;
}; };
struct by_account; struct by_account;
@ -105,14 +115,14 @@ namespace graphene { namespace chain {
ordered_unique< tag<by_account>, ordered_unique< tag<by_account>,
member<son_object, account_id_type, &son_object::son_account> member<son_object, account_id_type, &son_object::son_account>
>, >,
ordered_unique< tag<by_vote_id_bitcoin>, ordered_non_unique< tag<by_vote_id_bitcoin>,
const_mem_fun<son_object, vote_id_type, &son_object::get_bitcoin_vote_id> const_mem_fun<son_object, optional<vote_id_type>, &son_object::get_bitcoin_vote_id>
>, >,
ordered_unique< tag<by_vote_id_hive>, ordered_non_unique< tag<by_vote_id_hive>,
const_mem_fun<son_object, vote_id_type, &son_object::get_hive_vote_id> const_mem_fun<son_object, optional<vote_id_type>, &son_object::get_hive_vote_id>
>, >,
ordered_unique< tag<by_vote_id_ethereum>, ordered_non_unique< tag<by_vote_id_ethereum>,
const_mem_fun<son_object, vote_id_type, &son_object::get_ethereum_vote_id> const_mem_fun<son_object, optional<vote_id_type>, &son_object::get_ethereum_vote_id>
> >
> >
>; >;

View file

@ -0,0 +1,31 @@
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/sidechain_defs.hpp>
namespace graphene { namespace chain {
/**
* @class son_sidechain_info
* @brief tracks information about a SON info required to re/create primary wallet
* @ingroup object
*/
struct son_sidechain_info {
son_id_type son_id;
weight_type weight = 0;
public_key_type signing_key;
string public_key;
bool operator==(const son_sidechain_info& rhs) const {
bool son_sets_equal =
(son_id == rhs.son_id) &&
(weight == rhs.weight) &&
(signing_key == rhs.signing_key) &&
(public_key == rhs.public_key);
return son_sets_equal;
}
};
} }
FC_REFLECT( graphene::chain::son_sidechain_info, (son_id) (weight) (signing_key) (public_key) )

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <graphene/chain/protocol/types.hpp> #include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/son_info.hpp> #include <graphene/chain/son_sidechain_info.hpp>
#include <graphene/chain/sidechain_defs.hpp> #include <graphene/chain/sidechain_defs.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
@ -21,7 +21,7 @@ namespace graphene { namespace chain {
time_point_sec expires; time_point_sec expires;
flat_map<sidechain_type, string> addresses; flat_map<sidechain_type, string> addresses;
flat_map<sidechain_type, vector<son_info> > sons; flat_map<sidechain_type, vector<son_sidechain_info> > sons;
}; };
struct by_valid_from; struct by_valid_from;

View file

@ -174,31 +174,37 @@ void account_options::validate() const
{ {
auto needed_witnesses = num_witness; auto needed_witnesses = num_witness;
auto needed_committee = num_committee; auto needed_committee = num_committee;
FC_ASSERT( extensions.value.num_son.valid() , "Invalid son number" );
flat_map<sidechain_type, uint16_t> needed_sons = *extensions.value.num_son;
for( vote_id_type id : votes ) for( vote_id_type id : votes )
if( id.type() == vote_id_type::witness && needed_witnesses ) if( id.type() == vote_id_type::witness && needed_witnesses )
--needed_witnesses; --needed_witnesses;
else if ( id.type() == vote_id_type::committee && needed_committee ) else if ( id.type() == vote_id_type::committee && needed_committee )
--needed_committee; --needed_committee;
else if ( id.type() == vote_id_type::son_bitcoin && needed_sons[sidechain_type::bitcoin] )
FC_ASSERT( needed_witnesses == 0,
"May not specify fewer witnesses than the number voted for.");
FC_ASSERT( needed_committee == 0,
"May not specify fewer committee members than the number voted for.");
if ( extensions.value.num_son.valid() )
{
flat_map<sidechain_type, uint16_t> needed_sons = *extensions.value.num_son;
for( vote_id_type id : votes )
if ( id.type() == vote_id_type::son_bitcoin && needed_sons[sidechain_type::bitcoin] )
--needed_sons[sidechain_type::bitcoin]; --needed_sons[sidechain_type::bitcoin];
else if ( id.type() == vote_id_type::son_hive && needed_sons[sidechain_type::hive] ) else if ( id.type() == vote_id_type::son_hive && needed_sons[sidechain_type::hive] )
--needed_sons[sidechain_type::hive]; --needed_sons[sidechain_type::hive];
else if ( id.type() == vote_id_type::son_ethereum && needed_sons[sidechain_type::ethereum] ) else if ( id.type() == vote_id_type::son_ethereum && needed_sons[sidechain_type::ethereum] )
--needed_sons[sidechain_type::ethereum]; --needed_sons[sidechain_type::ethereum];
FC_ASSERT( needed_witnesses == 0,
"May not specify fewer witnesses than the number voted for.");
FC_ASSERT( needed_committee == 0,
"May not specify fewer committee members than the number voted for.");
FC_ASSERT( needed_sons[sidechain_type::bitcoin] == 0, FC_ASSERT( needed_sons[sidechain_type::bitcoin] == 0,
"May not specify fewer Bitcoin SONs than the number voted for."); "May not specify fewer Bitcoin SONs than the number voted for.");
FC_ASSERT( needed_sons[sidechain_type::hive] == 0, FC_ASSERT( needed_sons[sidechain_type::hive] == 0,
"May not specify fewer Hive SONs than the number voted for."); "May not specify fewer Hive SONs than the number voted for.");
FC_ASSERT( needed_sons[sidechain_type::ethereum] == 0, FC_ASSERT( needed_sons[sidechain_type::ethereum] == 0,
"May not specify fewer Ethereum SONs than the number voted for."); "May not specify fewer Ethereum SONs than the number voted for.");
}
} }
void affiliate_reward_distribution::validate() const void affiliate_reward_distribution::validate() const

View file

@ -11,7 +11,7 @@ namespace graphene { namespace chain {
void_result sidechain_transaction_create_evaluator::do_evaluate(const sidechain_transaction_create_operation &op) void_result sidechain_transaction_create_evaluator::do_evaluate(const sidechain_transaction_create_operation &op)
{ try { { try {
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer.");
FC_ASSERT((op.object_id.is<son_wallet_id_type>() || op.object_id.is<son_wallet_deposit_id_type>() || op.object_id.is<son_wallet_withdraw_id_type>()), "Invalid object id"); FC_ASSERT((op.object_id.is<son_wallet_id_type>() || op.object_id.is<son_wallet_deposit_id_type>() || op.object_id.is<son_wallet_withdraw_id_type>()), "Invalid object id");
@ -28,15 +28,26 @@ void_result sidechain_transaction_create_evaluator::do_evaluate(const sidechain_
object_id_type sidechain_transaction_create_evaluator::do_apply(const sidechain_transaction_create_operation &op) object_id_type sidechain_transaction_create_evaluator::do_apply(const sidechain_transaction_create_operation &op)
{ try { { try {
const auto &new_sidechain_transaction_object = db().create<sidechain_transaction_object>([&](sidechain_transaction_object &sto) { const auto &new_sidechain_transaction_object = db().create<sidechain_transaction_object>([&](sidechain_transaction_object &sto) {
sto.timestamp = db().head_block_time(); sto.timestamp = db().head_block_time();
sto.sidechain = op.sidechain; sto.sidechain = op.sidechain;
sto.object_id = op.object_id; sto.object_id = op.object_id;
sto.transaction = op.transaction; sto.transaction = op.transaction;
sto.signers = op.signers; std::vector<son_sidechain_info> signers;
std::transform(op.signers.begin(), op.signers.end(), std::inserter(sto.signatures, sto.signatures.end()), [](const son_info &si) { for(const auto& signer : op.signers){
son_sidechain_info ssi;
ssi.son_id = signer.son_id;
ssi.weight = signer.weight;
ssi.signing_key = signer.signing_key;
ssi.public_key = signer.sidechain_public_keys.at(op.sidechain);
signers.emplace_back(std::move(ssi));
}
sto.signers = std::move(signers);
std::transform(sto.signers.begin(), sto.signers.end(), std::inserter(sto.signatures, sto.signatures.end()), [](const son_sidechain_info &si) {
return std::make_pair(si.son_id, std::string()); return std::make_pair(si.son_id, std::string());
}); });
for (const auto &si : op.signers) { for (const auto &si : sto.signers) {
sto.total_weight = sto.total_weight + si.weight; sto.total_weight = sto.total_weight + si.weight;
} }
sto.sidechain_transaction = ""; sto.sidechain_transaction = "";

View file

@ -38,20 +38,29 @@ void_result create_son_evaluator::do_evaluate(const son_create_operation& op)
object_id_type create_son_evaluator::do_apply(const son_create_operation& op) object_id_type create_son_evaluator::do_apply(const son_create_operation& op)
{ try { { try {
vote_id_type vote_id_bitcoin; vote_id_type vote_id;
vote_id_type vote_id_hive; flat_map<sidechain_type, vote_id_type> vote_ids;
vote_id_type vote_id_ethereum;
db().modify(db().get_global_properties(), [&vote_id_bitcoin, &vote_id_hive, &vote_id_ethereum](global_property_object& p) {
vote_id_bitcoin = get_next_vote_id(p, vote_id_type::son_bitcoin);
vote_id_hive = get_next_vote_id(p, vote_id_type::son_hive);
vote_id_ethereum = get_next_vote_id(p, vote_id_type::son_ethereum);
});
const auto& new_son_object = db().create<son_object>( [&]( son_object& obj ){ const auto now = db().head_block_time();
if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
db().modify(db().get_global_properties(), [&vote_id](global_property_object &p) {
vote_id = get_next_vote_id(p, vote_id_type::son_bitcoin);
});
}
else {
db().modify(db().get_global_properties(), [&vote_ids](global_property_object &p) {
vote_ids[sidechain_type::bitcoin] = get_next_vote_id(p, vote_id_type::son_bitcoin);
vote_ids[sidechain_type::hive] = get_next_vote_id(p, vote_id_type::son_hive);
vote_ids[sidechain_type::ethereum] = get_next_vote_id(p, vote_id_type::son_ethereum);
});
}
const auto& new_son_object = db().create<son_object>( [&]( son_object& obj ) {
obj.son_account = op.owner_account; obj.son_account = op.owner_account;
obj.sidechain_vote_ids[sidechain_type::bitcoin] = vote_id_bitcoin; if( now < HARDFORK_SON_FOR_ETHEREUM_TIME )
obj.sidechain_vote_ids[sidechain_type::hive] = vote_id_hive; obj.sidechain_vote_ids[sidechain_type::bitcoin] = vote_id;
obj.sidechain_vote_ids[sidechain_type::ethereum] = vote_id_ethereum; else
obj.sidechain_vote_ids = vote_ids;
obj.url = op.url; obj.url = op.url;
obj.deposit = op.deposit; obj.deposit = op.deposit;
obj.signing_key = op.signing_key; obj.signing_key = op.signing_key;
@ -168,7 +177,7 @@ void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation&
fc::time_point_sec min_ts = db().head_block_time() - fc::seconds(5 * db().block_interval()); fc::time_point_sec min_ts = db().head_block_time() - fc::seconds(5 * db().block_interval());
// Account for server ntp sync difference // Account for server ntp sync difference
fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(5 * db().block_interval()); fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(5 * db().block_interval());
for(const auto& active_sidechain_type : active_sidechain_types) { for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())) {
if(stats.last_active_timestamp.contains(active_sidechain_type)) if(stats.last_active_timestamp.contains(active_sidechain_type))
FC_ASSERT(op.ts > stats.last_active_timestamp.at(active_sidechain_type), "Heartbeat sent for sidechain = ${sidechain} without waiting minimum time", ("sidechain", active_sidechain_type)); FC_ASSERT(op.ts > stats.last_active_timestamp.at(active_sidechain_type), "Heartbeat sent for sidechain = ${sidechain} without waiting minimum time", ("sidechain", active_sidechain_type));
if(stats.last_down_timestamp.contains(active_sidechain_type)) if(stats.last_down_timestamp.contains(active_sidechain_type))
@ -195,7 +204,7 @@ object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation&
active_son_ids.reserve(active_sons.size()); active_son_ids.reserve(active_sons.size());
std::transform(active_sons.cbegin(), active_sons.cend(), std::transform(active_sons.cbegin(), active_sons.cend(),
std::inserter(active_son_ids, active_son_ids.end()), std::inserter(active_son_ids, active_son_ids.end()),
[](const son_info &swi) { [](const son_sidechain_info &swi) {
return swi.son_id; return swi.son_id;
}); });
@ -244,7 +253,7 @@ void_result son_report_down_evaluator::do_evaluate(const son_report_down_operati
status_need_to_report_down = true; status_need_to_report_down = true;
} }
FC_ASSERT(status_need_to_report_down, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down"); FC_ASSERT(status_need_to_report_down, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down");
for(const auto& active_sidechain_type : active_sidechain_types) { for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())) {
if(stats.last_active_timestamp.contains(active_sidechain_type)) if(stats.last_active_timestamp.contains(active_sidechain_type))
FC_ASSERT(op.down_ts >= stats.last_active_timestamp.at(active_sidechain_type), "sidechain = ${sidechain} down_ts should be greater than last_active_timestamp", ("sidechain", active_sidechain_type)); FC_ASSERT(op.down_ts >= stats.last_active_timestamp.at(active_sidechain_type), "sidechain = ${sidechain} down_ts should be greater than last_active_timestamp", ("sidechain", active_sidechain_type));
} }

View file

@ -6,24 +6,22 @@ namespace graphene { namespace chain {
db.adjust_balance(son_account, pay); db.adjust_balance(son_account, pay);
} }
bool son_object::has_valid_config()const { bool son_object::has_valid_config(sidechain_type sidechain) const {
return ((std::string(signing_key).length() > 0) && return (sidechain_public_keys.find( sidechain ) != sidechain_public_keys.end()) &&
(sidechain_public_keys.size() > 0) && (sidechain_public_keys.at(sidechain).length() > 0);
(sidechain_public_keys.find( sidechain_type::bitcoin ) != sidechain_public_keys.end()) &&
(sidechain_public_keys.at(sidechain_type::bitcoin).length() > 0));
} }
bool son_object::has_valid_config(time_point_sec head_block_time)const { bool son_object::has_valid_config(time_point_sec head_block_time, sidechain_type sidechain) const {
bool retval = has_valid_config(); bool retval = (std::string(signing_key).length() > 0) && (sidechain_public_keys.size() > 0);
if (head_block_time >= HARDFORK_SON_FOR_HIVE_TIME) { if (head_block_time < HARDFORK_SON_FOR_HIVE_TIME) {
retval = retval && retval = retval && has_valid_config(sidechain_type::bitcoin);
(sidechain_public_keys.find( sidechain_type::hive ) != sidechain_public_keys.end()) && }
(sidechain_public_keys.at(sidechain_type::hive).length() > 0); if (head_block_time >= HARDFORK_SON_FOR_HIVE_TIME && head_block_time < HARDFORK_SON_FOR_ETHEREUM_TIME) {
retval = retval && has_valid_config(sidechain_type::bitcoin) && has_valid_config(sidechain_type::hive);
retval = retval && }
(sidechain_public_keys.find( sidechain_type::ethereum ) != sidechain_public_keys.end()) && else if (head_block_time >= HARDFORK_SON_FOR_ETHEREUM_TIME) {
(sidechain_public_keys.at(sidechain_type::ethereum).length() > 0); retval = retval && has_valid_config(sidechain);
} }
return retval; return retval;

View file

@ -7,8 +7,9 @@ namespace graphene { namespace chain {
void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate_operation& op) void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate_operation& op)
{ try{ { try{
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); const auto now = db().head_block_time();
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); FC_ASSERT(now >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer.");
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
auto itr = idx.rbegin(); auto itr = idx.rbegin();
@ -16,7 +17,23 @@ void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate
{ {
// Compare current wallet SONs and to-be lists of active sons // Compare current wallet SONs and to-be lists of active sons
auto cur_wallet_sons = (*itr).sons; auto cur_wallet_sons = (*itr).sons;
auto new_wallet_sons = op.sons; flat_map<sidechain_type, vector<son_sidechain_info> > new_wallet_sons;
if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
for(const auto& son : op.sons){
for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())){
son_sidechain_info ssi;
ssi.son_id = son.son_id;
ssi.weight = son.weight;
ssi.signing_key = son.signing_key;
ssi.public_key = son.sidechain_public_keys.at(active_sidechain_type);
new_wallet_sons[active_sidechain_type].emplace_back(std::move(ssi));
}
}
}
else{
FC_ASSERT(op.extensions.value.sidechain_sons.valid(), "Sons is not valid");
new_wallet_sons = *op.extensions.value.sidechain_sons;
}
bool son_sets_equal = (cur_wallet_sons.size() == new_wallet_sons.size()); bool son_sets_equal = (cur_wallet_sons.size() == new_wallet_sons.size());
if (son_sets_equal) { if (son_sets_equal) {
@ -51,9 +68,26 @@ object_id_type recreate_son_wallet_evaluator::do_apply(const son_wallet_recreate
} }
const auto& new_son_wallet_object = db().create<son_wallet_object>( [&]( son_wallet_object& obj ){ const auto& new_son_wallet_object = db().create<son_wallet_object>( [&]( son_wallet_object& obj ){
obj.valid_from = db().head_block_time(); const auto now = db().head_block_time();
obj.valid_from = now;
obj.expires = time_point_sec::maximum(); obj.expires = time_point_sec::maximum();
obj.sons = op.sons; if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
flat_map<sidechain_type, vector<son_sidechain_info> > sons;
for(const auto& son : op.sons){
for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())){
son_sidechain_info ssi;
ssi.son_id = son.son_id;
ssi.weight = son.weight;
ssi.signing_key = son.signing_key;
ssi.public_key = son.sidechain_public_keys.at(active_sidechain_type);
sons[active_sidechain_type].emplace_back(std::move(ssi));
}
}
obj.sons = std::move(sons);
}
else{
obj.sons = *op.extensions.value.sidechain_sons;
}
}); });
return new_son_wallet_object.id; return new_son_wallet_object.id;
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }
@ -64,7 +98,10 @@ void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_ope
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
FC_ASSERT( idx.find(op.son_wallet_id) != idx.end() ); const auto ast = active_sidechain_types(db().head_block_time());
const auto id = (op.son_wallet_id.instance.value - std::distance(ast.begin(), ast.find(op.sidechain))) / ast.size();
const son_wallet_id_type son_wallet_id{ id };
FC_ASSERT( idx.find(son_wallet_id) != idx.end() );
//auto itr = idx.find(op.son_wallet_id); //auto itr = idx.find(op.son_wallet_id);
//FC_ASSERT( itr->addresses.find(op.sidechain) == itr->addresses.end() || //FC_ASSERT( itr->addresses.find(op.sidechain) == itr->addresses.end() ||
// itr->addresses.at(op.sidechain).empty(), "Sidechain wallet address already set"); // itr->addresses.at(op.sidechain).empty(), "Sidechain wallet address already set");
@ -74,7 +111,10 @@ void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_ope
object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_operation& op) object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_operation& op)
{ try { { try {
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
auto itr = idx.find(op.son_wallet_id); const auto ast = active_sidechain_types(db().head_block_time());
const auto id = (op.son_wallet_id.instance.value - std::distance(ast.begin(), ast.find(op.sidechain))) / ast.size();
const son_wallet_id_type son_wallet_id{ id };
auto itr = idx.find(son_wallet_id);
if (itr != idx.end()) if (itr != idx.end())
{ {
if (itr->addresses.find(op.sidechain) == itr->addresses.end()) { if (itr->addresses.find(op.sidechain) == itr->addresses.end()) {

View file

@ -10,12 +10,13 @@ namespace graphene { namespace chain {
void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_create_operation& op) void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_create_operation& op)
{ try { { try {
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); const auto now = db().head_block_time();
FC_ASSERT(now >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
const auto &son_idx = db().get_index_type<son_index>().indices().get<by_id>(); const auto &son_idx = db().get_index_type<son_index>().indices().get<by_id>();
const auto so = son_idx.find(op.son_id); const auto so = son_idx.find(op.son_id);
FC_ASSERT(so != son_idx.end(), "SON not found"); FC_ASSERT(so != son_idx.end(), "SON not found");
FC_ASSERT(so->son_account == op.payer, "Payer is not SON account owner"); FC_ASSERT(so->son_account == op.payer, "Payer is not SON account owner");
FC_ASSERT(!(op.sidechain == sidechain_type::peerplays && now >= HARDFORK_SON_FOR_ETHEREUM_TIME), "Peerplays sidechain type is not allowed");
const auto &ss_idx = db().get_index_type<son_stats_index>().indices().get<by_owner>(); const auto &ss_idx = db().get_index_type<son_stats_index>().indices().get<by_owner>();
FC_ASSERT(ss_idx.find(op.son_id) != ss_idx.end(), "Statistic object for a given SON ID does not exists"); FC_ASSERT(ss_idx.find(op.son_id) != ss_idx.end(), "Statistic object for a given SON ID does not exists");
@ -23,9 +24,17 @@ void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_w
const auto &swwo_idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_peerplays_uid>(); const auto &swwo_idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_peerplays_uid>();
const auto swwo = swwo_idx.find(op.peerplays_uid); const auto swwo = swwo_idx.find(op.peerplays_uid);
if (swwo == swwo_idx.end()) { if (swwo == swwo_idx.end()) {
const sidechain_type sidechain = [&op]{
if(op.sidechain == sidechain_type::peerplays){
return op.withdraw_sidechain;
}
else
return op.sidechain;
}();
const auto &gpo = db().get_global_properties(); const auto &gpo = db().get_global_properties();
bool expected = false; bool expected = false;
for (auto &si : gpo.active_sons.at(op.sidechain)) { for (auto &si : gpo.active_sons.at(sidechain)) {
if (op.son_id == si.son_id) { if (op.son_id == si.son_id) {
expected = true; expected = true;
break; break;
@ -76,8 +85,16 @@ object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_w
swwo.withdraw_currency = op.withdraw_currency; swwo.withdraw_currency = op.withdraw_currency;
swwo.withdraw_amount = op.withdraw_amount; swwo.withdraw_amount = op.withdraw_amount;
const sidechain_type sidechain = [&op]{
if(op.sidechain == sidechain_type::peerplays){
return op.withdraw_sidechain;
}
else
return op.sidechain;
}();
const auto &gpo = db().get_global_properties(); const auto &gpo = db().get_global_properties();
for (auto &si : gpo.active_sons.at(op.sidechain)) { for (auto &si : gpo.active_sons.at(sidechain)) {
swwo.expected_reports.insert(std::make_pair(si.son_id, si.weight)); swwo.expected_reports.insert(std::make_pair(si.son_id, si.weight));
auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(si.son_id); auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(si.son_id);
@ -138,13 +155,17 @@ object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_w
void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_process_operation& op) void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_process_operation& op)
{ try{ { try{
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); const auto now = db().head_block_time();
FC_ASSERT(now >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
const auto& idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_id>(); const auto& idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
const auto& itr = idx.find(op.son_wallet_withdraw_id); const auto& itr = idx.find(op.son_wallet_withdraw_id);
FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found"); FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found");
FC_ASSERT(!(itr->sidechain == sidechain_type::peerplays && now >= HARDFORK_SON_FOR_ETHEREUM_TIME), "Peerplays sidechain type is not allowed");
if(itr->sidechain != sidechain_type::peerplays) {
FC_ASSERT(db().get_global_properties().active_sons.at(itr->sidechain).size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present"); FC_ASSERT(db().get_global_properties().active_sons.at(itr->sidechain).size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present");
}
FC_ASSERT(!itr->processed, "Son wallet withdraw is already processed"); FC_ASSERT(!itr->processed, "Son wallet withdraw is already processed");
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) } } FC_CAPTURE_AND_RETHROW( (op) ) }

4
libraries/plugins/peerplays_sidechain/CMakeLists.txt Executable file → Normal file
View file

@ -16,6 +16,8 @@ add_library( peerplays_sidechain
bitcoin/segwit_addr.cpp bitcoin/segwit_addr.cpp
bitcoin/utils.cpp bitcoin/utils.cpp
bitcoin/sign_bitcoin_transaction.cpp bitcoin/sign_bitcoin_transaction.cpp
bitcoin/libbitcoin_client.cpp
bitcoin/estimate_fee_external.cpp
common/rpc_client.cpp common/rpc_client.cpp
common/utils.cpp common/utils.cpp
ethereum/encoders.cpp ethereum/encoders.cpp
@ -42,7 +44,7 @@ endif()
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS) unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS)
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS CACHE) unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS CACHE)
target_link_libraries( peerplays_sidechain PRIVATE graphene_plugin sha3 zmq ) target_link_libraries( peerplays_sidechain PRIVATE curl graphene_plugin sha3 zmq bitcoin-system bitcoin-protocol bitcoin-client bitcoin-explorer )
target_include_directories( peerplays_sidechain target_include_directories( peerplays_sidechain
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )

View file

@ -242,12 +242,12 @@ bytes btc_multisig_segwit_address::get_address_bytes(const bytes &script_hash) {
} }
btc_weighted_multisig_address::btc_weighted_multisig_address(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data, btc_weighted_multisig_address::btc_weighted_multisig_address(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
network ntype) { network ntype, payment_type type) {
network_type = ntype; network_type = ntype;
this->type = type;
create_redeem_script(keys_data); create_redeem_script(keys_data);
create_witness_script(); create_witness_script();
create_segwit_address(); create_segwit_address();
type = payment_type::P2WSH;
} }
void btc_weighted_multisig_address::create_redeem_script(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) { void btc_weighted_multisig_address::create_redeem_script(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) {
@ -278,26 +278,43 @@ void btc_weighted_multisig_address::create_witness_script() {
script_builder builder; script_builder builder;
builder << op::_0; builder << op::_0;
builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size()); builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size());
witness_script_ = builder; witness_script_ = builder;
} }
void btc_weighted_multisig_address::create_segwit_address() { void btc_weighted_multisig_address::create_segwit_address() {
std::string hrp; std::string hrp;
address_types byte_version;
switch (network_type) { switch (network_type) {
case (network::mainnet): case (network::mainnet):
hrp = "bc"; hrp = "bc";
byte_version = address_types::MAINNET_SCRIPT;
break; break;
case (network::testnet): case (network::testnet):
hrp = "tb"; hrp = "tb";
byte_version = address_types::TESTNET_SCRIPT;
break; break;
case (network::regtest): case (network::regtest):
hrp = "bcrt"; hrp = "bcrt";
byte_version = address_types::TESTNET_SCRIPT;
break; break;
} }
if (type == payment_type::P2WSH) {
fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size());
std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size()); std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size());
address = segwit_addr::encode(hrp, 0, hash_data); address = segwit_addr::encode(hrp, 0, hash_data);
} else if (type == payment_type::P2SH_WSH) {
fc::sha256 hash256 = fc::sha256::hash(&witness_script_[0], witness_script_.size());
fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size());
raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size());
bytes address_bytes(1, byte_version); // 1 byte version
address_bytes.insert(address_bytes.end(), raw_address.begin(), raw_address.end());
fc::sha256 hash256_1 = fc::sha256::hash(fc::sha256::hash(address_bytes.data(), address_bytes.size()));
address_bytes.insert(address_bytes.end(), hash256_1.data(), hash256_1.data() + 4); // 4 byte checksum
address = fc::to_base58(address_bytes);
} else {
wlog("Unsupported payment type of address");
}
} }
btc_one_or_m_of_n_multisig_address::btc_one_or_m_of_n_multisig_address(const fc::ecc::public_key &user_key_data, btc_one_or_m_of_n_multisig_address::btc_one_or_m_of_n_multisig_address(const fc::ecc::public_key &user_key_data,
@ -353,12 +370,12 @@ void btc_one_or_m_of_n_multisig_address::create_segwit_address() {
btc_one_or_weighted_multisig_address::btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, btc_one_or_weighted_multisig_address::btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data,
const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
bitcoin_address::network ntype) { bitcoin_address::network ntype, payment_type type) {
network_type = ntype; network_type = ntype;
this->type = type;
create_redeem_script(user_key_data, keys_data); create_redeem_script(user_key_data, keys_data);
create_witness_script(); create_witness_script();
create_segwit_address(); create_segwit_address();
type = payment_type::P2WSH;
} }
void btc_one_or_weighted_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) { void btc_one_or_weighted_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) {
@ -398,20 +415,39 @@ void btc_one_or_weighted_multisig_address::create_witness_script() {
void btc_one_or_weighted_multisig_address::create_segwit_address() { void btc_one_or_weighted_multisig_address::create_segwit_address() {
std::string hrp; std::string hrp;
address_types byte_version;
switch (network_type) { switch (network_type) {
case (network::mainnet): case (network::mainnet):
byte_version = address_types::MAINNET_SCRIPT;
hrp = "bc"; hrp = "bc";
break; break;
case (network::testnet): case (network::testnet):
byte_version = address_types::TESTNET_SCRIPT;
hrp = "tb"; hrp = "tb";
break; break;
case (network::regtest): case (network::regtest):
byte_version = address_types::TESTNET_SCRIPT;
hrp = "bcrt"; hrp = "bcrt";
break; break;
} }
if (type == payment_type::P2WSH) {
fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size());
std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size()); std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size());
address = segwit_addr::encode(hrp, 0, hash_data); address = segwit_addr::encode(hrp, 0, hash_data);
} else if (type == payment_type::P2SH_WSH) {
fc::sha256 hash256 = fc::sha256::hash(&witness_script_[0], witness_script_.size());
fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size());
raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size());
bytes address_bytes(1, byte_version); // 1 byte version test net
address_bytes.insert(address_bytes.end(), raw_address.begin(), raw_address.end());
fc::sha256 hash256_1 = fc::sha256::hash(fc::sha256::hash(address_bytes.data(), address_bytes.size()));
address_bytes.insert(address_bytes.end(), hash256_1.data(), hash256_1.data() + 4); // 4 byte checksum
address = fc::to_base58(address_bytes);
} else {
elog("Unsupported payment type of address");
}
} }
btc_timelocked_one_or_weighted_multisig_address::btc_timelocked_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, uint32_t latency, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data, bitcoin_address::network ntype) : btc_timelocked_one_or_weighted_multisig_address::btc_timelocked_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, uint32_t latency, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data, bitcoin_address::network ntype) :

View file

@ -0,0 +1,157 @@
#include <graphene/peerplays_sidechain/bitcoin/estimate_fee_external.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <fc/log/logger.hpp>
#include <iostream>
namespace graphene {
namespace peerplays_sidechain {
static size_t writeFunction(void *ptr, size_t size, size_t nmemb, std::string *data) {
data->append((char *)ptr, size * nmemb);
return size * nmemb;
}
estimate_fee_external::estimate_fee_external() {
curl = curl_easy_init();
}
estimate_fee_external::~estimate_fee_external() {
curl_easy_cleanup(curl);
}
std::vector<std::pair<std::string, uint64_t>> estimate_fee_external::get_fee_external(uint16_t target_block) {
std::vector<std::pair<std::string, uint64_t>> estimate_fee_external_collection;
this->target_block = target_block;
for (auto &url_fee_parser : url_get_fee_parsers) {
response = get_response(url_fee_parser.first);
uint64_t fee = url_fee_parser.second();
std::string url_str = url_fee_parser.first;
if (fee != 0) {
estimate_fee_external_collection.emplace_back(std::make_pair(url_fee_parser.first, fee));
}
}
return estimate_fee_external_collection;
}
std::string estimate_fee_external::get_response(std::string url) {
std::string response;
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl, CURLOPT_USERPWD, "user:pass");
curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.42.0");
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_perform(curl);
}
return response;
}
uint64_t estimate_fee_external::parse_and_get_fee_1() {
//"https://www.bitgo.com/api/v2/btc/tx/fee"
uint64_t founded_fee = 0;
if (response.empty()) {
return founded_fee;
}
std::stringstream response_ss(response);
boost::property_tree::ptree response_pt;
boost::property_tree::read_json(response_ss, response_pt);
for (const auto &tx_child : response_pt.get_child("feeByBlockTarget")) {
const auto &block_num = tx_child.first.data();
const auto &fee = tx_child.second.data();
founded_fee = std::stoi(fee);
if (std::stoi(block_num) >= target_block) {
return founded_fee;
}
}
return founded_fee;
}
uint64_t estimate_fee_external::parse_and_get_fee_2() {
// https://bitcoiner.live/api/fees/estimates/latest
uint64_t founded_fee = 0;
if (response.empty()) {
return founded_fee;
}
std::stringstream response_ss(response);
boost::property_tree::ptree response_pt;
boost::property_tree::read_json(response_ss, response_pt);
for (const auto &tx_child : response_pt.get_child("estimates")) {
const auto &time_str = tx_child.first.data();
auto time = std::stoi(time_str);
auto block_num = time / 30;
if (tx_child.second.count("sat_per_vbyte")) {
auto founded_fee_str = tx_child.second.get_child("sat_per_vbyte").data();
founded_fee = std::stoi(founded_fee_str) * 1000;
}
if (block_num >= target_block) {
return founded_fee;
}
}
return founded_fee;
}
uint64_t estimate_fee_external::parse_and_get_fee_3() {
// https://api.blockchain.info/mempool/fees
if (response.empty()) {
return 0;
}
std::stringstream response_ss(response);
boost::property_tree::ptree response_pt;
boost::property_tree::read_json(response_ss, response_pt);
if (response_pt.get_child("limits").count("min") && response_pt.get_child("limits").count("max")) {
auto limits_min_str = response_pt.get_child("limits.min").data();
auto limits_max_str = response_pt.get_child("limits.max").data();
auto limits_min = std::stoi(limits_min_str);
auto limits_max = std::stoi(limits_max_str);
auto priority_max = (limits_max - (limits_min - 1)) / 2;
if (response_pt.count("regular") && response_pt.count("priority")) {
auto regular_str = response_pt.get_child("regular").data();
auto priority_str = response_pt.get_child("priority").data();
auto regular = std::stoi(regular_str);
auto priority = std::stoi(priority_str);
if (target_block >= priority_max) {
return regular * 1000;
} else {
return priority * 1000;
}
}
}
return 0;
}
}} // namespace graphene::peerplays_sidechain

View file

@ -0,0 +1,227 @@
#include <graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp>
#include <system_error>
#include <bitcoin/explorer/config/transaction.hpp>
#include <bitcoin/system/config/hash256.hpp>
#include <boost/xpressive/xpressive.hpp>
#include <fc/crypto/hex.hpp>
#include <fc/log/logger.hpp>
namespace graphene { namespace peerplays_sidechain {
libbitcoin_client::libbitcoin_client(std::string url) :
obelisk_client(LIBBITCOIN_SERVER_TIMEOUT, LIBBITCOIN_SERVER_RETRIES) {
std::string reg_expr = "^((?P<Protocol>https|http|tcp):\\/\\/)?(?P<Host>[a-zA-Z0-9\\-\\.]+)(:(?P<Port>\\d{1,5}))?(?P<Target>\\/.+)?";
boost::xpressive::sregex sr = boost::xpressive::sregex::compile(reg_expr);
boost::xpressive::smatch sm;
if (boost::xpressive::regex_search(url, sm, sr)) {
protocol = sm["Protocol"];
if (protocol.empty()) {
protocol = "tcp";
}
host = sm["Host"];
if (host.empty()) {
host + "localhost";
}
port = sm["Port"];
if (port.empty()) {
port = "9091";
}
}
uint16_t port_num = std::stoi(port);
std::string final_url = protocol + "://" + host;
libbitcoin::config::endpoint address(final_url, port_num);
libbitcoin::client::connection_type connection;
connection.retries = LIBBITCOIN_SERVER_RETRIES;
connection.server = address;
if (!obelisk_client.connect(connection)) {
elog("Can't connect libbitcoin for url: ${url}", ("url", final_url));
}
is_connected = true;
}
std::string libbitcoin_client::send_transaction(std::string tx) {
std::string res;
auto error_handler = [&](const std::error_code &ec) {
elog("error on sending bitcoin transaction ${error_code}", ("error_code", ec.message()));
};
auto result_handler = [&](libbitcoin::code result_code) {
ilog("result code on sending transaction ${result_code}", ("result_code", result_code.message()));
res = std::to_string(result_code.value());
};
libbitcoin::explorer::config::transaction transaction(tx);
libbitcoin::chain::transaction trx;
// This validates the tx, submits it to local tx pool, and notifies peers.
obelisk_client.transaction_pool_broadcast(error_handler, result_handler, transaction);
obelisk_client.wait();
return res;
}
libbitcoin::chain::output::list libbitcoin_client::get_transaction(std::string tx_id, std::string &tx_hash, uint32_t &confirmitions) {
libbitcoin::chain::output::list outs;
auto error_handler = [&](const std::error_code &ec) {
elog("error on fetch_trx_by_hash: ${hash} ${error_code}", ("hash", tx_id)("error_code", ec.message()));
};
auto transaction_handler = [&](const libbitcoin::chain::transaction &tx_handler) {
tx_hash = libbitcoin::config::hash256(tx_handler.hash(false)).to_string();
// TODO try to find this value (confirmitions)
confirmitions = 1;
outs = tx_handler.outputs();
};
libbitcoin::hash_digest hash = libbitcoin::config::hash256(tx_id);
// obelisk_client.blockchain_fetch_transaction (error_handler, transaction_handler,hash);
obelisk_client.blockchain_fetch_transaction2(error_handler, transaction_handler, hash);
obelisk_client.wait();
return outs;
}
std::vector<list_unspent_replay> libbitcoin_client::listunspent(std::string address, double amount) {
std::vector<list_unspent_replay> result;
auto error_handler = [&](const std::error_code &ec) {
elog("error on list_unspent ${error_code}", ("error_code", ec.message()));
};
auto replay_handler = [&](const libbitcoin::chain::points_value &points) {
for (auto &point : points.points) {
list_unspent_replay output;
output.hash = libbitcoin::config::hash256(point.hash()).to_string();
output.value = point.value();
output.index = point.index();
result.emplace_back(output);
}
};
libbitcoin::wallet::payment_address payment_address(address);
uint64_t satoshi = 100000000 * amount;
obelisk_client.blockchain_fetch_unspent_outputs(error_handler,
replay_handler, payment_address, satoshi, libbitcoin::wallet::select_outputs::algorithm::individual);
obelisk_client.wait();
return result;
}
bool libbitcoin_client::get_is_test_net() {
bool result = false;
auto error_handler = [&](const std::error_code &ec) {
elog("error on fetching genesis block ${error_code}", ("error_code", ec.message()));
};
auto block_header_handler = [&](const libbitcoin::chain::header &block_header) {
std::string hash_str = libbitcoin::config::hash256(block_header.hash()).to_string();
if (hash_str == GENESIS_TESTNET_HASH || hash_str == GENESIS_REGTEST_HASH) {
result = true;
}
};
obelisk_client.blockchain_fetch_block_header(error_handler, block_header_handler, 0);
obelisk_client.wait();
return result;
}
uint64_t libbitcoin_client::get_fee_from_trx(libbitcoin::chain::transaction trx) {
bool general_fee_est_error = false;
if (trx.is_coinbase()) {
return 0;
}
const auto total_output_value = trx.total_output_value();
// get the inputs and from inputs previous outputs
std::map<libbitcoin::hash_digest, std::vector<uint32_t>> prev_out_trxs;
for (auto &ins : trx.inputs()) {
const auto &prev_out = ins.previous_output();
prev_out_trxs[prev_out.hash()].emplace_back(prev_out.index());
}
// fetch the trx to get total input value
uint64_t total_input_value = 0;
auto transaction_handler = [&](const libbitcoin::chain::transaction &tx_handler) {
std::vector<uint32_t> indexes = prev_out_trxs[tx_handler.hash()];
for (auto &index : indexes) {
total_input_value += tx_handler.outputs()[index].value();
}
};
auto error_handler = [&](const std::error_code &ec) {
elog("error on fetching trx ${error_code}", ("error_code", ec.message()));
general_fee_est_error = true;
};
for (const auto &iter : prev_out_trxs) {
if (general_fee_est_error) {
break;
}
obelisk_client.blockchain_fetch_transaction2(error_handler, transaction_handler, iter.first);
obelisk_client.wait();
}
if (total_input_value >= total_output_value) {
return total_input_value - total_output_value;
} else {
// something is really wrong if this happens,so we are going to mark as an error
elog("On fee estimation something is wrong in total inputs and total outputs for trx hash: ${hash}",
("hash", libbitcoin::config::hash256(trx.hash()).to_string()));
return 0;
}
}
uint64_t libbitcoin_client::get_average_fee_from_trxs(std::vector<libbitcoin::chain::transaction> trx_list) {
std::vector<uint64_t> fee_per_trxs;
for (auto &trx : trx_list) {
uint64_t fee = get_fee_from_trx(trx);
if (fee > 0) {
fee_per_trxs.emplace_back(fee);
}
}
uint64_t average_estimated_fee = 0;
if (fee_per_trxs.size()) {
for (const auto &fee : fee_per_trxs) {
average_estimated_fee += fee;
}
average_estimated_fee /= fee_per_trxs.size();
}
return average_estimated_fee;
}
}} // namespace graphene::peerplays_sidechain

View file

@ -1,5 +1,6 @@
#include <graphene/peerplays_sidechain/common/utils.hpp> #include <graphene/peerplays_sidechain/common/utils.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp> #include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/binary_from_base64.hpp> #include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/transform_width.hpp> #include <boost/archive/iterators/transform_width.hpp>
@ -47,4 +48,17 @@ std::string object_id_to_string(graphene::chain::object_id_type id) {
return object_id; return object_id;
} }
graphene::chain::object_id_type string_to_object_id(const std::string &id) {
std::vector<std::string> strs;
boost::split(strs, id, boost::is_any_of("."));
if (strs.size() != 3) {
elog("Wrong object_id format: ${id}", ("id", id));
return graphene::chain::object_id_type{};
}
auto s = boost::lexical_cast<int>(strs.at(0));
auto t = boost::lexical_cast<int>(strs.at(1));
return graphene::chain::object_id_type{(uint8_t)s, (uint8_t)t, boost::lexical_cast<uint64_t>(strs.at(2))};
}
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -5,8 +5,51 @@
namespace graphene { namespace peerplays_sidechain { namespace ethereum { namespace graphene { namespace peerplays_sidechain { namespace ethereum {
//! rlp_decoder //! base_decoder
boost::multiprecision::uint256_t base_decoder::decode_uint256(const std::string &value) {
boost::multiprecision::uint256_t result = 0;
boost::multiprecision::uint256_t power(1);
uint8_t digit;
int pos = value.size() - 1;
while (pos >= 0) {
digit = 0;
if ('0' <= value[pos] && value[pos] <= '9') {
digit = value[pos] - '0';
} else if ('a' <= value[pos] && value[pos] <= 'z') {
digit = value[pos] - 'a' + 10;
}
result += digit * power;
pos--;
power *= 16;
}
return result;
}
std::string base_decoder::decode_address(const std::string &value) {
return value.substr(24, 40);
}
//! deposit_erc20_decoder
const std::string deposit_erc20_decoder::function_signature = "97feb926"; //! depositERC20(address,uint256)
fc::optional<deposit_erc20_transaction> deposit_erc20_decoder::decode(const std::string &input) {
const auto input_without_0x = remove_0x(input);
if (function_signature != input_without_0x.substr(0, 8)) {
return fc::optional<deposit_erc20_transaction>{};
}
if (input_without_0x.size() != 136) {
return fc::optional<deposit_erc20_transaction>{};
}
deposit_erc20_transaction erc_20;
erc_20.token = add_0x(base_decoder::decode_address(input_without_0x.substr(8, 64)));
erc_20.amount = base_decoder::decode_uint256(input_without_0x.substr(72, 64));
return erc_20;
}
//! rlp_decoder
namespace { namespace {
const signed char p_util_hexdigit[256] = const signed char p_util_hexdigit[256] =
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,

View file

@ -28,7 +28,7 @@ std::string base_encoder::encode_string(const std::string &value) {
//! update_owners_encoder //! update_owners_encoder
const std::string update_owners_encoder::function_signature = "23ab6adf"; //! updateOwners((address,uint256)[],string) const std::string update_owners_encoder::function_signature = "23ab6adf"; //! updateOwners((address,uint256)[],string)
std::string update_owners_encoder::encode(const std::vector<std::pair<std::string, uint16_t>> &owners_weights, const std::string &object_id) { std::string update_owners_encoder::encode(const std::vector<std::pair<std::string, uint16_t>> &owners_weights, const std::string &object_id) {
std::string data = "0x" + function_signature; std::string data = add_0x(function_signature);
data += base_encoder::encode_uint256(64); data += base_encoder::encode_uint256(64);
data += base_encoder::encode_uint256((owners_weights.size() * 2 + 3) * 32); data += base_encoder::encode_uint256((owners_weights.size() * 2 + 3) * 32);
data += base_encoder::encode_uint256(owners_weights.size()); data += base_encoder::encode_uint256(owners_weights.size());
@ -44,7 +44,7 @@ std::string update_owners_encoder::encode(const std::vector<std::pair<std::strin
//! withdrawal_encoder //! withdrawal_encoder
const std::string withdrawal_encoder::function_signature = "e088747b"; //! withdraw(address,uint256,string) const std::string withdrawal_encoder::function_signature = "e088747b"; //! withdraw(address,uint256,string)
std::string withdrawal_encoder::encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id) { std::string withdrawal_encoder::encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id) {
std::string data = "0x" + function_signature; std::string data = add_0x(function_signature);
data += base_encoder::encode_address(to); data += base_encoder::encode_address(to);
data += base_encoder::encode_uint256(amount); data += base_encoder::encode_uint256(amount);
data += base_encoder::encode_uint256(32 * 3); data += base_encoder::encode_uint256(32 * 3);
@ -53,7 +53,23 @@ std::string withdrawal_encoder::encode(const std::string &to, boost::multiprecis
return data; return data;
} }
//! withdrawal_erc20_encoder
const std::string withdrawal_erc20_encoder::function_signature = "483c0467"; //! withdrawERC20(address,address,uint256,string)
std::string withdrawal_erc20_encoder::encode(const std::string &token, const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id) {
std::string data = add_0x(function_signature);
data += base_encoder::encode_address(token);
data += base_encoder::encode_address(to);
data += base_encoder::encode_uint256(amount);
data += base_encoder::encode_uint256(32 * 4);
data += base_encoder::encode_string(object_id);
return data;
}
//! signature_encoder //! signature_encoder
const std::string update_owners_function_signature = "9d608673"; //! updateOwners((bytes,(uint8,bytes32,bytes32))[])
const std::string withdrawal_function_signature = "daac6c81"; //! withdraw((bytes,(uint8,bytes32,bytes32))[])
const std::string withdrawal_erc20_function_signature = "d2bf2866"; //! withdrawERC20((bytes,(uint8,bytes32,bytes32))[])
signature_encoder::signature_encoder(const std::string &function_hash) : signature_encoder::signature_encoder(const std::string &function_hash) :
function_signature{function_hash} { function_signature{function_hash} {
} }
@ -66,11 +82,14 @@ std::string signature_encoder::get_function_signature_from_transaction(const std
if (tr.substr(0, 8) == withdrawal_encoder::function_signature) if (tr.substr(0, 8) == withdrawal_encoder::function_signature)
return withdrawal_function_signature; return withdrawal_function_signature;
if (tr.substr(0, 8) == withdrawal_erc20_encoder::function_signature)
return withdrawal_erc20_function_signature;
return ""; return "";
} }
std::string signature_encoder::encode(const std::vector<encoded_sign_transaction> &transactions) const { std::string signature_encoder::encode(const std::vector<encoded_sign_transaction> &transactions) const {
std::string data = "0x" + function_signature; std::string data = add_0x(function_signature);
data += base_encoder::encode_uint256(32); data += base_encoder::encode_uint256(32);
data += base_encoder::encode_uint256(transactions.size()); data += base_encoder::encode_uint256(transactions.size());
size_t offset = (transactions.size()) * 32; size_t offset = (transactions.size()) * 32;

View file

@ -8,6 +8,14 @@ using namespace graphene::chain;
namespace graphene { namespace peerplays_sidechain { namespace bitcoin { namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
const bytes op_num = {0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; // OP_1 - OP_15 const bytes op_num = {0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; // OP_1 - OP_15
enum address_types { MAINNET_SCRIPT = 5,
TESTNET_SCRIPT = 196 };
enum script_op {
OP_0 = 0x00,
OP_PUSH = 0x20,
OP_SIZE_34 = 0x22
};
class bitcoin_address { class bitcoin_address {
@ -96,9 +104,6 @@ private:
void create_address(); void create_address();
public: public:
enum address_types { MAINNET_SCRIPT = 5,
TESTNET_SCRIPT = 196 };
enum { OP_0 = 0x00, enum { OP_0 = 0x00,
OP_EQUAL = 0x87, OP_EQUAL = 0x87,
OP_HASH160 = 0xa9, OP_HASH160 = 0xa9,
@ -145,7 +150,7 @@ public:
btc_weighted_multisig_address() = default; btc_weighted_multisig_address() = default;
btc_weighted_multisig_address(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data, btc_weighted_multisig_address(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
network network_type = network::regtest); network network_type = network::regtest, payment_type type = payment_type::P2SH_WSH);
bytes get_redeem_script() const { bytes get_redeem_script() const {
return redeem_script_; return redeem_script_;
@ -190,7 +195,7 @@ class btc_one_or_weighted_multisig_address : public bitcoin_address {
public: public:
btc_one_or_weighted_multisig_address() = default; btc_one_or_weighted_multisig_address() = default;
btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data, btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
network network_type = network::regtest); network network_type = network::regtest, payment_type type = payment_type::P2SH_WSH);
bytes get_redeem_script() const { bytes get_redeem_script() const {
return redeem_script_; return redeem_script_;
} }

View file

@ -0,0 +1,39 @@
#pragma once
#include <curl/curl.h>
#include <cstdint>
#include <functional>
#include <map>
#include <string>
#include <vector>
typedef std::function<uint64_t()> get_fee_func_type;
namespace graphene { namespace peerplays_sidechain {
class estimate_fee_external {
public:
estimate_fee_external();
~estimate_fee_external();
std::vector<std::pair<std::string, uint64_t>> get_fee_external(uint16_t target_block);
private:
std::string get_response(std::string url);
// Here add your custom parser for external url. Take care of incremental name
// and populate the list of url_parsers bellow paired with the function
uint64_t parse_and_get_fee_1();
uint64_t parse_and_get_fee_2();
uint64_t parse_and_get_fee_3();
const std::map<std::string, get_fee_func_type> url_get_fee_parsers{
{"https://www.bitgo.com/api/v2/btc/tx/fee", std::bind(&estimate_fee_external::parse_and_get_fee_1, this)},
{"https://bitcoiner.live/api/fees/estimates/latest", std::bind(&estimate_fee_external::parse_and_get_fee_2, this)},
{"https://api.blockchain.info/mempool/fees", std::bind(&estimate_fee_external::parse_and_get_fee_3, this)}};
std::string response;
uint16_t target_block;
CURL *curl{nullptr};
};
}} // namespace graphene::peerplays_sidechain

View file

@ -0,0 +1,53 @@
#pragma once
#include <bitcoin/client/obelisk_client.hpp>
#include <bitcoin/client/socket_stream.hpp>
#include <bitcoin/system/chain/block.hpp>
#include <boost/signals2.hpp>
#include <mutex>
#define LIBBITCOIN_SERVER_TIMEOUT (10)
#define LIBBITCOIN_SERVER_RETRIES (100)
#define DEAFULT_LIBBITCOIN_TRX_FEE (20000)
#define MAX_TRXS_IN_MEMORY_POOL (30000)
#define MIN_TRXS_IN_BUCKET (100)
#define GENESIS_MAINNET_HASH "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
#define GENESIS_TESTNET_HASH "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
#define GENESIS_REGTEST_HASH "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"
namespace graphene { namespace peerplays_sidechain {
typedef std::function<void(const libbitcoin::chain::block &)>
block_update_handler;
struct list_unspent_replay {
std::string hash;
uint64_t value;
uint32_t index;
};
class libbitcoin_client {
public:
libbitcoin_client(std::string url);
std::string send_transaction(const std::string tx);
libbitcoin::chain::output::list get_transaction(std::string tx_id, std::string &tx_hash, uint32_t &confirmitions);
std::vector<list_unspent_replay> listunspent(std::string address, double amount);
uint64_t get_average_fee_from_trxs(std::vector<libbitcoin::chain::transaction> trx_list);
uint64_t get_fee_from_trx(libbitcoin::chain::transaction trx);
bool get_is_test_net();
private:
libbitcoin::client::obelisk_client obelisk_client;
libbitcoin::protocol::zmq::identifier id;
std::string protocol;
std::string host;
std::string port;
std::string url;
bool is_connected = false;
};
}} // namespace graphene::peerplays_sidechain

View file

@ -1,4 +1,5 @@
#pragma once #pragma once
#include <fc/crypto/elliptic.hpp> #include <fc/crypto/elliptic.hpp>
#include <fc/crypto/hex.hpp> #include <fc/crypto/hex.hpp>
#include <graphene/peerplays_sidechain/bitcoin/types.hpp> #include <graphene/peerplays_sidechain/bitcoin/types.hpp>

View file

@ -8,5 +8,6 @@ std::string base64_encode(const std::string &s);
std::string base64_decode(const std::string &s); std::string base64_decode(const std::string &s);
std::string object_id_to_string(graphene::chain::object_id_type id); std::string object_id_to_string(graphene::chain::object_id_type id);
graphene::chain::object_id_type string_to_object_id(const std::string &id);
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -57,10 +57,16 @@ struct info_for_vin {
bool resend = false; bool resend = false;
}; };
enum class sidechain_event_type {
deposit,
withdrawal
};
struct sidechain_event_data { struct sidechain_event_data {
fc::time_point_sec timestamp; fc::time_point_sec timestamp;
uint32_t block_num; uint32_t block_num;
sidechain_type sidechain; sidechain_type sidechain;
sidechain_event_type type;
std::string sidechain_uid; std::string sidechain_uid;
std::string sidechain_transaction_id; std::string sidechain_transaction_id;
std::string sidechain_from; std::string sidechain_from;

View file

@ -1,10 +1,31 @@
#pragma once #pragma once
#include <boost/multiprecision/cpp_int.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
#include <fc/optional.hpp>
namespace graphene { namespace peerplays_sidechain { namespace ethereum { namespace graphene { namespace peerplays_sidechain { namespace ethereum {
class base_decoder {
public:
static boost::multiprecision::uint256_t decode_uint256(const std::string &value);
static std::string decode_address(const std::string &value);
};
struct deposit_erc20_transaction {
std::string token;
boost::multiprecision::uint256_t amount;
};
class deposit_erc20_decoder {
public:
static const std::string function_signature;
static fc::optional<deposit_erc20_transaction> decode(const std::string &input);
};
class rlp_decoder { class rlp_decoder {
private: private:
enum RLP_constants { enum RLP_constants {

View file

@ -8,9 +8,6 @@
namespace graphene { namespace peerplays_sidechain { namespace ethereum { namespace graphene { namespace peerplays_sidechain { namespace ethereum {
const std::string update_owners_function_signature = "9d608673"; //! updateOwners((bytes,(uint8,bytes32,bytes32))[])
const std::string withdrawal_function_signature = "daac6c81"; //! withdraw((bytes,(uint8,bytes32,bytes32))[])
struct encoded_sign_transaction { struct encoded_sign_transaction {
std::string data; std::string data;
signature sign; signature sign;
@ -37,6 +34,13 @@ public:
static std::string encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id); static std::string encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id);
}; };
class withdrawal_erc20_encoder {
public:
static const std::string function_signature;
static std::string encode(const std::string &token, const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id);
};
class signature_encoder { class signature_encoder {
public: public:
const std::string function_signature; const std::string function_signature;

View file

@ -59,6 +59,7 @@ protected:
sidechain_type sidechain; sidechain_type sidechain;
bool debug_rpc_calls; bool debug_rpc_calls;
bool use_bitcoind_client;
std::map<std::string, std::string> private_keys; std::map<std::string, std::string> private_keys;

View file

@ -1,18 +1,20 @@
#pragma once #pragma once
#include <graphene/peerplays_sidechain/common/rpc_client.hpp>
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp> #include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
#include <mutex>
#include <string> #include <string>
#include <thread> #include <thread>
#include <zmq_addon.hpp> #include <zmq_addon.hpp>
#include <boost/signals2.hpp> #include <boost/signals2.hpp>
#include <mutex>
#include <fc/network/http/connection.hpp> #include <fc/network/http/connection.hpp>
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp> #include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp>
#include <graphene/peerplays_sidechain/bitcoin/estimate_fee_external.hpp>
#include <graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp>
#include <graphene/peerplays_sidechain/common/rpc_client.hpp>
namespace graphene { namespace peerplays_sidechain { namespace graphene { namespace peerplays_sidechain {
@ -23,7 +25,27 @@ public:
uint64_t amount_; uint64_t amount_;
}; };
class bitcoin_rpc_client : public rpc_client { class btc_txin {
public:
std::vector<std::string> tx_address;
uint64_t tx_amount;
uint64_t tx_vout;
};
class btc_tx {
public:
std::string tx_txid;
uint32_t tx_confirmations;
std::vector<btc_txin> tx_in_list;
};
class block_data {
public:
std::string block_hash;
libbitcoin::chain::block block;
};
class bitcoin_client_base {
public: public:
enum class multi_type { enum class multi_type {
script, script,
@ -41,14 +63,47 @@ public:
std::string label; std::string label;
}; };
virtual uint64_t estimatesmartfee(uint16_t conf_target = 1) = 0;
virtual std::vector<info_for_vin> getblock(const block_data &block, int32_t verbosity = 2) = 0;
virtual btc_tx getrawtransaction(const std::string &txid, const bool verbose = false) = 0;
virtual void getnetworkinfo() = 0;
virtual std::string getblockchaininfo() = 0;
virtual 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) = 0;
virtual std::string sendrawtransaction(const std::string &tx_hex) = 0;
virtual void importmulti(const std::vector<multi_params> &address_or_script_array, const bool rescan = true) {
;
};
virtual std::string loadwallet(const std::string &filename) {
return "";
};
virtual std::string walletlock() {
return "";
};
virtual bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60) {
return false;
};
void import_trx_to_memory_pool(const libbitcoin::chain::transaction &trx) {
std::unique_lock<std::mutex> lck(libbitcoin_event_mutex);
if (trx_memory_pool.size() < MAX_TRXS_IN_MEMORY_POOL) {
trx_memory_pool.emplace_back(trx);
}
}
protected:
std::vector<libbitcoin::chain::transaction> trx_memory_pool;
std::mutex libbitcoin_event_mutex;
};
class bitcoin_rpc_client : public bitcoin_client_base, public rpc_client {
public:
public: public:
bitcoin_rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls); bitcoin_rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls);
std::string createwallet(const std::string &wallet_name); uint64_t estimatesmartfee(uint16_t conf_target = 1);
uint64_t estimatesmartfee(uint16_t conf_target = 128); std::vector<info_for_vin> getblock(const block_data &block, int32_t verbosity = 2);
std::string getblock(const std::string &block_hash, int32_t verbosity = 2); btc_tx getrawtransaction(const std::string &txid, const bool verbose = false);
std::string getrawtransaction(const std::string &txid, const bool verbose = false); void getnetworkinfo();
std::string getnetworkinfo();
std::string getblockchaininfo(); std::string getblockchaininfo();
void importmulti(const std::vector<multi_params> &address_or_script_array, const bool rescan = true); 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(const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
@ -60,35 +115,84 @@ public:
private: private:
std::string ip; std::string ip;
uint32_t rpc_port;
std::string user; std::string user;
std::string password; std::string password;
std::string wallet_name; std::string wallet_name;
std::string wallet_password; std::string wallet_password;
uint32_t bitcoin_major_version;
};
class bitcoin_libbitcoin_client : public bitcoin_client_base, public libbitcoin_client {
public:
bitcoin_libbitcoin_client(std::string url);
uint64_t estimatesmartfee(uint16_t conf_target = 1);
std::vector<info_for_vin> getblock(const block_data &block, int32_t verbosity = 2);
btc_tx getrawtransaction(const std::string &txid, const bool verbose = false);
void getnetworkinfo();
std::string getblockchaininfo();
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 sendrawtransaction(const std::string &tx_hex);
private:
bool is_test_net = false;
std::unique_ptr<estimate_fee_external> estimate_fee_ext;
uint64_t current_internal_fee = DEAFULT_LIBBITCOIN_TRX_FEE;
}; };
// ============================================================================= // =============================================================================
class zmq_listener { class zmq_listener_base {
public: public:
zmq_listener(std::string _ip, uint32_t _zmq); virtual ~zmq_listener_base(){};
virtual ~zmq_listener(); zmq_listener_base(std::string _ip, uint32_t _block_zmq_port, uint32_t _trx_zmq_port = 0) {
ip = _ip;
block_zmq_port = _block_zmq_port;
trx_zmq_port = _trx_zmq_port;
stopped = false;
};
virtual void start() = 0;
boost::signals2::signal<void(const block_data &)> block_event_received;
boost::signals2::signal<void(const libbitcoin::chain::transaction &)> trx_event_received;
protected:
std::string ip;
uint32_t block_zmq_port;
uint32_t trx_zmq_port;
std::atomic_bool stopped;
std::thread block_thr;
std::thread trx_thr;
};
class zmq_listener : public zmq_listener_base {
public:
zmq_listener(std::string _ip, uint32_t _block_zmq_port, uint32_t _trx_zmq_port = 0);
virtual ~zmq_listener();
void start(); void start();
boost::signals2::signal<void(const std::string &)> event_received;
private: private:
void handle_zmq(); void handle_zmq();
std::vector<zmq::message_t> receive_multipart(); std::vector<zmq::message_t> receive_multipart();
std::string ip;
uint32_t zmq_port;
zmq::context_t ctx; zmq::context_t ctx;
zmq::socket_t socket; zmq::socket_t socket;
};
std::atomic_bool stopped; class zmq_listener_libbitcoin : public zmq_listener_base {
std::thread thr; public:
zmq_listener_libbitcoin(std::string _ip, uint32_t _block_zmq_port = 9093, uint32_t _trx_zmq_port = 9094);
virtual ~zmq_listener_libbitcoin();
void start();
private:
void handle_block();
void handle_trx();
libbitcoin::protocol::zmq::context block_context;
libbitcoin::protocol::zmq::socket block_socket;
libbitcoin::protocol::zmq::poller block_poller;
libbitcoin::protocol::zmq::context trx_context;
libbitcoin::protocol::zmq::socket trx_socket;
libbitcoin::protocol::zmq::poller trx_poller;
}; };
// ============================================================================= // =============================================================================
@ -109,16 +213,19 @@ public:
virtual optional<asset> estimate_withdrawal_transaction_fee() const override; virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
private: private:
std::string ip; std::string bitcoin_node_ip;
uint32_t zmq_port; std::string libbitcoin_server_ip;
uint32_t libbitcoin_block_zmq_port;
uint32_t libbitcoin_trx_zmq_port;
uint32_t bitcoin_node_zmq_port;
uint32_t rpc_port; uint32_t rpc_port;
std::string rpc_user; std::string rpc_user;
std::string rpc_password; std::string rpc_password;
std::string wallet_name; std::string wallet_name;
std::string wallet_password; std::string wallet_password;
std::unique_ptr<bitcoin_rpc_client> bitcoin_client; std::unique_ptr<bitcoin_client_base> bitcoin_client;
std::unique_ptr<zmq_listener> listener; std::unique_ptr<zmq_listener_base> listener;
fc::future<void> on_changed_objects_task; fc::future<void> on_changed_objects_task;
@ -128,7 +235,7 @@ private:
std::mutex event_handler_mutex; std::mutex event_handler_mutex;
typedef std::lock_guard<decltype(event_handler_mutex)> scoped_lock; typedef std::lock_guard<decltype(event_handler_mutex)> scoped_lock;
std::string create_primary_wallet_address(const std::vector<son_info> &son_pubkeys); std::string create_primary_wallet_address(const std::vector<son_sidechain_info> &son_pubkeys);
std::string create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address); std::string create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address);
std::string create_deposit_transaction(const son_wallet_deposit_object &swdo); std::string create_deposit_transaction(const son_wallet_deposit_object &swdo);
@ -138,9 +245,9 @@ private:
std::string sign_transaction(const sidechain_transaction_object &sto); std::string sign_transaction(const sidechain_transaction_object &sto);
std::string send_transaction(const sidechain_transaction_object &sto); std::string send_transaction(const sidechain_transaction_object &sto);
void handle_event(const std::string &event_data); void block_handle_event(const block_data &event_data);
void trx_handle_event(const libbitcoin::chain::transaction &event_data);
std::string get_redeemscript_for_userdeposit(const std::string &user_address); std::string get_redeemscript_for_userdeposit(const std::string &user_address);
std::vector<info_for_vin> extract_info_from_block(const std::string &_block);
void on_changed_objects(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts); void on_changed_objects(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts);
void on_changed_objects_cb(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts); void on_changed_objects_cb(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts);
}; };

View file

@ -4,6 +4,7 @@
#include <string> #include <string>
#include <boost/bimap.hpp>
#include <boost/signals2.hpp> #include <boost/signals2.hpp>
#include <graphene/peerplays_sidechain/common/rpc_client.hpp> #include <graphene/peerplays_sidechain/common/rpc_client.hpp>
@ -52,18 +53,22 @@ public:
bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount);
virtual optional<asset> estimate_withdrawal_transaction_fee() const override; virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
private:
using bimap_type = boost::bimap<std::string, std::string>;
private: private:
std::string rpc_url; std::string rpc_url;
std::string rpc_user; std::string rpc_user;
std::string rpc_password; std::string rpc_password;
std::string wallet_contract_address; std::string wallet_contract_address;
bimap_type erc20_addresses;
ethereum_rpc_client *rpc_client; ethereum_rpc_client *rpc_client;
ethereum::chain_id_type chain_id; ethereum::chain_id_type chain_id;
ethereum::network_id_type network_id; ethereum::network_id_type network_id;
std::string create_primary_wallet_transaction(const std::vector<son_info> &son_pubkeys, const std::string &object_id); std::string create_primary_wallet_transaction(const std::vector<son_sidechain_info> &son_pubkeys, const std::string &object_id);
std::string create_deposit_transaction(const son_wallet_deposit_object &swdo); std::string create_deposit_transaction(const son_wallet_deposit_object &swdo);
std::string create_withdrawal_transaction(const son_wallet_withdraw_object &swwo); std::string create_withdrawal_transaction(const son_wallet_withdraw_object &swwo);

View file

@ -116,21 +116,21 @@ peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidec
sidechain_enabled_peerplays(false), sidechain_enabled_peerplays(false),
current_son_id([] { current_son_id([] {
std::map<sidechain_type, son_id_type> current_son_id; std::map<sidechain_type, son_id_type> current_son_id;
for (const auto &active_sidechain_type : active_sidechain_types) { for (const auto &active_sidechain_type : all_sidechain_types) {
current_son_id.emplace(active_sidechain_type, son_id_type(std::numeric_limits<uint32_t>().max())); current_son_id.emplace(active_sidechain_type, son_id_type(std::numeric_limits<uint32_t>().max()));
} }
return current_son_id; return current_son_id;
}()), }()),
sidechain_enabled([] { sidechain_enabled([] {
std::map<sidechain_type, bool> sidechain_enabled; std::map<sidechain_type, bool> sidechain_enabled;
for (const auto &active_sidechain_type : active_sidechain_types) { for (const auto &active_sidechain_type : all_sidechain_types) {
sidechain_enabled.emplace(active_sidechain_type, false); sidechain_enabled.emplace(active_sidechain_type, false);
} }
return sidechain_enabled; return sidechain_enabled;
}()), }()),
net_handlers([] { net_handlers([] {
std::map<sidechain_type, std::unique_ptr<sidechain_net_handler>> net_handlers; std::map<sidechain_type, std::unique_ptr<sidechain_net_handler>> net_handlers;
for (const auto &active_sidechain_type : active_sidechain_types) { for (const auto &active_sidechain_type : all_sidechain_types) {
net_handlers.emplace(active_sidechain_type, nullptr); net_handlers.emplace(active_sidechain_type, nullptr);
} }
return net_handlers; return net_handlers;
@ -149,7 +149,7 @@ peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() {
} }
try { try {
for (const auto &active_sidechain_type : active_sidechain_types) { for (const auto &active_sidechain_type : all_sidechain_types) {
if (_son_processing_task.count(active_sidechain_type) != 0 && _son_processing_task.at(active_sidechain_type).valid()) if (_son_processing_task.count(active_sidechain_type) != 0 && _son_processing_task.at(active_sidechain_type).valid())
_son_processing_task.at(active_sidechain_type).wait(); _son_processing_task.at(active_sidechain_type).wait();
} }
@ -177,6 +177,10 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options(
cli.add_options()("debug-rpc-calls", bpo::value<bool>()->default_value(false), "Outputs RPC calls to console"); cli.add_options()("debug-rpc-calls", bpo::value<bool>()->default_value(false), "Outputs RPC calls to console");
cli.add_options()("bitcoin-sidechain-enabled", bpo::value<bool>()->default_value(false), "Bitcoin sidechain handler enabled"); cli.add_options()("bitcoin-sidechain-enabled", bpo::value<bool>()->default_value(false), "Bitcoin sidechain handler enabled");
cli.add_options()("use-bitcoind-client", bpo::value<bool>()->default_value(false), "Use bitcoind client instead of libbitcoin client");
cli.add_options()("libbitcoin-server-ip", bpo::value<string>()->default_value("127.0.0.1"), "Libbitcoin server IP address");
cli.add_options()("libbitcoin-server-block-zmq-port", bpo::value<uint32_t>()->default_value(9093), "Block ZMQ port of libbitcoin server");
cli.add_options()("libbitcoin-server-trx-zmq-port", bpo::value<uint32_t>()->default_value(9094), "Trx ZMQ port of libbitcoin server");
cli.add_options()("bitcoin-node-ip", bpo::value<string>()->default_value("127.0.0.1"), "IP address of Bitcoin node"); cli.add_options()("bitcoin-node-ip", bpo::value<string>()->default_value("127.0.0.1"), "IP address of Bitcoin node");
cli.add_options()("bitcoin-node-zmq-port", bpo::value<uint32_t>()->default_value(11111), "ZMQ port of Bitcoin node"); cli.add_options()("bitcoin-node-zmq-port", bpo::value<uint32_t>()->default_value(11111), "ZMQ port of Bitcoin node");
cli.add_options()("bitcoin-node-rpc-port", bpo::value<uint32_t>()->default_value(8332), "RPC port of Bitcoin node"); cli.add_options()("bitcoin-node-rpc-port", bpo::value<uint32_t>()->default_value(8332), "RPC port of Bitcoin node");
@ -192,6 +196,8 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options(
cli.add_options()("ethereum-node-rpc-user", bpo::value<string>(), "Ethereum RPC user"); cli.add_options()("ethereum-node-rpc-user", bpo::value<string>(), "Ethereum RPC user");
cli.add_options()("ethereum-node-rpc-password", bpo::value<string>(), "Ethereum RPC password"); cli.add_options()("ethereum-node-rpc-password", bpo::value<string>(), "Ethereum RPC password");
cli.add_options()("ethereum-wallet-contract-address", bpo::value<string>(), "Ethereum wallet contract address"); cli.add_options()("ethereum-wallet-contract-address", bpo::value<string>(), "Ethereum wallet contract address");
cli.add_options()("ethereum-erc-20-address", bpo::value<vector<string>>()->composing()->multitoken(),
"Tuple of [ERC-20 symbol, ERC-20 address] (may specify multiple times)");
cli.add_options()("ethereum-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", "9bedac2bd8fe2a6f6528e066c67fc8ac0622e96828d40c0e820d83c5bd2b0589")), cli.add_options()("ethereum-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", "9bedac2bd8fe2a6f6528e066c67fc8ac0622e96828d40c0e820d83c5bd2b0589")),
"Tuple of [Ethereum public key, Ethereum private key] (may specify multiple times)"); "Tuple of [Ethereum public key, Ethereum private key] (may specify multiple times)");
@ -247,12 +253,14 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt
} }
sidechain_enabled_bitcoin = options.at("bitcoin-sidechain-enabled").as<bool>(); sidechain_enabled_bitcoin = options.at("bitcoin-sidechain-enabled").as<bool>();
config_ready_bitcoin = options.count("bitcoin-node-ip") &&
options.count("bitcoin-node-zmq-port") && options.count("bitcoin-node-rpc-port") && config_ready_bitcoin = (((options.count("libbitcoin-server-ip") && options.count("libbitcoin-server-zmq-port")) ||
options.count("bitcoin-node-rpc-user") && options.count("bitcoin-node-rpc-password") && (options.count("bitcoin-node-ip") && options.count("bitcoin-node-zmq-port") &&
options.count("bitcoin-wallet-name") && options.count("bitcoin-wallet-password") && options.count("bitcoin-node-rpc-port") && options.count("bitcoin-node-rpc-user") &&
options.count("bitcoin-private-key"); options.count("bitcoin-node-rpc-password") && options.count("bitcoin-wallet-name") &&
if (sidechain_enabled_bitcoin && !config_ready_bitcoin) { options.count("bitcoin-wallet-password"))) &&
options.count("bitcoin-private-key"));
if (!config_ready_bitcoin) {
wlog("Haven't set up Bitcoin sidechain parameters"); wlog("Haven't set up Bitcoin sidechain parameters");
} }
@ -360,7 +368,7 @@ bool peerplays_sidechain_plugin_impl::is_active_son(sidechain_type sidechain, so
set<son_id_type> active_son_ids; set<son_id_type> active_son_ids;
std::transform(gpo.active_sons.at(sidechain).cbegin(), gpo.active_sons.at(sidechain).cend(), std::transform(gpo.active_sons.at(sidechain).cbegin(), gpo.active_sons.at(sidechain).cend(),
std::inserter(active_son_ids, active_son_ids.end()), std::inserter(active_son_ids, active_son_ids.end()),
[](const son_info &swi) { [](const son_sidechain_info &swi) {
return swi.son_id; return swi.son_id;
}); });
@ -413,7 +421,7 @@ bool peerplays_sidechain_plugin_impl::is_son_down_op_valid(const chain::operatio
status_son_down_op_valid = false; status_son_down_op_valid = false;
} }
if (status_son_down_op_valid) { if (status_son_down_op_valid) {
for (const auto &active_sidechain_type : active_sidechain_types) { for (const auto &active_sidechain_type : active_sidechain_types(d.head_block_time())) {
if (stats.last_active_timestamp.contains(active_sidechain_type)) { if (stats.last_active_timestamp.contains(active_sidechain_type)) {
const fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(active_sidechain_type) > last_maintenance_time) ? stats.last_active_timestamp.at(active_sidechain_type) : last_maintenance_time); const fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(active_sidechain_type) > last_maintenance_time) ? stats.last_active_timestamp.at(active_sidechain_type) : last_maintenance_time);
if (((fc::time_point::now() - last_active_ts) <= fc::seconds(down_threshold))) { if (((fc::time_point::now() - last_active_ts) <= fc::seconds(down_threshold))) {
@ -466,7 +474,7 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() {
//! Check that son is active (at least for one sidechain_type) //! Check that son is active (at least for one sidechain_type)
bool is_son_active = false; bool is_son_active = false;
for (const auto &active_sidechain_type : active_sidechain_types) { for (const auto &active_sidechain_type : active_sidechain_types(d.head_block_time())) {
if (sidechain_enabled.at(active_sidechain_type)) { if (sidechain_enabled.at(active_sidechain_type)) {
if (is_active_son(active_sidechain_type, son_id)) if (is_active_son(active_sidechain_type, son_id))
is_son_active = true; is_son_active = true;
@ -504,7 +512,7 @@ void peerplays_sidechain_plugin_impl::schedule_son_processing() {
const auto next_wakeup = now + std::chrono::microseconds(time_to_next_son_processing); const auto next_wakeup = now + std::chrono::microseconds(time_to_next_son_processing);
for (const auto &active_sidechain_type : active_sidechain_types) { for (const auto &active_sidechain_type : active_sidechain_types(plugin.database().head_block_time())) {
if (_son_processing_task.count(active_sidechain_type) != 0 && _son_processing_task.at(active_sidechain_type).wait_for(std::chrono::seconds{0}) != std::future_status::ready) { if (_son_processing_task.count(active_sidechain_type) != 0 && _son_processing_task.at(active_sidechain_type).wait_for(std::chrono::seconds{0}) != std::future_status::ready) {
wlog("Son doesn't process in time for sidechain: ${active_sidechain_type}", ("active_sidechain_type", active_sidechain_type)); wlog("Son doesn't process in time for sidechain: ${active_sidechain_type}", ("active_sidechain_type", active_sidechain_type));
_son_processing_task.at(active_sidechain_type).wait(); _son_processing_task.at(active_sidechain_type).wait();
@ -621,7 +629,7 @@ bool peerplays_sidechain_plugin_impl::can_son_participate(sidechain_type sidecha
std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin_impl::get_son_listener_log() { std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin_impl::get_son_listener_log() {
std::map<sidechain_type, std::vector<std::string>> result; std::map<sidechain_type, std::vector<std::string>> result;
for (const auto &active_sidechain_type : active_sidechain_types) { for (const auto &active_sidechain_type : active_sidechain_types(plugin.database().head_block_time())) {
if (net_handlers.at(active_sidechain_type)) { if (net_handlers.at(active_sidechain_type)) {
result.emplace(active_sidechain_type, net_handlers.at(active_sidechain_type)->get_son_listener_log()); result.emplace(active_sidechain_type, net_handlers.at(active_sidechain_type)->get_son_listener_log());
} }
@ -630,6 +638,11 @@ std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin_im
} }
optional<asset> peerplays_sidechain_plugin_impl::estimate_withdrawal_transaction_fee(sidechain_type sidechain) { optional<asset> peerplays_sidechain_plugin_impl::estimate_withdrawal_transaction_fee(sidechain_type sidechain) {
if (net_handlers.count(sidechain) == 0) {
wlog("No net handler for sidechain: ${sidechain}", ("sidechain", sidechain));
return optional<asset>();
}
if (!net_handlers.at(sidechain)) { if (!net_handlers.at(sidechain)) {
wlog("Net handler is null for sidechain: ${sidechain}", ("sidechain", sidechain)); wlog("Net handler is null for sidechain: ${sidechain}", ("sidechain", sidechain));
return optional<asset>(); return optional<asset>();

View file

@ -190,22 +190,25 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
// (sed.sidechain_currency.compare("HIVE") != 0); // (sed.sidechain_currency.compare("HIVE") != 0);
#endif #endif
bool deposit_condition = (sed.peerplays_to == gpo.parameters.son_account()) && const bool deposit_condition = (sed.peerplays_to == gpo.parameters.son_account()) &&
(sed.sidechain == sidechain) &&
(sed.type == sidechain_event_type::deposit) &&
(((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency.compare("BTC") == 0)) || (((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency.compare("BTC") == 0)) ||
((sed.sidechain == sidechain_type::ethereum) && (sed.sidechain_currency.compare("ETH") == 0)) || ((sed.sidechain == sidechain_type::ethereum) && (!sed.sidechain_currency.empty())) ||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HBD") == 0)) || ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HBD") == 0)) ||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) || ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) ||
enable_peerplays_asset_deposits); enable_peerplays_asset_deposits);
bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain == sidechain) && //! Fixme -> sidechain_type::peerplays const bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) &&
((sed.sidechain_currency == object_id_to_string(gpo.parameters.btc_asset())) || (sed.sidechain == sidechain) &&
(sed.sidechain_currency == object_id_to_string(gpo.parameters.eth_asset())) || (sed.type == sidechain_event_type::withdrawal) &&
(sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset())) || (((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency == object_id_to_string(gpo.parameters.btc_asset()))) ||
(sed.sidechain_currency == object_id_to_string(gpo.parameters.hive_asset()))); ((sed.sidechain == sidechain_type::ethereum) && (!sed.sidechain_currency.empty())) ||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset()))) ||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency == object_id_to_string(gpo.parameters.hive_asset()))));
// Deposit request // Deposit request
if (deposit_condition) { if (deposit_condition) {
for (son_id_type son_id : plugin.get_sons()) { for (son_id_type son_id : plugin.get_sons()) {
if (plugin.is_active_son(sidechain, son_id)) { if (plugin.is_active_son(sidechain, son_id)) {
@ -269,9 +272,19 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
withdraw_currency_price = database.get<asset_object>(database.get_global_properties().parameters.hive_asset()).options.core_exchange_rate; withdraw_currency_price = database.get<asset_object>(database.get_global_properties().parameters.hive_asset()).options.core_exchange_rate;
} }
if (withdraw_currency.empty()) { if (withdraw_currency.empty()) {
//! This is ERC-20 withdrawal
const auto asset_object_id = string_to_object_id(sed.sidechain_currency);
const auto &assets_by_id = database.get_index_type<asset_index>().indices().get<by_id>();
const auto asset_itr = assets_by_id.find(asset_object_id);
if (asset_itr == assets_by_id.end()) {
wlog("Could not find asset: ${asset_object_id}", ("asset_object_id", asset_object_id));
return; return;
} }
withdraw_currency = asset_itr->symbol;
withdraw_currency_price = asset_itr->options.core_exchange_rate;
}
for (son_id_type son_id : plugin.get_sons()) { for (son_id_type son_id : plugin.get_sons()) {
if (plugin.is_active_son(sidechain, son_id)) { if (plugin.is_active_son(sidechain, son_id)) {
@ -663,9 +676,10 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) {
continue; continue;
} }
bool is_tracked_asset = const bool is_tracked_asset =
((sidechain == sidechain_type::bitcoin) && (transfer_op.amount.asset_id == gpo.parameters.btc_asset())) || ((sidechain == sidechain_type::bitcoin) && (transfer_op.amount.asset_id == gpo.parameters.btc_asset())) ||
((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id == gpo.parameters.eth_asset())) || ((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id == gpo.parameters.eth_asset())) ||
(sidechain == sidechain_type::ethereum) ||
((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hbd_asset())) || ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hbd_asset())) ||
((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hive_asset())); ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hive_asset()));
@ -691,7 +705,8 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) {
sidechain_event_data sed; sidechain_event_data sed;
sed.timestamp = database.head_block_time(); sed.timestamp = database.head_block_time();
sed.block_num = database.head_block_num(); sed.block_num = database.head_block_num();
sed.sidechain = sidechain; //! Fixme -> sidechain_type::peerplays sed.sidechain = sidechain;
sed.type = sidechain_event_type::withdrawal;
sed.sidechain_uid = sidechain_uid; sed.sidechain_uid = sidechain_uid;
sed.sidechain_transaction_id = trx.id().str(); sed.sidechain_transaction_id = trx.id().str();
sed.sidechain_from = sidechain_from; sed.sidechain_from = sidechain_from;

View file

@ -13,7 +13,7 @@
#include <graphene/chain/account_object.hpp> #include <graphene/chain/account_object.hpp>
#include <graphene/chain/protocol/son_wallet.hpp> #include <graphene/chain/protocol/son_wallet.hpp>
#include <graphene/chain/sidechain_transaction_object.hpp> #include <graphene/chain/sidechain_transaction_object.hpp>
#include <graphene/chain/son_info.hpp> #include <graphene/chain/son_sidechain_info.hpp>
#include <graphene/chain/son_wallet_object.hpp> #include <graphene/chain/son_wallet_object.hpp>
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp> #include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp>
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_transaction.hpp> #include <graphene/peerplays_sidechain/bitcoin/bitcoin_transaction.hpp>
@ -29,11 +29,6 @@ bitcoin_rpc_client::bitcoin_rpc_client(std::string _url, std::string _user, std:
rpc_client(_url, _user, _password, _debug_rpc_calls) { rpc_client(_url, _user, _password, _debug_rpc_calls) {
} }
std::string bitcoin_rpc_client::createwallet(const std::string &wallet_name) {
const std::string params = std::string("[\"") + wallet_name + std::string("\"]");
return send_post_request("createwallet", params, debug_rpc_calls);
}
uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) { uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) {
const std::string params = std::string("[") + std::to_string(conf_target) + std::string("]"); const std::string params = std::string("[") + std::to_string(conf_target) + std::string("]");
const std::string str = send_post_request("estimatesmartfee", params, debug_rpc_calls); const std::string str = send_post_request("estimatesmartfee", params, debug_rpc_calls);
@ -58,36 +53,130 @@ uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) {
return 20000; return 20000;
} }
std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t verbosity) { std::vector<info_for_vin> bitcoin_rpc_client::getblock(const block_data &block, int32_t verbosity) {
const std::string params = std::string("[\"") + block_hash + std::string("\",") + std::to_string(verbosity) + std::string("]"); std::string params = std::string("[\"") + block.block_hash + std::string("\",") + std::to_string(verbosity) + std::string("]");
return send_post_request("getblock", params, debug_rpc_calls); std::string str = send_post_request("getblock", params, debug_rpc_calls);
std::vector<info_for_vin> result;
if (str.empty()) {
return result;
}
std::stringstream ss(str);
boost::property_tree::ptree json;
boost::property_tree::read_json(ss, json);
auto json_result = json.get_child_optional("result");
for (const auto &tx_child : json_result.get().get_child("tx")) {
const auto &tx = tx_child.second;
for (const auto &o : tx.get_child("vout")) {
const auto script = o.second.get_child("scriptPubKey");
std::vector<std::string> address_list;
if (script.count("address")) {
address_list.emplace_back(script.get<std::string>("address"));
} else if (script.count("addresses")) {
for (const auto &addr : script.get_child("addresses")) {
address_list.emplace_back(addr.second.get_value<std::string>());
}
} else {
continue;
}
for (auto &address : address_list) {
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>();
amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end());
vin.out.amount = std::stoll(amount);
vin.out.n_vout = o.second.get_child("n").get_value<uint32_t>();
vin.address = address_base58;
result.push_back(vin);
}
}
}
return result;
} }
std::string bitcoin_rpc_client::getnetworkinfo() { void bitcoin_rpc_client::getnetworkinfo() {
static const std::string params = std::string("[]"); std::string params = std::string("[]");
return send_post_request("getnetworkinfo", params, debug_rpc_calls); std::string str = send_post_request("getnetworkinfo", params, debug_rpc_calls);
std::stringstream network_info_ss(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));
} }
std::string bitcoin_rpc_client::getrawtransaction(const std::string &txid, const bool verbose) { btc_tx bitcoin_rpc_client::getrawtransaction(const std::string &txid, const bool verbose) {
const std::string params = std::string("[\"") + txid + std::string("\",") + (verbose ? "true" : "false") + std::string("]"); std::string params = std::string("[\"") + txid + std::string("\",") + (verbose ? "true" : "false") + std::string("]");
return send_post_request("getrawtransaction", params, debug_rpc_calls); std::string str = send_post_request("getrawtransaction", params, debug_rpc_calls);
btc_tx tx;
std::stringstream tx_ss(str);
boost::property_tree::ptree tx_json;
boost::property_tree::read_json(tx_ss, tx_json);
if (tx_json.count("error") && tx_json.get_child("error").empty()) {
std::string tx_txid = tx_json.get<std::string>("result.txid");
uint32_t tx_confirmations = tx_json.get<uint32_t>("result.confirmations");
tx.tx_txid = tx_txid;
tx.tx_confirmations = tx_confirmations;
for (auto &input : tx_json.get_child("result.vout")) {
btc_txin tx_in;
std::string tx_vout_s = input.second.get<std::string>("n");
tx_in.tx_vout = std::stoll(tx_vout_s);
if (bitcoin_major_version > 21) {
std::string address = input.second.get<std::string>("scriptPubKey.address");
tx_in.tx_address.emplace_back(address);
} else {
for (auto &address : input.second.get_child("scriptPubKey.addresses")) {
tx_in.tx_address.emplace_back(address.second.data());
}
}
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_in.tx_amount = std::stoll(tx_amount_s);
tx.tx_in_list.emplace_back(tx_in);
}
}
return tx;
} }
std::string bitcoin_rpc_client::getblockchaininfo() { std::string bitcoin_rpc_client::getblockchaininfo() {
static const std::string params = std::string("[]"); static const std::string params = std::string("[]");
const std::string str = send_post_request("getblockchaininfo", params, debug_rpc_calls); const std::string str = send_post_request("getblockchaininfo", params, debug_rpc_calls);
std::string result;
if (str.length() > 0) { if (str.length() > 0) {
std::stringstream ss(str); std::stringstream ss(str);
boost::property_tree::ptree json; boost::property_tree::ptree json;
boost::property_tree::read_json(ss, json); boost::property_tree::read_json(ss, json);
boost::property_tree::json_parser::write_json(ss, json.get_child("result")); if (json.find("result") != json.not_found()) {
return ss.str(); auto json_result = json.get_child("result");
if (json_result.count("chain")) {
result = json_result.get<std::string>("chain");
}
}
} }
return str; return result;
} }
void bitcoin_rpc_client::importmulti(const std::vector<multi_params> &address_or_script_array, const bool rescan) { void bitcoin_rpc_client::importmulti(const std::vector<multi_params> &address_or_script_array, const bool rescan) {
@ -243,15 +332,178 @@ bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_
else else
return true; return true;
} }
bitcoin_libbitcoin_client::bitcoin_libbitcoin_client(std::string url) :
libbitcoin_client(url) {
estimate_fee_ext = std::unique_ptr<estimate_fee_external>(new estimate_fee_external());
}
uint64_t bitcoin_libbitcoin_client::estimatesmartfee(uint16_t conf_target) {
std::vector<std::pair<std::string, uint64_t>> fees = estimate_fee_ext->get_fee_external(conf_target);
std::vector<uint64_t> accumulated_fees;
for (auto &external_fees : fees) {
if (external_fees.second != 0) {
accumulated_fees.emplace_back(external_fees.second);
}
}
// we will rather pick minimal fee from external sources than internal fee calculation
if (accumulated_fees.empty()) {
accumulated_fees.emplace_back(current_internal_fee);
}
return *std::min_element(accumulated_fees.begin(), accumulated_fees.end());
}
std::vector<info_for_vin> bitcoin_libbitcoin_client::getblock(const block_data &block, int32_t verbosity) {
std::unique_lock<std::mutex> lck(libbitcoin_event_mutex);
// estimate fee
const auto &block_trxs = block.block.transactions();
std::vector<libbitcoin::chain::transaction> bucket_trxs;
for (auto &mem_pool_trx : trx_memory_pool) {
for (auto &trx : block_trxs) {
if (mem_pool_trx.hash() == trx.hash()) {
bucket_trxs.emplace_back(mem_pool_trx);
break;
}
}
}
uint64_t average_fee = get_average_fee_from_trxs(bucket_trxs);
if (average_fee > 0 && bucket_trxs.size() >= MIN_TRXS_IN_BUCKET) {
current_internal_fee = average_fee;
}
// We could consider accumulation which could spread to multiple blocks for better metric
// for now we only keep tracking for not confirmed transaction until we get next block
trx_memory_pool.clear();
std::vector<info_for_vin> result;
const libbitcoin::chain::transaction::list trx_list = block.block.transactions();
for (const auto &tx : trx_list) {
uint32_t vout_seq = 0;
for (const auto &o : tx.outputs()) {
std::vector<std::string> address_list;
libbitcoin::wallet::payment_address::list addresses;
if (is_test_net) {
addresses = o.addresses(libbitcoin::wallet::payment_address::testnet_p2kh,
libbitcoin::wallet::payment_address::testnet_p2sh);
} else {
addresses = o.addresses();
}
for (auto &payment_address : addresses) {
std::stringstream ss;
ss << payment_address;
address_list.emplace_back(ss.str());
}
// addres list consists usual of one element
for (auto &address : address_list) {
const auto address_base58 = address;
info_for_vin vin;
vin.out.hash_tx = libbitcoin::config::hash256(tx.hash()).to_string();
vin.out.amount = std::floor(o.value());
vin.out.n_vout = vout_seq;
vin.address = address_base58;
result.push_back(vin);
}
vout_seq++;
}
}
return result;
}
btc_tx bitcoin_libbitcoin_client::getrawtransaction(const std::string &txid, const bool verbose) {
btc_tx tx;
std::string tx_hash;
uint32_t confirmitions;
libbitcoin::chain::output::list outs = get_transaction(txid, tx_hash, confirmitions);
if (tx_hash.empty()) {
return tx;
}
tx.tx_txid = tx_hash;
tx.tx_confirmations = confirmitions;
uint64_t tx_vout_sequence = 0;
for (auto &out : outs) {
btc_txin tx_in;
tx_in.tx_vout = tx_vout_sequence++;
libbitcoin::wallet::payment_address::list addresses;
if (is_test_net) {
addresses = out.addresses(libbitcoin::wallet::payment_address::testnet_p2kh,
libbitcoin::wallet::payment_address::testnet_p2sh);
} else {
addresses = out.addresses();
}
for (auto &address : addresses) {
std::stringstream ss;
ss << address;
tx_in.tx_address.emplace_back(ss.str());
}
tx_in.tx_amount = std::floor(out.value());
tx.tx_in_list.emplace_back(tx_in);
}
return tx;
}
void bitcoin_libbitcoin_client::getnetworkinfo() {
// This function is only used for bitcoind client in order of getting
// version of bitcoin client. Version is used for extracting addresses or address
// which is not important for libbitcoin client
}
std::string bitcoin_libbitcoin_client::getblockchaininfo() {
if (get_is_test_net()) {
is_test_net = true;
return "regtest";
}
return "";
}
std::vector<btc_txout> bitcoin_libbitcoin_client::listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf, const uint32_t maxconf) {
std::vector<btc_txout> result;
std::vector<list_unspent_replay> outputs = listunspent(address, transfer_amount);
for (auto &output : outputs) {
btc_txout txo;
txo.txid_ = output.hash;
txo.out_num_ = output.index;
txo.amount_ = output.value;
result.push_back(txo);
}
return result;
}
std::string bitcoin_libbitcoin_client::sendrawtransaction(const std::string &tx_hex) {
std::string res = send_transaction(tx_hex);
return res;
}
// ============================================================================= // =============================================================================
zmq_listener::zmq_listener(std::string _ip, uint32_t _zmq) : zmq_listener::zmq_listener(std::string _ip, uint32_t _zmq_block_port, uint32_t _zmq_trx_port) :
ip(_ip), zmq_listener_base(_ip, _zmq_block_port, _zmq_trx_port),
zmq_port(_zmq),
ctx(1), ctx(1),
socket(ctx, ZMQ_SUB), socket(ctx, ZMQ_SUB) {
stopped(false) {
} }
void zmq_listener::start() { void zmq_listener::start() {
@ -265,16 +517,16 @@ void zmq_listener::start() {
// socket.setsockopt( ZMQ_SUBSCRIBE, "hashtx", 6 ); // socket.setsockopt( ZMQ_SUBSCRIBE, "hashtx", 6 );
// socket.setsockopt( ZMQ_SUBSCRIBE, "rawblock", 8 ); // socket.setsockopt( ZMQ_SUBSCRIBE, "rawblock", 8 );
// socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 5 ); // socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 5 );
socket.connect("tcp://" + ip + ":" + std::to_string(zmq_port)); socket.connect("tcp://" + ip + ":" + std::to_string(block_zmq_port));
thr = std::thread(&zmq_listener::handle_zmq, this); block_thr = std::thread(&zmq_listener::handle_zmq, this);
ilog("zmq_listener thread started"); ilog("zmq_listener thread started");
} }
zmq_listener::~zmq_listener() { zmq_listener::~zmq_listener() {
stopped = true; stopped = true;
thr.join(); block_thr.join();
} }
std::vector<zmq::message_t> zmq_listener::receive_multipart() { std::vector<zmq::message_t> zmq_listener::receive_multipart() {
@ -302,7 +554,9 @@ void zmq_listener::handle_zmq() {
} }
const auto header = std::string(static_cast<char *>(msg[0].data()), msg[0].size()); const auto header = std::string(static_cast<char *>(msg[0].data()), msg[0].size());
const auto block_hash = boost::algorithm::hex(std::string(static_cast<char *>(msg[1].data()), msg[1].size())); const auto block_hash = boost::algorithm::hex(std::string(static_cast<char *>(msg[1].data()), msg[1].size()));
event_received(block_hash); block_data event_data;
event_data.block_hash = block_hash;
block_event_received(event_data);
} }
} catch (zmq::error_t &e) { } catch (zmq::error_t &e) {
elog("handle_zmq recv_multipart exception ${str}", ("str", e.what())); elog("handle_zmq recv_multipart exception ${str}", ("str", e.what()));
@ -314,6 +568,92 @@ void zmq_listener::handle_zmq() {
// ============================================================================= // =============================================================================
// =============================================================================
zmq_listener_libbitcoin::zmq_listener_libbitcoin(std::string _ip, uint32_t _block_zmq_port, uint32_t _trx_zmq_port) :
zmq_listener_base(_ip, _block_zmq_port, _trx_zmq_port),
block_socket(block_context, libbitcoin::protocol::zmq::socket::role::subscriber),
trx_socket(trx_context, libbitcoin::protocol::zmq::socket::role::subscriber) {
}
zmq_listener_libbitcoin::~zmq_listener_libbitcoin() {
stopped.store(true);
block_thr.join();
trx_thr.join();
}
void zmq_listener_libbitcoin::start() {
std::string endpoint_address = "tcp://" + ip;
libbitcoin::config::endpoint block_address(endpoint_address, block_zmq_port);
libbitcoin::config::endpoint trx_address(endpoint_address, trx_zmq_port);
block_socket.connect(block_address);
trx_socket.connect(trx_address);
block_thr = std::thread(&zmq_listener_libbitcoin::handle_block, this);
trx_thr = std::thread(&zmq_listener_libbitcoin::handle_trx, this);
}
void zmq_listener_libbitcoin::handle_trx() {
trx_poller.add(trx_socket);
while (!stopped.load()) {
const auto identifiers = trx_poller.wait(500);
if (identifiers.contains(trx_socket.id())) {
libbitcoin::protocol::zmq::message message;
trx_socket.receive(message);
std::vector<uint8_t> data;
for (int i = 0; i < 2; i++) {
data.clear();
message.dequeue(data);
}
libbitcoin::chain::transaction trx;
trx.from_data(data, true);
trx_event_received(trx);
}
}
ilog("zmq_listener_libbitcoin trx thread finished");
}
void zmq_listener_libbitcoin::handle_block() {
block_poller.add(block_socket);
while (!stopped.load()) {
const auto identifiers = block_poller.wait(500);
if (identifiers.contains(block_socket.id())) {
libbitcoin::protocol::zmq::message message;
block_socket.receive(message);
std::vector<uint8_t> data;
for (int i = 0; i < 3; i++) {
data.clear();
message.dequeue(data);
}
libbitcoin::chain::block block;
block.from_data(data, true);
block_data event_data;
event_data.block_hash = libbitcoin::config::hash256(block.hash()).to_string();
event_data.block = std::move(block);
block_event_received(event_data);
}
}
ilog("zmq_listener_libbitcoin block thread finished");
}
// =============================================================================
sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) :
sidechain_net_handler(_plugin, options) { sidechain_net_handler(_plugin, options) {
sidechain = sidechain_type::bitcoin; sidechain = sidechain_type::bitcoin;
@ -322,8 +662,16 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain
debug_rpc_calls = options.at("debug-rpc-calls").as<bool>(); debug_rpc_calls = options.at("debug-rpc-calls").as<bool>();
} }
ip = options.at("bitcoin-node-ip").as<std::string>(); if (options.count("use-bitcoind-client")) {
zmq_port = options.at("bitcoin-node-zmq-port").as<uint32_t>(); use_bitcoind_client = options.at("use-bitcoind-client").as<bool>();
}
libbitcoin_server_ip = options.at("libbitcoin-server-ip").as<std::string>();
libbitcoin_block_zmq_port = options.at("libbitcoin-server-block-zmq-port").as<uint32_t>();
libbitcoin_trx_zmq_port = options.at("libbitcoin-server-trx-zmq-port").as<uint32_t>();
bitcoin_node_ip = options.at("bitcoin-node-ip").as<std::string>();
bitcoin_node_zmq_port = options.at("bitcoin-node-zmq-port").as<uint32_t>();
rpc_port = options.at("bitcoin-node-rpc-port").as<uint32_t>(); rpc_port = options.at("bitcoin-node-rpc-port").as<uint32_t>();
rpc_user = options.at("bitcoin-node-rpc-user").as<std::string>(); rpc_user = options.at("bitcoin-node-rpc-user").as<std::string>();
rpc_password = options.at("bitcoin-node-rpc-password").as<std::string>(); rpc_password = options.at("bitcoin-node-rpc-password").as<std::string>();
@ -348,55 +696,44 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain
} }
} }
std::string url = ip + ":" + std::to_string(rpc_port); if (use_bitcoind_client) {
std::string url = bitcoin_node_ip + ":" + std::to_string(rpc_port);
if (!wallet_name.empty()) { if (!wallet_name.empty()) {
url = url + "/wallet/" + wallet_name; url = url + "/wallet/" + wallet_name;
} }
bitcoin_client = std::unique_ptr<bitcoin_rpc_client>(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls)); bitcoin_client = std::unique_ptr<bitcoin_rpc_client>(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls));
if (!wallet_name.empty()) { if (!wallet_name.empty()) {
bitcoin_client->loadwallet(wallet_name); bitcoin_client->loadwallet(wallet_name);
} }
std::string blockchain_info = bitcoin_client->getblockchaininfo(); listener = std::unique_ptr<zmq_listener>(new zmq_listener(bitcoin_node_ip, bitcoin_node_zmq_port));
if (blockchain_info.empty()) {
elog("No Bitcoin node running at ${url}", ("url", url)); } else {
FC_ASSERT(false); bitcoin_client = std::unique_ptr<bitcoin_libbitcoin_client>(new bitcoin_libbitcoin_client(libbitcoin_server_ip));
listener = std::unique_ptr<zmq_listener_libbitcoin>(new zmq_listener_libbitcoin(libbitcoin_server_ip, libbitcoin_block_zmq_port, libbitcoin_trx_zmq_port));
} }
std::stringstream bci_ss(std::string(blockchain_info.begin(), blockchain_info.end()));
boost::property_tree::ptree bci_json; std::string chain_info = bitcoin_client->getblockchaininfo();
boost::property_tree::read_json(bci_ss, bci_json);
using namespace bitcoin; using namespace bitcoin;
network_type = bitcoin_address::network::mainnet; network_type = bitcoin_address::network::mainnet;
if (bci_json.count("chain")) { if (chain_info == "test") {
std::string chain = bci_json.get<std::string>("chain");
if (chain.length() > 0) {
if (chain == "test") {
network_type = bitcoin_address::network::testnet; network_type = bitcoin_address::network::testnet;
} else if (chain == "regtest") { } else if (chain_info == "regtest") {
network_type = bitcoin_address::network::regtest; network_type = bitcoin_address::network::regtest;
} }
}
}
std::string network_info_str = bitcoin_client->getnetworkinfo(); bitcoin_client->getnetworkinfo();
if (network_info_str.empty()) {
elog("No Bitcoin node running at ${url}", ("url", url));
FC_ASSERT(false);
}
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->start(); listener->start();
listener->event_received.connect([this](const std::string &event_data) { listener->block_event_received.connect([this](const block_data &block_event_data) {
std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach(); std::thread(&sidechain_net_handler_bitcoin::block_handle_event, this, block_event_data).detach();
});
listener->trx_event_received.connect([this](const libbitcoin::chain::transaction &trx_event_data) {
std::thread(&sidechain_net_handler_bitcoin::trx_handle_event, this, trx_event_data).detach();
}); });
database.changed_objects.connect([this](const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts) { database.changed_objects.connect([this](const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts) {
@ -418,7 +755,7 @@ sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() {
bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) { bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) {
//ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); // ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain)));
bool should_approve = false; bool should_approve = false;
@ -448,12 +785,15 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
bool transaction_ok = false; bool transaction_ok = false;
std::string new_pw_address = ""; std::string new_pw_address = "";
son_wallet_id_type swo_id = op_obj_idx_0.get<son_wallet_update_operation>().son_wallet_id; son_wallet_id_type swo_id = op_obj_idx_0.get<son_wallet_update_operation>().son_wallet_id;
const auto ast = active_sidechain_types(database.head_block_time());
const auto id = (swo_id.instance.value - std::distance(ast.begin(), ast.find(sidechain))) / ast.size();
const son_wallet_id_type op_id{id};
const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>(); const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
const auto swo = idx.find(swo_id); const auto swo = idx.find(op_id);
if (swo != idx.end()) { if (swo != idx.end()) {
const auto &active_sons = gpo.active_sons.at(sidechain); const auto &active_sons = gpo.active_sons.at(sidechain);
vector<son_info> wallet_sons = swo->sons.at(sidechain); const auto &wallet_sons = swo->sons.at(sidechain);
bool son_sets_equal = (active_sons.size() == wallet_sons.size()); bool son_sets_equal = (active_sons.size() == wallet_sons.size());
@ -466,7 +806,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
if (son_sets_equal) { if (son_sets_equal) {
const auto &active_sons = gpo.active_sons.at(sidechain); const auto &active_sons = gpo.active_sons.at(sidechain);
vector<string> son_pubkeys_bitcoin; vector<string> son_pubkeys_bitcoin;
for (const son_info &si : active_sons) { for (const auto &si : active_sons) {
son_pubkeys_bitcoin.push_back(si.public_key); son_pubkeys_bitcoin.push_back(si.public_key);
} }
@ -485,18 +825,21 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
} }
if (po.proposed_transaction.operations.size() >= 2) { if (po.proposed_transaction.operations.size() >= 2) {
object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id; const object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id;
const auto ast = active_sidechain_types(database.head_block_time());
const auto id = (object_id.instance() - std::distance(ast.begin(), ast.find(sidechain))) / ast.size();
const object_id_type obj_id{object_id.space(), object_id.type(), id};
std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction; std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction;
const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_object_id>(); const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_object_id>();
const auto st = st_idx.find(object_id); const auto st = st_idx.find(obj_id);
if (st == st_idx.end()) { if (st == st_idx.end()) {
std::string tx_str = ""; std::string tx_str = "";
if (object_id.is<son_wallet_id_type>()) { if (obj_id.is<son_wallet_id_type>()) {
const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>(); const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
const auto swo = idx.find(object_id); const auto swo = idx.find(obj_id);
if (swo != idx.end()) { if (swo != idx.end()) {
tx_str = create_primary_wallet_transaction(*swo, new_pw_address); tx_str = create_primary_wallet_transaction(*swo, new_pw_address);
} }
@ -527,40 +870,26 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
uint64_t swdo_amount = swdo->sidechain_amount.value; uint64_t swdo_amount = swdo->sidechain_amount.value;
uint64_t swdo_vout = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-") + 1)); uint64_t swdo_vout = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-") + 1));
const std::string tx_str = bitcoin_client->getrawtransaction(swdo_txid, true); btc_tx tx = bitcoin_client->getrawtransaction(swdo_txid, true);
if (tx_str != "") {
std::stringstream tx_ss(tx_str);
boost::property_tree::ptree tx_json;
boost::property_tree::read_json(tx_ss, tx_json);
if (tx_json.count("error") && tx_json.get_child("error").empty()) { if (!tx.tx_in_list.empty()) {
std::string tx_txid = tx_json.get<std::string>("result.txid"); std::string tx_txid = tx.tx_txid;
uint32_t tx_confirmations = tx_json.get<uint32_t>("result.confirmations"); uint32_t tx_confirmations = tx.tx_confirmations;
std::string tx_address = ""; std::string tx_address = "";
uint64_t tx_amount = -1; uint64_t tx_amount = -1;
uint64_t tx_vout = -1; uint64_t tx_vout = -1;
for (auto &input : tx_json.get_child("result.vout")) { for (auto &input : tx.tx_in_list) {
std::string tx_vout_s = input.second.get<std::string>("n"); tx_vout = input.tx_vout;
tx_vout = std::stoll(tx_vout_s);
if (tx_vout == swdo_vout) { if (tx_vout == swdo_vout) {
if (bitcoin_major_version > 21) { for (auto &address : input.tx_address) {
std::string address = input.second.get<std::string>("scriptPubKey.address");
if (address == swdo_address) { if (address == swdo_address) {
tx_address = 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; break;
} }
} }
} tx_amount = input.tx_amount;
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);
break; break;
} }
} }
@ -571,7 +900,6 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
(swdo_vout == tx_vout) && (swdo_vout == tx_vout) &&
(gpo.parameters.son_bitcoin_min_tx_confirmations() <= tx_confirmations); (gpo.parameters.son_bitcoin_min_tx_confirmations() <= tx_confirmations);
} }
}
object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id; object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id;
std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction; std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction;
@ -716,7 +1044,11 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() {
if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) ||
(active_sw->addresses.at(sidechain).empty())) { (active_sw->addresses.at(sidechain).empty())) {
if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id)) { const auto ast = active_sidechain_types(database.head_block_time());
const auto id = active_sw->id.instance() * ast.size() + std::distance(ast.begin(), ast.find(sidechain));
const object_id_type op_id{active_sw->id.space(), active_sw->id.type(), id};
if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, op_id)) {
return; return;
} }
@ -729,7 +1061,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() {
boost::property_tree::ptree active_pw_pt; boost::property_tree::ptree active_pw_pt;
boost::property_tree::read_json(active_pw_ss, active_pw_pt); boost::property_tree::read_json(active_pw_ss, active_pw_pt);
if (active_pw_pt.count("error") && active_pw_pt.get_child("error").empty()) { if (active_pw_pt.count("error") && active_pw_pt.get_child("error").empty()) {
if (!plugin.can_son_participate(sidechain, chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id)) { if (!plugin.can_son_participate(sidechain, chain::operation::tag<chain::son_wallet_update_operation>::value, op_id)) {
return; return;
} }
@ -743,7 +1075,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() {
son_wallet_update_operation swu_op; son_wallet_update_operation swu_op;
swu_op.payer = gpo.parameters.son_account(); swu_op.payer = gpo.parameters.son_account();
swu_op.son_wallet_id = active_sw->id; swu_op.son_wallet_id = op_id;
swu_op.sidechain = sidechain; swu_op.sidechain = sidechain;
swu_op.address = res.str(); swu_op.address = res.str();
@ -753,12 +1085,23 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() {
std::string new_pw_address = active_pw_pt.get<std::string>("result.address"); std::string new_pw_address = active_pw_pt.get<std::string>("result.address");
std::string tx_str = create_primary_wallet_transaction(*prev_sw, new_pw_address); std::string tx_str = create_primary_wallet_transaction(*prev_sw, new_pw_address);
if (!tx_str.empty()) { if (!tx_str.empty()) {
const auto ast = active_sidechain_types(database.head_block_time());
const auto prev_id = prev_sw->id.instance() * ast.size() + std::distance(ast.begin(), ast.find(sidechain));
const object_id_type prev_op_id{prev_sw->id.space(), prev_sw->id.type(), prev_id};
sidechain_transaction_create_operation stc_op; sidechain_transaction_create_operation stc_op;
stc_op.payer = gpo.parameters.son_account(); stc_op.payer = gpo.parameters.son_account();
stc_op.object_id = prev_sw->id; stc_op.object_id = prev_op_id;
stc_op.sidechain = sidechain; stc_op.sidechain = sidechain;
stc_op.transaction = tx_str; stc_op.transaction = tx_str;
stc_op.signers = prev_sw->sons.at(sidechain); for (const auto &signer : prev_sw->sons.at(sidechain)) {
son_info si;
si.son_id = signer.son_id;
si.weight = signer.weight;
si.signing_key = signer.signing_key;
si.sidechain_public_keys[sidechain] = signer.public_key;
stc_op.signers.emplace_back(std::move(si));
}
proposal_op.proposed_ops.emplace_back(stc_op); proposal_op.proposed_ops.emplace_back(stc_op);
} }
} }
@ -769,7 +1112,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() {
database.push_transaction(trx, database::validation_steps::skip_block_size_check); database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node()) if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx)); plugin.app().p2p_node()->broadcast(net::trx_message(trx));
plugin.log_son_proposal_retry(sidechain, chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id); plugin.log_son_proposal_retry(sidechain, chain::operation::tag<chain::son_wallet_update_operation>::value, op_id);
} catch (fc::exception &e) { } catch (fc::exception &e) {
elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what()));
return; return;
@ -799,7 +1142,12 @@ void sidechain_net_handler_bitcoin::process_sidechain_addresses() {
if (sao.expires == time_point_sec::maximum()) { if (sao.expires == time_point_sec::maximum()) {
auto usr_pubkey = fc::ecc::public_key(create_public_key_data(parse_hex(sao.deposit_public_key))); auto usr_pubkey = fc::ecc::public_key(create_public_key_data(parse_hex(sao.deposit_public_key)));
btc_one_or_weighted_multisig_address addr(usr_pubkey, pubkeys, network_type); payment_type payment_type_address = payment_type::P2SH_WSH;
if (use_bitcoind_client) {
payment_type_address = payment_type::P2WSH;
}
btc_one_or_weighted_multisig_address addr(usr_pubkey, pubkeys, network_type, payment_type_address);
std::string address_data = "{ \"redeemScript\": \"" + fc::to_hex(addr.get_redeem_script()) + std::string address_data = "{ \"redeemScript\": \"" + fc::to_hex(addr.get_redeem_script()) +
"\", \"witnessScript\": \"" + fc::to_hex(addr.get_witness_script()) + "\" }"; "\", \"witnessScript\": \"" + fc::to_hex(addr.get_witness_script()) + "\" }";
@ -861,7 +1209,14 @@ bool sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_obj
stc_op.object_id = swdo.id; stc_op.object_id = swdo.id;
stc_op.sidechain = sidechain; stc_op.sidechain = sidechain;
stc_op.transaction = tx_str; stc_op.transaction = tx_str;
stc_op.signers = gpo.active_sons.at(sidechain); for (const auto &signer : gpo.active_sons.at(sidechain)) {
son_info si;
si.son_id = signer.son_id;
si.weight = signer.weight;
si.signing_key = signer.signing_key;
si.sidechain_public_keys[sidechain] = signer.public_key;
stc_op.signers.emplace_back(std::move(si));
}
proposal_op.proposed_ops.emplace_back(stc_op); proposal_op.proposed_ops.emplace_back(stc_op);
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op);
@ -905,7 +1260,14 @@ bool sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw
stc_op.object_id = swwo.id; stc_op.object_id = swwo.id;
stc_op.sidechain = sidechain; stc_op.sidechain = sidechain;
stc_op.transaction = tx_str; stc_op.transaction = tx_str;
stc_op.signers = gpo.active_sons.at(sidechain); for (const auto &signer : gpo.active_sons.at(sidechain)) {
son_info si;
si.son_id = signer.son_id;
si.weight = signer.weight;
si.signing_key = signer.signing_key;
si.sidechain_public_keys[sidechain] = signer.public_key;
stc_op.signers.emplace_back(std::move(si));
}
proposal_op.proposed_ops.emplace_back(stc_op); proposal_op.proposed_ops.emplace_back(stc_op);
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op);
@ -942,13 +1304,11 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain
return false; return false;
} }
const std::string tx_str = bitcoin_client->getrawtransaction(sto.sidechain_transaction, true); btc_tx tx = bitcoin_client->getrawtransaction(sto.sidechain_transaction, true);
if (tx_str != "") {
std::stringstream tx_ss(tx_str);
boost::property_tree::ptree tx_json;
boost::property_tree::read_json(tx_ss, tx_json);
if ((tx_json.count("error")) && (!tx_json.get_child("error").empty())) { if (tx.tx_in_list.empty()) {
// This case will result with segmentation fault.
// FIXME check if that happened before introducing libbitcoin
return false; return false;
} }
@ -960,37 +1320,32 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain
auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(si.public_key))); auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(si.public_key)));
pubkey_weights.push_back(std::make_pair(pub_key, si.weight)); pubkey_weights.push_back(std::make_pair(pub_key, si.weight));
} }
btc_weighted_multisig_address addr(pubkey_weights, network_type);
std::string tx_txid = tx_json.get<std::string>("result.txid"); payment_type payment_type_address = payment_type::P2SH_WSH;
uint32_t tx_confirmations = tx_json.get<uint32_t>("result.confirmations"); if (use_bitcoind_client) {
payment_type_address = payment_type::P2WSH;
}
btc_weighted_multisig_address addr(pubkey_weights, network_type, payment_type_address);
std::string tx_txid = tx.tx_txid;
uint32_t tx_confirmations = tx.tx_confirmations;
std::string tx_address = addr.get_address(); std::string tx_address = addr.get_address();
int64_t tx_amount = -1; int64_t tx_amount = -1;
if (tx_confirmations >= gpo.parameters.son_bitcoin_min_tx_confirmations()) { if (tx_confirmations >= gpo.parameters.son_bitcoin_min_tx_confirmations()) {
if (sto.object_id.is<son_wallet_deposit_id_type>()) { if (sto.object_id.is<son_wallet_deposit_id_type>()) {
for (auto &input : tx_json.get_child("result.vout")) { for (auto &input : tx.tx_in_list) {
if (bitcoin_major_version > 21) { for (auto &address : input.tx_address) {
std::string address = input.second.get<std::string>("scriptPubKey.address");
if (address == tx_address) { if (address == tx_address) {
std::string tx_amount_s = input.second.get<std::string>("value"); tx_amount = input.tx_amount;
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");
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);
break; break;
} }
} }
}
}
settle_amount = asset(tx_amount, database.get_global_properties().parameters.btc_asset()); settle_amount = asset(tx_amount, database.get_global_properties().parameters.btc_asset());
return true; return true;
} }
}
if (sto.object_id.is<son_wallet_withdraw_id_type>()) { if (sto.object_id.is<son_wallet_withdraw_id_type>()) {
auto swwo = database.get<son_wallet_withdraw_object>(sto.object_id); auto swwo = database.get<son_wallet_withdraw_object>(sto.object_id);
@ -998,7 +1353,6 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain
return true; return true;
} }
} }
}
return false; return false;
} }
@ -1007,7 +1361,7 @@ optional<asset> sidechain_net_handler_bitcoin::estimate_withdrawal_transaction_f
return optional<asset>{}; return optional<asset>{};
} }
std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const std::vector<son_info> &son_pubkeys) { std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const std::vector<son_sidechain_info> &son_pubkeys) {
using namespace bitcoin; using namespace bitcoin;
std::vector<std::pair<fc::ecc::public_key, uint16_t>> pubkey_weights; std::vector<std::pair<fc::ecc::public_key, uint16_t>> pubkey_weights;
@ -1016,7 +1370,11 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const s
pubkey_weights.push_back(std::make_pair(pub_key, son.weight)); pubkey_weights.push_back(std::make_pair(pub_key, son.weight));
} }
btc_weighted_multisig_address addr(pubkey_weights, network_type); payment_type payment_type_address = payment_type::P2SH_WSH;
if (use_bitcoind_client) {
payment_type_address = payment_type::P2WSH;
}
btc_weighted_multisig_address addr(pubkey_weights, network_type, payment_type_address);
std::stringstream ss; std::stringstream ss;
@ -1252,18 +1610,34 @@ std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_tran
} }
// Add redeemscripts to vins and make tx ready for sending // Add redeemscripts to vins and make tx ready for sending
sign_witness_transaction_finalize(tx, redeem_scripts, false); sign_witness_transaction_finalize(tx, redeem_scripts, false);
if (!use_bitcoind_client) {
// get witness script from redeem script
bitcoin::bytes redeem_bytes = parse_hex(redeem_script);
fc::sha256 sha = fc::sha256::hash(&redeem_bytes[0], redeem_bytes.size());
std::string witness_script(sha.str());
witness_script.insert(0, std::string("220020"));
for (size_t i = 0; i < tx.vin.size(); i++) {
tx.vin[i].scriptSig = parse_hex(witness_script);
}
}
std::string final_tx_hex = fc::to_hex(pack(tx)); std::string final_tx_hex = fc::to_hex(pack(tx));
return bitcoin_client->sendrawtransaction(final_tx_hex); std::string res = bitcoin_client->sendrawtransaction(final_tx_hex);
if (res.empty()) {
return res;
}
return tx.get_txid().str();
} }
void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) { void sidechain_net_handler_bitcoin::block_handle_event(const block_data &event_data) {
const std::string block = bitcoin_client->getblock(event_data);
if (block.empty())
return;
add_to_son_listener_log("BLOCK : " + event_data); auto vins = bitcoin_client->getblock(event_data);
add_to_son_listener_log("BLOCK : " + event_data.block_hash);
auto vins = extract_info_from_block(block);
scoped_lock interlock(event_handler_mutex); 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>(); const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_address_and_expires>();
@ -1282,6 +1656,7 @@ void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data)
sed.timestamp = database.head_block_time(); sed.timestamp = database.head_block_time();
sed.block_num = database.head_block_num(); sed.block_num = database.head_block_num();
sed.sidechain = addr_itr->sidechain; sed.sidechain = addr_itr->sidechain;
sed.type = sidechain_event_type::deposit;
sed.sidechain_uid = sidechain_uid; sed.sidechain_uid = sidechain_uid;
sed.sidechain_transaction_id = v.out.hash_tx; sed.sidechain_transaction_id = v.out.hash_tx;
sed.sidechain_from = v.address; sed.sidechain_from = v.address;
@ -1299,6 +1674,10 @@ void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data)
} }
} }
void sidechain_net_handler_bitcoin::trx_handle_event(const libbitcoin::chain::transaction &trx_data) {
bitcoin_client->import_trx_to_memory_pool(trx_data);
}
std::string sidechain_net_handler_bitcoin::get_redeemscript_for_userdeposit(const std::string &user_address) { std::string sidechain_net_handler_bitcoin::get_redeemscript_for_userdeposit(const std::string &user_address) {
using namespace bitcoin; using namespace bitcoin;
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_address_and_expires>(); const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_address_and_expires>();
@ -1319,58 +1698,16 @@ std::string sidechain_net_handler_bitcoin::get_redeemscript_for_userdeposit(cons
pubkey_weights.push_back(std::make_pair(pub_key, son.weight)); pubkey_weights.push_back(std::make_pair(pub_key, son.weight));
} }
auto user_pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(addr_itr->deposit_public_key))); auto user_pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(addr_itr->deposit_public_key)));
btc_one_or_weighted_multisig_address deposit_addr(user_pub_key, pubkey_weights, network_type);
payment_type payment_type_address = payment_type::P2SH_WSH;
if (use_bitcoind_client) {
payment_type_address = payment_type::P2WSH;
}
btc_one_or_weighted_multisig_address deposit_addr(user_pub_key, pubkey_weights, network_type, payment_type_address);
return fc::to_hex(deposit_addr.get_redeem_script()); return fc::to_hex(deposit_addr.get_redeem_script());
} }
std::vector<info_for_vin> sidechain_net_handler_bitcoin::extract_info_from_block(const std::string &_block) {
std::stringstream ss(_block);
boost::property_tree::ptree json;
boost::property_tree::read_json(ss, json);
auto json_result = json.get_child_optional("result");
std::vector<info_for_vin> result;
for (const auto &tx_child : json_result.get().get_child("tx")) {
const auto &tx = tx_child.second;
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;
}
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>();
amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end());
vin.out.amount = std::stoll(amount);
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>());
}
}
}
return result;
}
void sidechain_net_handler_bitcoin::on_changed_objects(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts) { void sidechain_net_handler_bitcoin::on_changed_objects(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts) {
fc::time_point now = fc::time_point::now(); fc::time_point now = fc::time_point::now();
int64_t time_to_next_changed_objects_processing = 5000; int64_t time_to_next_changed_objects_processing = 5000;

View file

@ -14,8 +14,9 @@
#include <graphene/chain/account_object.hpp> #include <graphene/chain/account_object.hpp>
#include <graphene/chain/protocol/son_wallet.hpp> #include <graphene/chain/protocol/son_wallet.hpp>
#include <graphene/chain/sidechain_transaction_object.hpp> #include <graphene/chain/sidechain_transaction_object.hpp>
#include <graphene/chain/son_info.hpp> #include <graphene/chain/son_sidechain_info.hpp>
#include <graphene/chain/son_wallet_object.hpp> #include <graphene/chain/son_wallet_object.hpp>
#include <graphene/peerplays_sidechain/ethereum/decoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/encoders.hpp> #include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/transaction.hpp> #include <graphene/peerplays_sidechain/ethereum/transaction.hpp>
#include <graphene/peerplays_sidechain/ethereum/utils.hpp> #include <graphene/peerplays_sidechain/ethereum/utils.hpp>
@ -147,6 +148,21 @@ sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidecha
wallet_contract_address = options.at("ethereum-wallet-contract-address").as<std::string>(); wallet_contract_address = options.at("ethereum-wallet-contract-address").as<std::string>();
if (options.count("ethereum-erc-20-address")) {
const std::vector<std::string> symbol_addresses = options["ethereum-erc-20-address"].as<std::vector<std::string>>();
for (const std::string &itr : symbol_addresses) {
auto itr_pair = graphene::app::dejsonify<std::pair<std::string, std::string>>(itr, 5);
ilog("ERC-20 symbol: ${symbol}, address: ${address}", ("symbol", itr_pair.first)("address", itr_pair.second));
if (!itr_pair.first.length() || !itr_pair.second.length()) {
FC_THROW("Invalid symbol address pair.");
}
auto address = itr_pair.second;
std::transform(address.begin(), address.end(), address.begin(), ::tolower);
erc20_addresses.insert(bimap_type::value_type{itr_pair.first, address});
}
}
if (options.count("ethereum-private-key")) { if (options.count("ethereum-private-key")) {
const std::vector<std::string> pub_priv_keys = options["ethereum-private-key"].as<std::vector<std::string>>(); const std::vector<std::string> pub_priv_keys = options["ethereum-private-key"].as<std::vector<std::string>>();
for (const std::string &itr_key_pair : pub_priv_keys) { for (const std::string &itr_key_pair : pub_priv_keys) {
@ -218,12 +234,15 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po)
bool address_ok = false; bool address_ok = false;
bool transaction_ok = false; bool transaction_ok = false;
const son_wallet_id_type swo_id = op_obj_idx_0.get<son_wallet_update_operation>().son_wallet_id; const son_wallet_id_type swo_id = op_obj_idx_0.get<son_wallet_update_operation>().son_wallet_id;
const auto ast = active_sidechain_types(database.head_block_time());
const auto id = (swo_id.instance.value - std::distance(ast.begin(), ast.find(sidechain))) / ast.size();
const son_wallet_id_type op_id{id};
const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>(); const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
const auto swo = idx.find(swo_id); const auto swo = idx.find(op_id);
if (swo != idx.end()) { if (swo != idx.end()) {
const auto active_sons = gpo.active_sons.at(sidechain); const auto active_sons = gpo.active_sons.at(sidechain);
const vector<son_info> wallet_sons = swo->sons.at(sidechain); const vector<son_sidechain_info> wallet_sons = swo->sons.at(sidechain);
bool son_sets_equal = (active_sons.size() == wallet_sons.size()); bool son_sets_equal = (active_sons.size() == wallet_sons.size());
@ -239,17 +258,19 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po)
if (po.proposed_transaction.operations.size() >= 2) { if (po.proposed_transaction.operations.size() >= 2) {
const object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id; const object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id;
const auto id = (object_id.instance() - std::distance(ast.begin(), ast.find(sidechain))) / ast.size();
const object_id_type obj_id{object_id.space(), object_id.type(), id};
const std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction; const std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction;
const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_object_id>(); const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_object_id>();
const auto st = st_idx.find(object_id); const auto st = st_idx.find(obj_id);
if (st == st_idx.end()) { if (st == st_idx.end()) {
std::string tx_str = ""; std::string tx_str = "";
if (object_id.is<son_wallet_id_type>()) { if (obj_id.is<son_wallet_id_type>()) {
const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>(); const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
const auto swo = idx.find(object_id); const auto swo = idx.find(obj_id);
if (swo != idx.end()) { if (swo != idx.end()) {
tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), object_id.operator std::string()); tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), object_id.operator std::string());
} }
@ -290,21 +311,41 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po)
const std::string sidechain_from = tx.get<std::string>("result.from"); const std::string sidechain_from = tx.get<std::string>("result.from");
const std::string sidechain_to = tx.get<std::string>("result.to"); const std::string sidechain_to = tx.get<std::string>("result.to");
const std::string value_s = tx.get<std::string>("result.value");
boost::multiprecision::uint256_t amount(value_s);
amount = amount / 100000;
amount = amount / 100000;
const fc::safe<uint64_t> sidechain_amount = amount;
std::string cmp_sidechain_to = sidechain_to; std::string cmp_sidechain_to = sidechain_to;
std::transform(cmp_sidechain_to.begin(), cmp_sidechain_to.end(), cmp_sidechain_to.begin(), ::toupper); std::transform(cmp_sidechain_to.begin(), cmp_sidechain_to.end(), cmp_sidechain_to.begin(), ::toupper);
std::string cmp_wallet_contract_address = wallet_contract_address; std::string cmp_wallet_contract_address = wallet_contract_address;
std::transform(cmp_wallet_contract_address.begin(), cmp_wallet_contract_address.end(), cmp_wallet_contract_address.begin(), ::toupper); std::transform(cmp_wallet_contract_address.begin(), cmp_wallet_contract_address.end(), cmp_wallet_contract_address.begin(), ::toupper);
process_ok = (swdo_sidechain_from == sidechain_from) && //! Check whether it is ERC-20 token deposit
std::string symbol;
boost::multiprecision::uint256_t amount;
bool error_in_deposit = false;
const auto deposit_erc_20 = ethereum::deposit_erc20_decoder::decode(tx.get<std::string>("result.input"));
if (deposit_erc_20.valid()) {
std::string cmp_token = deposit_erc_20->token;
std::transform(cmp_token.begin(), cmp_token.end(), cmp_token.begin(), ::tolower);
const auto it = erc20_addresses.right.find(cmp_token);
if (it == erc20_addresses.right.end()) {
wlog("No erc-20 token with address: ${address}", ("address", cmp_token));
error_in_deposit = true;
}
symbol = it->second;
amount = deposit_erc_20->amount;
} else {
symbol = "ETH";
const std::string value_s = tx.get<std::string>("result.value");
amount = boost::multiprecision::uint256_t{value_s};
amount = amount / 100000;
amount = amount / 100000;
}
process_ok = (!error_in_deposit) &&
(swdo_sidechain_from == sidechain_from) &&
(cmp_sidechain_to == cmp_wallet_contract_address) && (cmp_sidechain_to == cmp_wallet_contract_address) &&
(swdo_sidechain_currency == "ETH") && (swdo_sidechain_currency == symbol) &&
(swdo_sidechain_amount == sidechain_amount.value); (swdo_sidechain_amount == fc::safe<uint64_t>{amount}.value);
} }
} }
} }
@ -416,29 +457,33 @@ void sidechain_net_handler_ethereum::process_primary_wallet() {
if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) ||
(active_sw->addresses.at(sidechain).empty())) { (active_sw->addresses.at(sidechain).empty())) {
if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id)) { const auto ast = active_sidechain_types(database.head_block_time());
const auto id = active_sw->id.instance() * ast.size() + std::distance(ast.begin(), ast.find(sidechain));
const object_id_type op_id{active_sw->id.space(), active_sw->id.type(), id};
if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, op_id)) {
return; return;
} }
if (!plugin.can_son_participate(sidechain, chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id)) { if (!plugin.can_son_participate(sidechain, chain::operation::tag<chain::son_wallet_update_operation>::value, op_id)) {
return; return;
} }
const chain::global_property_object &gpo = database.get_global_properties(); const chain::global_property_object &gpo = database.get_global_properties();
proposal_create_operation proposal_op; proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account;
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; const uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
son_wallet_update_operation swu_op; son_wallet_update_operation swu_op;
swu_op.payer = gpo.parameters.son_account(); swu_op.payer = gpo.parameters.son_account();
swu_op.son_wallet_id = active_sw->id; swu_op.son_wallet_id = op_id;
swu_op.sidechain = sidechain; swu_op.sidechain = sidechain;
swu_op.address = wallet_contract_address; swu_op.address = wallet_contract_address;
proposal_op.proposed_ops.emplace_back(swu_op); proposal_op.proposed_ops.emplace_back(swu_op);
const auto signers = [this, &prev_sw, &active_sw, &swi] { const auto signers = [this, &prev_sw, &active_sw, &swi] {
std::vector<son_info> signers; std::vector<son_sidechain_info> signers;
//! Check if we don't have any previous set of active SONs use the current one //! Check if we don't have any previous set of active SONs use the current one
if (prev_sw != swi.rend()) { if (prev_sw != swi.rend()) {
if (!prev_sw->sons.at(sidechain).empty()) if (!prev_sw->sons.at(sidechain).empty())
@ -452,14 +497,21 @@ void sidechain_net_handler_ethereum::process_primary_wallet() {
return signers; return signers;
}(); }();
std::string tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), active_sw->id.operator std::string()); std::string tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), op_id.operator std::string());
if (!tx_str.empty()) { if (!tx_str.empty()) {
sidechain_transaction_create_operation stc_op; sidechain_transaction_create_operation stc_op;
stc_op.payer = gpo.parameters.son_account(); stc_op.payer = gpo.parameters.son_account();
stc_op.object_id = active_sw->id; stc_op.object_id = op_id;
stc_op.sidechain = sidechain; stc_op.sidechain = sidechain;
stc_op.transaction = tx_str; stc_op.transaction = tx_str;
stc_op.signers = signers; for (const auto &signer : signers) {
son_info si;
si.son_id = signer.son_id;
si.weight = signer.weight;
si.signing_key = signer.signing_key;
si.sidechain_public_keys[sidechain] = signer.public_key;
stc_op.signers.emplace_back(std::move(si));
}
proposal_op.proposed_ops.emplace_back(stc_op); proposal_op.proposed_ops.emplace_back(stc_op);
} }
@ -469,7 +521,7 @@ void sidechain_net_handler_ethereum::process_primary_wallet() {
database.push_transaction(trx, database::validation_steps::skip_block_size_check); database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node()) if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx)); plugin.app().p2p_node()->broadcast(net::trx_message(trx));
plugin.log_son_proposal_retry(sidechain, chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id); plugin.log_son_proposal_retry(sidechain, chain::operation::tag<chain::son_wallet_update_operation>::value, op_id);
} catch (fc::exception &e) { } catch (fc::exception &e) {
elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what()));
return; return;
@ -489,12 +541,19 @@ bool sidechain_net_handler_ethereum::process_deposit(const son_wallet_deposit_ob
const chain::global_property_object &gpo = database.get_global_properties(); const chain::global_property_object &gpo = database.get_global_properties();
price asset_price = database.get<asset_object>(database.get_global_properties().parameters.eth_asset()).options.core_exchange_rate; const auto &assets_by_symbol = database.get_index_type<asset_index>().indices().get<by_symbol>();
asset asset_to_issue = asset(swdo.peerplays_asset.amount * asset_price.quote.amount / asset_price.base.amount, database.get_global_properties().parameters.eth_asset()); const auto asset_itr = assets_by_symbol.find(swdo.sidechain_currency);
if (asset_itr == assets_by_symbol.end()) {
wlog("Could not find asset: ${symbol}", ("symbol", swdo.sidechain_currency));
return false;
}
const price asset_price = asset_itr->options.core_exchange_rate;
const asset asset_to_issue = asset(swdo.peerplays_asset.amount * asset_price.quote.amount / asset_price.base.amount, asset_itr->get_id());
proposal_create_operation proposal_op; proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account;
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; const uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
son_wallet_deposit_process_operation swdp_op; son_wallet_deposit_process_operation swdp_op;
@ -537,7 +596,7 @@ bool sidechain_net_handler_ethereum::process_withdrawal(const son_wallet_withdra
proposal_create_operation proposal_op; proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account;
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; const uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
son_wallet_withdraw_process_operation swwp_op; son_wallet_withdraw_process_operation swwp_op;
@ -550,7 +609,14 @@ bool sidechain_net_handler_ethereum::process_withdrawal(const son_wallet_withdra
stc_op.object_id = swwo.id; stc_op.object_id = swwo.id;
stc_op.sidechain = sidechain; stc_op.sidechain = sidechain;
stc_op.transaction = tx_str; stc_op.transaction = tx_str;
stc_op.signers = gpo.active_sons.at(sidechain); for (const auto &signer : gpo.active_sons.at(sidechain)) {
son_info si;
si.son_id = signer.son_id;
si.weight = signer.weight;
si.signing_key = signer.signing_key;
si.sidechain_public_keys[sidechain] = signer.public_key;
stc_op.signers.emplace_back(std::move(si));
}
proposal_op.proposed_ops.emplace_back(stc_op); proposal_op.proposed_ops.emplace_back(stc_op);
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op);
@ -683,7 +749,13 @@ bool sidechain_net_handler_ethereum::settle_sidechain_transaction(const sidechai
if (sto.object_id.is<son_wallet_withdraw_id_type>()) { if (sto.object_id.is<son_wallet_withdraw_id_type>()) {
auto swwo = database.get<son_wallet_withdraw_object>(sto.object_id); auto swwo = database.get<son_wallet_withdraw_object>(sto.object_id);
settle_amount = asset(swwo.withdraw_amount, database.get_global_properties().parameters.eth_asset()); const auto &assets_by_symbol = database.get_index_type<asset_index>().indices().get<by_symbol>();
const auto asset_itr = assets_by_symbol.find(swwo.withdraw_currency);
if (asset_itr == assets_by_symbol.end()) {
wlog("Could not find asset: ${symbol}", ("symbol", swwo.withdraw_currency));
return false;
}
settle_amount = asset(swwo.withdraw_amount, asset_itr->get_id());
} }
return true; return true;
@ -712,24 +784,19 @@ optional<asset> sidechain_net_handler_ethereum::estimate_withdrawal_transaction_
return optional<asset>{}; return optional<asset>{};
} }
const auto &assets_by_symbol = database.get_index_type<asset_index>().indices().get<by_symbol>();
auto asset_itr = assets_by_symbol.find("ETH");
if (asset_itr == assets_by_symbol.end()) {
wlog("Could not find asset matching ETH");
return optional<asset>{};
}
const auto &public_key = son->sidechain_public_keys.at(sidechain); const auto &public_key = son->sidechain_public_keys.at(sidechain);
const auto data = ethereum::withdrawal_encoder::encode(public_key, 1 * 10000000000, son_wallet_withdraw_id_type{0}.operator object_id_type().operator std::string()); const auto data = ethereum::withdrawal_encoder::encode(public_key, boost::multiprecision::uint256_t{1} * boost::multiprecision::uint256_t{10000000000}, son_wallet_withdraw_id_type{0}.operator object_id_type().operator std::string());
const std::string params = "[{\"from\":\"" + ethereum::add_0x(public_key) + "\", \"to\":\"" + wallet_contract_address + "\", \"data\":\"" + data + "\"}]"; const std::string params = "[{\"from\":\"" + ethereum::add_0x(public_key) + "\", \"to\":\"" + wallet_contract_address + "\", \"data\":\"" + data + "\"}]";
const auto estimate_gas = ethereum::from_hex<int64_t>(rpc_client->get_estimate_gas(params)); const auto estimate_gas = ethereum::from_hex<int64_t>(rpc_client->get_estimate_gas(params));
const auto gas_price = ethereum::from_hex<int64_t>(rpc_client->get_gas_price()); const auto gas_price = ethereum::from_hex<int64_t>(rpc_client->get_gas_price());
const auto eth_gas_fee = double(estimate_gas * gas_price) / double{1000000000000000000}; const auto eth_gas_fee = double(estimate_gas * gas_price) / double{1000000000000000000};
return asset_itr->amount_from_string(std::to_string(eth_gas_fee));
const auto asset = database.get<asset_object>(database.get_global_properties().parameters.eth_asset());
return asset.amount_from_string(std::to_string(eth_gas_fee));
} }
std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(const std::vector<son_info> &son_pubkeys, const std::string &object_id) { std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(const std::vector<son_sidechain_info> &son_pubkeys, const std::string &object_id) {
std::vector<std::pair<std::string, uint16_t>> owners_weights; std::vector<std::pair<std::string, uint16_t>> owners_weights;
for (auto &son : son_pubkeys) { for (auto &son : son_pubkeys) {
const std::string pub_key_str = son.public_key; const std::string pub_key_str = son.public_key;
@ -739,12 +806,19 @@ std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(co
return ethereum::update_owners_encoder::encode(owners_weights, object_id); return ethereum::update_owners_encoder::encode(owners_weights, object_id);
} }
std::string sidechain_net_handler_ethereum::create_deposit_transaction(const son_wallet_deposit_object &swdo) {
return "Deposit-Transaction";
}
std::string sidechain_net_handler_ethereum::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) { std::string sidechain_net_handler_ethereum::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) {
return ethereum::withdrawal_encoder::encode(swwo.withdraw_address.substr(2), swwo.withdraw_amount.value * 10000000000, swwo.id.operator std::string()); if (swwo.withdraw_currency == "ETH") {
return ethereum::withdrawal_encoder::encode(ethereum::remove_0x(swwo.withdraw_address), boost::multiprecision::uint256_t{swwo.withdraw_amount.value} * boost::multiprecision::uint256_t{10000000000}, swwo.id.operator std::string());
} else {
const auto it = erc20_addresses.left.find(swwo.withdraw_currency);
if (it == erc20_addresses.left.end()) {
elog("No erc-20 token: ${symbol}", ("symbol", swwo.withdraw_currency));
return "";
}
return ethereum::withdrawal_erc20_encoder::encode(ethereum::remove_0x(it->second), ethereum::remove_0x(swwo.withdraw_address), boost::multiprecision::uint256_t{swwo.withdraw_amount.value}, swwo.id.operator std::string());
}
return "";
} }
std::string sidechain_net_handler_ethereum::sign_transaction(const sidechain_transaction_object &sto) { std::string sidechain_net_handler_ethereum::sign_transaction(const sidechain_transaction_object &sto) {
@ -761,10 +835,10 @@ std::string sidechain_net_handler_ethereum::sign_transaction(const sidechain_tra
} }
void sidechain_net_handler_ethereum::schedule_ethereum_listener() { void sidechain_net_handler_ethereum::schedule_ethereum_listener() {
fc::time_point now = fc::time_point::now(); const fc::time_point now = fc::time_point::now();
int64_t time_to_next = 5000; const int64_t time_to_next = 5000;
fc::time_point next_wakeup(now + fc::milliseconds(time_to_next)); const fc::time_point next_wakeup(now + fc::milliseconds(time_to_next));
_listener_task = fc::schedule([this] { _listener_task = fc::schedule([this] {
ethereum_listener_loop(); ethereum_listener_loop();
@ -776,10 +850,9 @@ void sidechain_net_handler_ethereum::ethereum_listener_loop() {
schedule_ethereum_listener(); schedule_ethereum_listener();
const auto reply = rpc_client->eth_blockNumber(); const auto reply = rpc_client->eth_blockNumber();
//std::string reply = rpc_client->eth_get_logs(wallet_contract_address);
if (!reply.empty()) { if (!reply.empty()) {
uint64_t head_block_number = ethereum::from_hex<uint64_t>(reply); const uint64_t head_block_number = ethereum::from_hex<uint64_t>(reply);
if (head_block_number != last_block_received) { if (head_block_number != last_block_received) {
//! Check that current block number is greater than last one //! Check that current block number is greater than last one
@ -814,11 +887,11 @@ void sidechain_net_handler_ethereum::handle_event(const std::string &block_numbe
size_t tx_idx = -1; size_t tx_idx = -1;
for (const auto &tx_child : block_json.get_child("result.transactions")) { for (const auto &tx_child : block_json.get_child("result.transactions")) {
boost::property_tree::ptree tx = tx_child.second; const boost::property_tree::ptree tx = tx_child.second;
tx_idx = tx_idx + 1; tx_idx = tx_idx + 1;
std::string from = tx.get<std::string>("from"); const std::string from = tx.get<std::string>("from");
std::string to = tx.get<std::string>("to"); const std::string to = tx.get<std::string>("to");
std::string cmp_to = to; std::string cmp_to = to;
std::transform(cmp_to.begin(), cmp_to.end(), cmp_to.begin(), ::toupper); std::transform(cmp_to.begin(), cmp_to.end(), cmp_to.begin(), ::toupper);
@ -827,10 +900,35 @@ void sidechain_net_handler_ethereum::handle_event(const std::string &block_numbe
if (cmp_to == cmp_wallet_contract_address) { if (cmp_to == cmp_wallet_contract_address) {
std::string value_s = tx.get<std::string>("value"); //! Check whether it is ERC-20 token deposit
boost::multiprecision::uint256_t amount(value_s); std::string symbol;
boost::multiprecision::uint256_t amount;
const auto deposit_erc_20 = ethereum::deposit_erc20_decoder::decode(tx.get<std::string>("input"));
if (deposit_erc_20.valid()) {
std::string cmp_token = deposit_erc_20->token;
std::transform(cmp_token.begin(), cmp_token.end(), cmp_token.begin(), ::tolower);
const auto it = erc20_addresses.right.find(cmp_token);
if (it == erc20_addresses.right.end()) {
wlog("No erc-20 token with address: ${address}", ("address", cmp_token));
continue;
}
symbol = it->second;
amount = deposit_erc_20->amount;
} else {
symbol = "ETH";
const std::string value_s = tx.get<std::string>("value");
amount = boost::multiprecision::uint256_t{value_s};
amount = amount / 100000; amount = amount / 100000;
amount = amount / 100000; amount = amount / 100000;
}
const auto &assets_by_symbol = database.get_index_type<asset_index>().indices().get<by_symbol>();
const auto asset_itr = assets_by_symbol.find(symbol);
if (asset_itr == assets_by_symbol.end()) {
wlog("Could not find asset: ${symbol}", ("symbol", symbol));
continue;
}
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_address_and_expires>(); const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_address_and_expires>();
const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, from, time_point_sec::maximum())); const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, from, time_point_sec::maximum()));
@ -841,22 +939,22 @@ void sidechain_net_handler_ethereum::handle_event(const std::string &block_numbe
std::stringstream ss; std::stringstream ss;
ss << "ethereum" ss << "ethereum"
<< "-" << tx.get<std::string>("hash") << "-" << tx_idx; << "-" << tx.get<std::string>("hash") << "-" << tx_idx;
std::string sidechain_uid = ss.str();
sidechain_event_data sed; sidechain_event_data sed;
sed.timestamp = database.head_block_time(); sed.timestamp = database.head_block_time();
sed.block_num = database.head_block_num(); sed.block_num = database.head_block_num();
sed.sidechain = sidechain; sed.sidechain = sidechain;
sed.sidechain_uid = sidechain_uid; sed.type = sidechain_event_type::deposit;
sed.sidechain_uid = ss.str();
sed.sidechain_transaction_id = tx.get<std::string>("hash"); sed.sidechain_transaction_id = tx.get<std::string>("hash");
sed.sidechain_from = from; sed.sidechain_from = from;
sed.sidechain_to = to; sed.sidechain_to = to;
sed.sidechain_currency = "ETH"; sed.sidechain_currency = symbol;
sed.sidechain_amount = amount; sed.sidechain_amount = amount;
sed.peerplays_from = addr_itr->sidechain_address_account; sed.peerplays_from = addr_itr->sidechain_address_account;
sed.peerplays_to = database.get_global_properties().parameters.son_account(); sed.peerplays_to = database.get_global_properties().parameters.son_account();
price eth_price = database.get<asset_object>(database.get_global_properties().parameters.eth_asset()).options.core_exchange_rate; const price price = asset_itr->options.core_exchange_rate;
sed.peerplays_asset = asset(sed.sidechain_amount * eth_price.base.amount / eth_price.quote.amount); sed.peerplays_asset = asset(sed.sidechain_amount * price.base.amount / price.quote.amount);
add_to_son_listener_log("TRX : " + sed.sidechain_transaction_id); add_to_son_listener_log("TRX : " + sed.sidechain_transaction_id);

View file

@ -20,7 +20,7 @@
#include <graphene/chain/account_object.hpp> #include <graphene/chain/account_object.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp> #include <graphene/chain/protocol/fee_schedule.hpp>
#include <graphene/chain/protocol/son_wallet.hpp> #include <graphene/chain/protocol/son_wallet.hpp>
#include <graphene/chain/son_info.hpp> #include <graphene/chain/son_sidechain_info.hpp>
#include <graphene/chain/son_wallet_object.hpp> #include <graphene/chain/son_wallet_object.hpp>
#include <graphene/peerplays_sidechain/common/utils.hpp> #include <graphene/peerplays_sidechain/common/utils.hpp>
#include <graphene/peerplays_sidechain/hive/asset.hpp> #include <graphene/peerplays_sidechain/hive/asset.hpp>
@ -209,12 +209,15 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) {
bool address_ok = false; bool address_ok = false;
bool transaction_ok = false; bool transaction_ok = false;
son_wallet_id_type swo_id = op_obj_idx_0.get<son_wallet_update_operation>().son_wallet_id; son_wallet_id_type swo_id = op_obj_idx_0.get<son_wallet_update_operation>().son_wallet_id;
const auto ast = active_sidechain_types(database.head_block_time());
const auto id = (swo_id.instance.value - std::distance(ast.begin(), ast.find(sidechain))) / ast.size();
const son_wallet_id_type op_id{id};
const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>(); const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
const auto swo = idx.find(swo_id); const auto swo = idx.find(op_id);
if (swo != idx.end()) { if (swo != idx.end()) {
auto active_sons = gpo.active_sons.at(sidechain); const auto &active_sons = gpo.active_sons.at(sidechain);
vector<son_info> wallet_sons = swo->sons.at(sidechain); const auto &wallet_sons = swo->sons.at(sidechain);
bool son_sets_equal = (active_sons.size() == wallet_sons.size()); bool son_sets_equal = (active_sons.size() == wallet_sons.size());
@ -229,18 +232,20 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) {
} }
if (po.proposed_transaction.operations.size() >= 2) { if (po.proposed_transaction.operations.size() >= 2) {
object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id; const object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id;
const auto id = (object_id.instance() - std::distance(ast.begin(), ast.find(sidechain))) / ast.size();
const object_id_type obj_id{object_id.space(), object_id.type(), id};
std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction; std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction;
const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_object_id>(); const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_object_id>();
const auto st = st_idx.find(object_id); const auto st = st_idx.find(obj_id);
if (st == st_idx.end()) { if (st == st_idx.end()) {
std::string tx_str = ""; std::string tx_str = "";
if (object_id.is<son_wallet_id_type>()) { if (obj_id.is<son_wallet_id_type>()) {
const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>(); const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
const auto swo = idx.find(object_id); const auto swo = idx.find(obj_id);
if (swo != idx.end()) { if (swo != idx.end()) {
std::stringstream ss_trx(boost::algorithm::unhex(op_tx_str)); std::stringstream ss_trx(boost::algorithm::unhex(op_tx_str));
@ -486,7 +491,11 @@ void sidechain_net_handler_hive::process_primary_wallet() {
if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) ||
(active_sw->addresses.at(sidechain).empty())) { (active_sw->addresses.at(sidechain).empty())) {
if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id)) { const auto ast = active_sidechain_types(database.head_block_time());
const auto id = active_sw->id.instance() * ast.size() + std::distance(ast.begin(), ast.find(sidechain));
const object_id_type op_id{active_sw->id.space(), active_sw->id.type(), id};
if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, op_id)) {
return; return;
} }
@ -542,14 +551,14 @@ void sidechain_net_handler_hive::process_primary_wallet() {
son_wallet_update_operation swu_op; son_wallet_update_operation swu_op;
swu_op.payer = gpo.parameters.son_account(); swu_op.payer = gpo.parameters.son_account();
swu_op.son_wallet_id = active_sw->id; swu_op.son_wallet_id = op_id;
swu_op.sidechain = sidechain; swu_op.sidechain = sidechain;
swu_op.address = wallet_account_name; swu_op.address = wallet_account_name;
proposal_op.proposed_ops.emplace_back(swu_op); proposal_op.proposed_ops.emplace_back(swu_op);
const auto signers = [this, &prev_sw, &active_sw, &swi] { const auto signers = [this, &prev_sw, &active_sw, &swi] {
std::vector<son_info> signers; std::vector<son_sidechain_info> signers;
//! Check if we don't have any previous set of active SONs use the current one //! Check if we don't have any previous set of active SONs use the current one
if (prev_sw != swi.rend()) { if (prev_sw != swi.rend()) {
if (!prev_sw->sons.at(sidechain).empty()) if (!prev_sw->sons.at(sidechain).empty())
@ -565,11 +574,17 @@ void sidechain_net_handler_hive::process_primary_wallet() {
sidechain_transaction_create_operation stc_op; sidechain_transaction_create_operation stc_op;
stc_op.payer = gpo.parameters.son_account(); stc_op.payer = gpo.parameters.son_account();
stc_op.object_id = active_sw->id; stc_op.object_id = op_id;
stc_op.sidechain = sidechain; stc_op.sidechain = sidechain;
stc_op.transaction = tx_str; stc_op.transaction = tx_str;
stc_op.signers = signers; for (const auto &signer : gpo.active_sons.at(sidechain)) {
son_info si;
si.son_id = signer.son_id;
si.weight = signer.weight;
si.signing_key = signer.signing_key;
si.sidechain_public_keys[sidechain] = signer.public_key;
stc_op.signers.emplace_back(std::move(si));
}
proposal_op.proposed_ops.emplace_back(stc_op); proposal_op.proposed_ops.emplace_back(stc_op);
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op);
@ -725,7 +740,14 @@ bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_ob
stc_op.object_id = swwo.id; stc_op.object_id = swwo.id;
stc_op.sidechain = sidechain; stc_op.sidechain = sidechain;
stc_op.transaction = tx_str; stc_op.transaction = tx_str;
stc_op.signers = gpo.active_sons.at(sidechain); for (const auto &signer : gpo.active_sons.at(sidechain)) {
son_info si;
si.son_id = signer.son_id;
si.weight = signer.weight;
si.signing_key = signer.signing_key;
si.sidechain_public_keys[sidechain] = signer.public_key;
stc_op.signers.emplace_back(std::move(si));
}
proposal_op.proposed_ops.emplace_back(stc_op); proposal_op.proposed_ops.emplace_back(stc_op);
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op);
@ -950,6 +972,7 @@ void sidechain_net_handler_hive::handle_event(const std::string &event_data) {
sed.timestamp = database.head_block_time(); sed.timestamp = database.head_block_time();
sed.block_num = database.head_block_num(); sed.block_num = database.head_block_num();
sed.sidechain = sidechain; sed.sidechain = sidechain;
sed.type = sidechain_event_type::deposit;
sed.sidechain_uid = sidechain_uid; sed.sidechain_uid = sidechain_uid;
sed.sidechain_transaction_id = transaction_id; sed.sidechain_transaction_id = transaction_id;
sed.sidechain_from = from; sed.sidechain_from = from;

View file

@ -197,7 +197,14 @@ bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_o
stc_op.object_id = swdo.id; stc_op.object_id = swdo.id;
stc_op.sidechain = sidechain; stc_op.sidechain = sidechain;
stc_op.transaction = tx_str; stc_op.transaction = tx_str;
stc_op.signers = gpo.active_sons.at(sidechain); for (const auto &signer : gpo.active_sons.at(sidechain)) {
son_info si;
si.son_id = signer.son_id;
si.weight = signer.weight;
si.signing_key = signer.signing_key;
si.sidechain_public_keys[sidechain] = signer.public_key;
stc_op.signers.emplace_back(std::move(si));
}
proposal_create_operation proposal_op; proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account;

View file

@ -1559,14 +1559,14 @@ class wallet_api
* @brief Get list of active sons * @brief Get list of active sons
* @return List of active SONs * @return List of active SONs
*/ */
flat_map<sidechain_type, vector<son_info>> get_active_sons(); flat_map<sidechain_type, vector<son_sidechain_info>> get_active_sons();
/** /**
* @brief Get list of active sons * @brief Get list of active sons
* @param sidechain Sidechain type [bitcoin|ethereum|hive] * @param sidechain Sidechain type [bitcoin|ethereum|hive]
* @return List of active SONs * @return List of active SONs
*/ */
vector<son_info> get_active_sons_by_sidechain(sidechain_type sidechain); vector<son_sidechain_info> get_active_sons_by_sidechain(sidechain_type sidechain);
/** /**
* @brief Get SON network status * @brief Get SON network status

View file

@ -2230,12 +2230,12 @@ public:
return sign_transaction( tx, broadcast ); return sign_transaction( tx, broadcast );
} FC_CAPTURE_AND_RETHROW( (owner_account) ) } } FC_CAPTURE_AND_RETHROW( (owner_account) ) }
flat_map<sidechain_type, vector<son_info>> get_active_sons() flat_map<sidechain_type, vector<son_sidechain_info>> get_active_sons()
{ try { { try {
return _remote_db->get_active_sons(); return _remote_db->get_active_sons();
} FC_CAPTURE_AND_RETHROW() } } FC_CAPTURE_AND_RETHROW() }
vector<son_info> get_active_sons_by_sidechain(sidechain_type sidechain) vector<son_sidechain_info> get_active_sons_by_sidechain(sidechain_type sidechain)
{ try { { try {
return _remote_db->get_active_sons_by_sidechain(sidechain); return _remote_db->get_active_sons_by_sidechain(sidechain);
} FC_CAPTURE_AND_RETHROW() } } FC_CAPTURE_AND_RETHROW() }
@ -2775,13 +2775,15 @@ public:
if (approve) if (approve)
{ {
auto insert_result = voting_account_object.options.votes.insert(son_obj->get_sidechain_vote_id(sidechain)); FC_ASSERT(son_obj->get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", *son_obj));
auto insert_result = voting_account_object.options.votes.insert(*son_obj->get_sidechain_vote_id(sidechain));
if (!insert_result.second) if (!insert_result.second)
FC_THROW("Account ${account} has already voted for son ${son} for sidechain ${sidechain}", ("account", voting_account)("son", son)("sidechain", sidechain)); FC_THROW("Account ${account} has already voted for son ${son} for sidechain ${sidechain}", ("account", voting_account)("son", son)("sidechain", sidechain));
} }
else else
{ {
unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->get_sidechain_vote_id(sidechain)); FC_ASSERT(son_obj->get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", *son_obj));
unsigned votes_removed = voting_account_object.options.votes.erase(*son_obj->get_sidechain_vote_id(sidechain));
if (!votes_removed) if (!votes_removed)
FC_THROW("Account ${account} has already unvoted for son ${son} for sidechain ${sidechain}", ("account", voting_account)("son", son)("sidechain", sidechain)); FC_THROW("Account ${account} has already unvoted for son ${son} for sidechain ${sidechain}", ("account", voting_account)("son", son)("sidechain", sidechain));
} }
@ -2819,7 +2821,8 @@ public:
FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son));
FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type"); FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type");
auto insert_result = voting_account_object.options.votes.insert(son_obj->get_sidechain_vote_id(sidechain)); FC_ASSERT(son_obj->get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", *son_obj));
auto insert_result = voting_account_object.options.votes.insert(*son_obj->get_sidechain_vote_id(sidechain));
if (!insert_result.second) if (!insert_result.second)
FC_THROW("Account ${account} was already voting for SON ${son}", ("account", voting_account)("son", son)); FC_THROW("Account ${account} was already voting for SON ${son}", ("account", voting_account)("son", son));
} }
@ -2830,7 +2833,8 @@ public:
FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son));
FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type"); FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type");
unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->get_sidechain_vote_id(sidechain)); FC_ASSERT(son_obj->get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", *son_obj));
unsigned votes_removed = voting_account_object.options.votes.erase(*son_obj->get_sidechain_vote_id(sidechain));
if (!votes_removed) if (!votes_removed)
FC_THROW("Account ${account} is already not voting for SON ${son}", ("account", voting_account)("son", son)); FC_THROW("Account ${account} is already not voting for SON ${son}", ("account", voting_account)("son", son));
} }
@ -5294,12 +5298,12 @@ map<string, son_id_type> wallet_api::list_sons(const string& lowerbound, uint32_
return my->_remote_db->lookup_son_accounts(lowerbound, limit); return my->_remote_db->lookup_son_accounts(lowerbound, limit);
} }
flat_map<sidechain_type, vector<son_info>> wallet_api::get_active_sons() flat_map<sidechain_type, vector<son_sidechain_info>> wallet_api::get_active_sons()
{ {
return my->get_active_sons(); return my->get_active_sons();
} }
vector<son_info> wallet_api::get_active_sons_by_sidechain(sidechain_type sidechain) vector<son_sidechain_info> wallet_api::get_active_sons_by_sidechain(sidechain_type sidechain)
{ {
return my->get_active_sons_by_sidechain(sidechain); return my->get_active_sons_by_sidechain(sidechain);
} }

View file

@ -39,7 +39,7 @@ public:
fixture_(fixture) fixture_(fixture)
{ {
fixture_.init_nathan(); fixture_.init_nathan();
fixture_.generate_blocks(HARDFORK_SON3_TIME); fixture_.generate_blocks(HARDFORK_SON_FOR_ETHEREUM_TIME);
fixture_.generate_block(); fixture_.generate_block();
} }
@ -143,9 +143,12 @@ BOOST_AUTO_TEST_CASE( create_sons )
BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1"); BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1");
BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::hive], "hive account 1"); BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::hive], "hive account 1");
BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 1"); BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 1");
BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 22); BOOST_REQUIRE(son1_obj.get_sidechain_vote_id(sidechain_type::bitcoin));
BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 23); BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::bitcoin)->instance(), 22);
BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 24); BOOST_REQUIRE(son1_obj.get_sidechain_vote_id(sidechain_type::hive));
BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::hive)->instance(), 23);
BOOST_REQUIRE(son1_obj.get_sidechain_vote_id(sidechain_type::ethereum));
BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::ethereum)->instance(), 24);
auto son2_obj = con.wallet_api_ptr->get_son("son2account"); auto son2_obj = con.wallet_api_ptr->get_son("son2account");
BOOST_CHECK(son2_obj.son_account == con.wallet_api_ptr->get_account_id("son2account")); BOOST_CHECK(son2_obj.son_account == con.wallet_api_ptr->get_account_id("son2account"));
@ -153,9 +156,12 @@ BOOST_AUTO_TEST_CASE( create_sons )
BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 2"); BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 2");
BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::hive], "hive account 2"); BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::hive], "hive account 2");
BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 2"); BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 2");
BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 25); BOOST_REQUIRE(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin));
BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 26); BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin)->instance(), 25);
BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 27); BOOST_REQUIRE(son2_obj.get_sidechain_vote_id(sidechain_type::hive));
BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::hive)->instance(), 26);
BOOST_REQUIRE(son2_obj.get_sidechain_vote_id(sidechain_type::ethereum));
BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::ethereum)->instance(), 27);
} catch( fc::exception& e ) { } catch( fc::exception& e ) {
BOOST_TEST_MESSAGE("SON cli wallet tests exception"); BOOST_TEST_MESSAGE("SON cli wallet tests exception");
@ -190,9 +196,12 @@ BOOST_AUTO_TEST_CASE( cli_update_son )
BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1"); BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1");
BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::hive], "hive account 1"); BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::hive], "hive account 1");
BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 1"); BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 1");
BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 22); BOOST_REQUIRE(son_data.get_sidechain_vote_id(sidechain_type::bitcoin));
BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::hive).instance(), 23); BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::bitcoin)->instance(), 22);
BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 24); BOOST_REQUIRE(son_data.get_sidechain_vote_id(sidechain_type::hive));
BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::hive)->instance(), 23);
BOOST_REQUIRE(son_data.get_sidechain_vote_id(sidechain_type::ethereum));
BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::ethereum)->instance(), 24);
// update SON // update SON
sidechain_public_keys.clear(); sidechain_public_keys.clear();
@ -1400,7 +1409,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture )
// Check Network Status Before sending Heartbeats // Check Network Status Before sending Heartbeats
BOOST_CHECK(generate_maintenance_block()); BOOST_CHECK(generate_maintenance_block());
for(sidechain_type sidechain:active_sidechain_types) for(sidechain_type sidechain : all_sidechain_types)
{ {
auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain);
for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter)
@ -1448,7 +1457,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture )
generate_blocks(50); generate_blocks(50);
BOOST_TEST_MESSAGE("Checking Network Status"); BOOST_TEST_MESSAGE("Checking Network Status");
for(sidechain_type sidechain:active_sidechain_types) for(sidechain_type sidechain : all_sidechain_types)
{ {
auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain);
for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter)
@ -1479,7 +1488,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture )
con.wallet_api_ptr->sign_transaction(trx2, true); con.wallet_api_ptr->sign_transaction(trx2, true);
generate_blocks(50); generate_blocks(50);
for(sidechain_type sidechain:active_sidechain_types) for(sidechain_type sidechain : all_sidechain_types)
{ {
auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain);
for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter)
@ -1507,7 +1516,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture )
generate_blocks(db->head_block_time() + gpo.parameters.son_heartbeat_frequency(), false); generate_blocks(db->head_block_time() + gpo.parameters.son_heartbeat_frequency(), false);
BOOST_TEST_MESSAGE("Checking Network Status"); BOOST_TEST_MESSAGE("Checking Network Status");
for(sidechain_type sidechain:active_sidechain_types) for(sidechain_type sidechain : all_sidechain_types)
{ {
auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain);
for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter)
@ -1535,7 +1544,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture )
generate_blocks(db->head_block_time() + gpo.parameters.son_heartbeat_frequency() + gpo.parameters.son_down_time(), false);; generate_blocks(db->head_block_time() + gpo.parameters.son_heartbeat_frequency() + gpo.parameters.son_down_time(), false);;
BOOST_TEST_MESSAGE("Checking Network Status"); BOOST_TEST_MESSAGE("Checking Network Status");
for(sidechain_type sidechain:active_sidechain_types) for(sidechain_type sidechain : all_sidechain_types)
{ {
auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain);
for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter)

View file

@ -2,6 +2,7 @@
#include <fc/crypto/hex.hpp> #include <fc/crypto/hex.hpp>
#include <graphene/peerplays_sidechain/ethereum/encoders.hpp> #include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/decoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/transaction.hpp> #include <graphene/peerplays_sidechain/ethereum/transaction.hpp>
using namespace graphene::peerplays_sidechain::ethereum; using namespace graphene::peerplays_sidechain::ethereum;
@ -9,22 +10,43 @@ using namespace graphene::peerplays_sidechain::ethereum;
BOOST_AUTO_TEST_SUITE(ethereum_transaction_tests) BOOST_AUTO_TEST_SUITE(ethereum_transaction_tests)
BOOST_AUTO_TEST_CASE(withdrawal_encoder_test) { BOOST_AUTO_TEST_CASE(withdrawal_encoder_test) {
const auto tx = withdrawal_encoder::encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.0"); const auto tx = withdrawal_encoder::encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.39.0");
BOOST_CHECK_EQUAL(tx, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E300000000000000000000000000000000000000000000000000000"); BOOST_CHECK_EQUAL(tx, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000");
const auto tx1 = withdrawal_encoder::encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.1"); const auto tx1 = withdrawal_encoder::encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.39.1");
BOOST_CHECK_EQUAL(tx1, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); BOOST_CHECK_EQUAL(tx1, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33392E310000000000000000000000000000000000000000000000000000");
} }
BOOST_AUTO_TEST_CASE(withdrawal_signature_encoder_test) { BOOST_AUTO_TEST_CASE(withdrawal_signature_encoder_test) {
const signature_encoder encoder{withdrawal_function_signature};
encoded_sign_transaction transaction; encoded_sign_transaction transaction;
transaction.data = "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E300000000000000000000000000000000000000000000000000000"; transaction.data = "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000";
transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" ); transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" );
const auto function_signature = signature_encoder::get_function_signature_from_transaction(transaction.data);
BOOST_REQUIRE_EQUAL(function_signature.empty(), false);
const signature_encoder encoder{function_signature};
const auto tx = encoder.encode({transaction}); const auto tx = encoder.encode({transaction});
BOOST_CHECK_EQUAL(tx, "0xdaac6c8100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000065c4622d2ff2b2d89c5c6f8225ab0f979bc69d4fcd4fd47db757b66fb8a39e2bc5522be5d101aa11e66da78db973f136b323be10bd107ff0b648f06b4c71ef2a4f00000000000000000000000000000000000000000000000000000000000000a4e088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); BOOST_CHECK_EQUAL(tx, "0xdaac6c810000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006689c3a93d7059430d19ff952900dfada310c0dcced9ed046c335f886091c7e50c1a01016a488777b41a1815ca01a7d809ed47c36dcb0d5f86a43b079ce0d04afe00000000000000000000000000000000000000000000000000000000000000a4e088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33392E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
}
BOOST_AUTO_TEST_CASE(withdrawal_erc20_encoder_test) {
const auto tx = withdrawal_erc20_encoder::encode("cc806da9df9d634b5dac0aa36dca1e7780e42C60", "5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10, "1.39.0");
BOOST_CHECK_EQUAL(tx, "0x483c0467000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42C600000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc12000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000");
const auto tx1 = withdrawal_erc20_encoder::encode("cc806da9df9d634b5dac0aa36dca1e7780e42C60", "5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10, "1.39.1");
BOOST_CHECK_EQUAL(tx1, "0x483c0467000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42C600000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc12000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000006312E33392E310000000000000000000000000000000000000000000000000000");
}
BOOST_AUTO_TEST_CASE(withdrawal_erc20_signature_encoder_test) {
encoded_sign_transaction transaction;
transaction.data = "0x483c0467000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42C600000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc12000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000";
transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" );
const auto function_signature = signature_encoder::get_function_signature_from_transaction(transaction.data);
BOOST_REQUIRE_EQUAL(function_signature.empty(), false);
const signature_encoder encoder{function_signature};
const auto tx = encoder.encode({transaction});
BOOST_CHECK_EQUAL(tx, "0xd2bf286600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000066f51f7732435016936f0e21aa3c290023ea96ddbc369a957aca28a865cb5004a46675855fccd4bd5a283e1ff61aa60ca9b8b63664e770689e5cfc1a0c6bbdc79a00000000000000000000000000000000000000000000000000000000000000c4483c0467000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42C600000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc12000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000006312E33392E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
} }
BOOST_AUTO_TEST_CASE(update_owners_encoder_test) { BOOST_AUTO_TEST_CASE(update_owners_encoder_test) {
@ -32,23 +54,36 @@ BOOST_AUTO_TEST_CASE(update_owners_encoder_test) {
owners_weights.emplace_back("5FbBb31BE52608D2F52247E8400B7fCaA9E0bC12", 1); owners_weights.emplace_back("5FbBb31BE52608D2F52247E8400B7fCaA9E0bC12", 1);
owners_weights.emplace_back("76ce31bd03f601c3fc13732def921c5bac282676", 1); owners_weights.emplace_back("76ce31bd03f601c3fc13732def921c5bac282676", 1);
const auto tx = update_owners_encoder::encode(owners_weights, "1.35.0"); const auto tx = update_owners_encoder::encode(owners_weights, "1.39.0");
BOOST_CHECK_EQUAL(tx, "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000"); BOOST_CHECK_EQUAL(tx, "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000");
owners_weights.emplace_back("09ee460834498a4ee361beb819470061b7381b49", 1); owners_weights.emplace_back("09ee460834498a4ee361beb819470061b7381b49", 1);
const auto tx1 = update_owners_encoder::encode(owners_weights, "1.36.1"); const auto tx1 = update_owners_encoder::encode(owners_weights, "1.39.1");
BOOST_CHECK_EQUAL(tx1, "0x23ab6adf0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac282676000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009ee460834498a4ee361beb819470061b7381b4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); BOOST_CHECK_EQUAL(tx1, "0x23ab6adf0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac282676000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009ee460834498a4ee361beb819470061b7381b4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33392E310000000000000000000000000000000000000000000000000000");
} }
BOOST_AUTO_TEST_CASE(update_owners_signature_encoder_test) { BOOST_AUTO_TEST_CASE(update_owners_signature_encoder_test) {
const signature_encoder encoder{update_owners_function_signature};
encoded_sign_transaction transaction; encoded_sign_transaction transaction;
transaction.data = "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000"; transaction.data = "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000";
transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" ); transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" );
const auto function_signature = signature_encoder::get_function_signature_from_transaction(transaction.data);
BOOST_REQUIRE_EQUAL(function_signature.empty(), false);
const signature_encoder encoder{function_signature};
const auto tx = encoder.encode({transaction}); const auto tx = encoder.encode({transaction});
BOOST_CHECK_EQUAL(tx, "0x9d6086730000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006698877eafa525c1a55f6b5e0a7187dfae484c97d9f77c4421a00276a9408a3e713d24402b44c05a883142fcffa84e1a802be37c17bb360f6f4810eb0415c8bbfd000000000000000000000000000000000000000000000000000000000000012423ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); BOOST_CHECK_EQUAL(tx, "0x9d608673000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000667121da3c6ab5054b1a77cac477f24e7ce7bfcb5e3a857cfcaf48e67fc8f003ac38dfa8821525383608a68c9f215f9a2a232e192ae80079cd2f31b0e01caa6e1d000000000000000000000000000000000000000000000000000000000000012423ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33392E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
}
BOOST_AUTO_TEST_CASE(deposit_erc20_decoder_test) {
const auto erc_20_1 = deposit_erc20_decoder::decode("0x97feb926000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42c600000000000000000000000000000000000000000000000000000000000000064");
BOOST_REQUIRE_EQUAL(erc_20_1.valid(), true);
BOOST_CHECK_EQUAL(erc_20_1->token, "0xcc806da9df9d634b5dac0aa36dca1e7780e42c60");
BOOST_CHECK_EQUAL(erc_20_1->amount, 100);
const auto erc_20_2 = deposit_erc20_decoder::decode("0x97feb926000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42c600000000000000000000000000000000000000000000000006400000000000000");
BOOST_REQUIRE_EQUAL(erc_20_2.valid(), true);
BOOST_CHECK_EQUAL(erc_20_2->token, "0xcc806da9df9d634b5dac0aa36dca1e7780e42c60");
BOOST_CHECK_EQUAL(erc_20_2->amount, 7205759403792793600);
} }
BOOST_AUTO_TEST_CASE(raw_transaction_serialization_test) { BOOST_AUTO_TEST_CASE(raw_transaction_serialization_test) {

View file

@ -1038,7 +1038,7 @@ BOOST_FIXTURE_TEST_CASE( hardfork_son2_time, database_fixture )
generate_block(); // get the maintenance skip slots out of the way*/ generate_block(); // get the maintenance skip slots out of the way*/
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), 7); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), 7);
generate_blocks(HARDFORK_SON3_TIME); generate_blocks(HARDFORK_SON_FOR_ETHEREUM_TIME);
// after this hardfork maximum son account should not reset the value // after this hardfork maximum son account should not reset the value
// on 7 after maintenance interval anymore. It must be HARDFORK_SON2_TIME // on 7 after maintenance interval anymore. It must be HARDFORK_SON2_TIME
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), GRAPHENE_DEFAULT_MAX_SONS); BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), GRAPHENE_DEFAULT_MAX_SONS);

View file

@ -15,7 +15,7 @@ using namespace graphene::chain::test;
BOOST_FIXTURE_TEST_SUITE( son_operation_tests, database_fixture ) BOOST_FIXTURE_TEST_SUITE( son_operation_tests, database_fixture )
BOOST_AUTO_TEST_CASE( create_son_test ) { BOOST_AUTO_TEST_CASE( create_son_test ) {
generate_blocks(HARDFORK_SON_TIME); generate_blocks(HARDFORK_SON_FOR_ETHEREUM_TIME);
generate_block(); generate_block();
set_expiration(db, trx); set_expiration(db, trx);
@ -314,7 +314,7 @@ BOOST_AUTO_TEST_CASE( son_pay_test )
const dynamic_global_property_object& dpo = db.get_dynamic_global_properties(); const dynamic_global_property_object& dpo = db.get_dynamic_global_properties();
const auto block_interval = db.get_global_properties().parameters.block_interval; const auto block_interval = db.get_global_properties().parameters.block_interval;
BOOST_CHECK( dpo.son_budget.value == 0); BOOST_CHECK( dpo.son_budget.value == 0);
generate_blocks(HARDFORK_SON_TIME); generate_blocks(HARDFORK_SON_FOR_ETHEREUM_TIME);
while (db.head_block_time() <= HARDFORK_SON_TIME) { while (db.head_block_time() <= HARDFORK_SON_TIME) {
generate_block(); generate_block();
} }
@ -634,16 +634,18 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
// Modify SON's status to active // Modify SON's status to active
db.modify( *obj, [&]( son_object& _s) db.modify( *obj, [&]( son_object& _s)
{ {
_s.statuses[sidechain_type::bitcoin] = son_status::active; for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
_s.statuses[sidechain_type::hive] = son_status::active; {
_s.statuses[sidechain_type::ethereum] = son_status::active; _s.statuses[active_sidechain_type] = son_status::active;
}
}); });
db.modify( *son_stats_obj, [&]( son_statistics_object& _s) db.modify( *son_stats_obj, [&]( son_statistics_object& _s)
{ {
_s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time()); for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
_s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time()); {
_s.last_down_timestamp[sidechain_type::ethereum] = fc::time_point_sec(db.head_block_time()); _s.last_down_timestamp[active_sidechain_type] = fc::time_point_sec(db.head_block_time());
}
}); });
{ {
@ -660,9 +662,10 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
PUSH_TX( db, trx, ~0); PUSH_TX( db, trx, ~0);
generate_block(); generate_block();
trx.clear(); trx.clear();
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance); for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::request_maintenance); {
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::request_maintenance); BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::request_maintenance);
}
} }
{ {
@ -679,23 +682,26 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
PUSH_TX( db, trx, ~0); PUSH_TX( db, trx, ~0);
generate_block(); generate_block();
trx.clear(); trx.clear();
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); {
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::active);
}
} }
// Modify SON's status to in_maintenance // Modify SON's status to in_maintenance
db.modify( *obj, [&]( son_object& _s) db.modify( *obj, [&]( son_object& _s)
{ {
_s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
_s.statuses[sidechain_type::hive] = son_status::in_maintenance; {
_s.statuses[sidechain_type::ethereum] = son_status::in_maintenance; _s.statuses[active_sidechain_type] = son_status::in_maintenance;
}
}); });
flat_map<sidechain_type, uint64_t> downtime; flat_map<sidechain_type, uint64_t> downtime;
downtime[sidechain_type::bitcoin] = 0; for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
downtime[sidechain_type::hive] = 0; {
downtime[sidechain_type::ethereum] = 0; downtime[active_sidechain_type] = 0;
}
{ {
generate_block(); generate_block();
@ -711,36 +717,34 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
PUSH_TX( db, trx, ~0); PUSH_TX( db, trx, ~0);
generate_block(); generate_block();
trx.clear(); trx.clear();
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch());
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch()); for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch()); {
downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch(); BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(active_sidechain_type), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(active_sidechain_type).sec_since_epoch());
downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch(); downtime[active_sidechain_type] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(active_sidechain_type).sec_since_epoch();
downtime[sidechain_type::ethereum] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch(); BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::inactive);
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::inactive); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(active_sidechain_type) == op.ts);
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::inactive); }
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::inactive);
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts);
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts);
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts);
} }
// Modify SON's status to in_maintenance // Modify SON's status to in_maintenance
db.modify( *obj, [&]( son_object& _s) db.modify( *obj, [&]( son_object& _s)
{ {
_s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
_s.statuses[sidechain_type::hive] = son_status::in_maintenance; {
_s.statuses[sidechain_type::ethereum] = son_status::in_maintenance; _s.statuses[active_sidechain_type] = son_status::in_maintenance;
}
}); });
// SON is selected as one of the active SONs // SON is selected as one of the active SONs
db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
{ {
son_info son_inf; son_sidechain_info son_inf;
son_inf.son_id = son_id_type(0); son_inf.son_id = son_id_type(0);
_gpo.active_sons[sidechain_type::bitcoin].push_back(son_inf); for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
_gpo.active_sons[sidechain_type::hive].push_back(son_inf); {
_gpo.active_sons[sidechain_type::ethereum].push_back(son_inf); _gpo.active_sons[active_sidechain_type].push_back(son_inf);
}
}); });
{ {
@ -758,18 +762,13 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
generate_block(); generate_block();
trx.clear(); trx.clear();
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch()); for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch()); {
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), downtime.at(sidechain_type::ethereum) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch()); BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(active_sidechain_type), downtime.at(active_sidechain_type) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(active_sidechain_type).sec_since_epoch());
downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch(); downtime[active_sidechain_type] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(active_sidechain_type).sec_since_epoch();
downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch(); BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::active);
downtime[sidechain_type::ethereum] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch(); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(active_sidechain_type) == op.ts);
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); }
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active);
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active);
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts);
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts);
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts);
} }
{ {
@ -786,15 +785,13 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
PUSH_TX( db, trx, ~0); PUSH_TX( db, trx, ~0);
generate_block(); generate_block();
trx.clear(); trx.clear();
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin));
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive)); for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), downtime.at(sidechain_type::ethereum)); {
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(active_sidechain_type), downtime.at(active_sidechain_type));
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::active);
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(active_sidechain_type) == op.ts);
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); }
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts);
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts);
} }
} FC_LOG_AND_RETHROW() } FC_LOG_AND_RETHROW()
} }
@ -819,9 +816,10 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) {
auto son_stats_obj = sidx.find( obj->statistics ); auto son_stats_obj = sidx.find( obj->statistics );
BOOST_REQUIRE( son_stats_obj != sidx.end() ); BOOST_REQUIRE( son_stats_obj != sidx.end() );
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); {
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::active);
}
{ {
// Check that transaction fails if down_ts < last_active_timestamp // Check that transaction fails if down_ts < last_active_timestamp
@ -873,12 +871,11 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) {
generate_block(); generate_block();
trx.clear(); trx.clear();
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::in_maintenance); for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::in_maintenance); {
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::in_maintenance); BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::in_maintenance);
BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin) == op.down_ts); BOOST_CHECK( son_stats_obj->last_down_timestamp.at(active_sidechain_type) == op.down_ts);
BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::hive) == op.down_ts); }
BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum) == op.down_ts);
} }
{ {

View file

@ -160,8 +160,8 @@ BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) {
si.son_id = son_id_type(0); si.son_id = son_id_type(0);
si.weight = 1000; si.weight = 1000;
si.signing_key = alice_public_key; si.signing_key = alice_public_key;
si.public_key = ""; si.sidechain_public_keys[sidechain_type::bitcoin] = "";
op.sons[sidechain_type::bitcoin].push_back(si); op.sons.push_back(si);
} }
{ {
@ -169,8 +169,8 @@ BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) {
si.son_id = son_id_type(1); si.son_id = son_id_type(1);
si.weight = 1000; si.weight = 1000;
si.signing_key = bob_public_key; si.signing_key = bob_public_key;
si.public_key = ""; si.sidechain_public_keys[sidechain_type::bitcoin] = "";
op.sons[sidechain_type::bitcoin].push_back(si); op.sons.push_back(si);
} }
trx.operations.push_back(op); trx.operations.push_back(op);