Bitcoin SON based on libbitcoin

This commit is contained in:
Davor Hirunda 2023-02-06 22:48:40 +00:00 committed by serkixenos
parent 9c9aaa03d3
commit e9c7021e16
18 changed files with 1396 additions and 423 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

@ -2340,7 +2340,7 @@ 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);
if(son_obj.get_sidechain_vote_id(sidechain).valid()) { 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}); votes_for_sons[sidechain].emplace_back(votes_info_object{*son_obj.get_sidechain_vote_id(sidechain), son_obj.id});
} }
} }

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;
} }
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()); if (type == payment_type::P2WSH) {
address = segwit_addr::encode(hrp, 0, hash_data); 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());
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;
} }
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()); if (type == payment_type::P2WSH) {
address = segwit_addr::encode(hrp, 0, hash_data); 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());
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

@ -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,37 @@
#pragma once
#include <curl/curl.h>
#include <functional>
#include <map>
#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

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

@ -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,7 +196,7 @@ 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()("erc-20-address", bpo::value<vector<string>>()->composing()->multitoken(), 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)"); "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)");
@ -249,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");
} }

View file

@ -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) {
@ -106,11 +195,11 @@ void bitcoin_rpc_client::importmulti(const std::vector<multi_params> &address_or
//! Note //! Note
/* Creation time of the key expressed in UNIX epoch time, /* Creation time of the key expressed in UNIX epoch time,
or the string "now" to substitute the current synced blockchain time. The timestamp of the oldest or the string "now" to substitute the current synced blockchain time. The timestamp of the oldest
key will determine how far back blockchain rescans need to begin for missing wallet transactions. key will determine how far back blockchain rescans need to begin for missing wallet transactions.
"now" can be specified to bypass scanning, for keys which are known to never have been used, and "now" can be specified to bypass scanning, for keys which are known to never have been used, and
0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key
creation time of all keys being imported by the importmulti call will be scanned.*/ creation time of all keys being imported by the importmulti call will be scanned.*/
if (&param != &address_or_script_array.back()) { if (&param != &address_or_script_array.back()) {
argument_1 += ", "; argument_1 += ", ";
@ -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) {
if (!wallet_name.empty()) { std::string url = bitcoin_node_ip + ":" + std::to_string(rpc_port);
url = url + "/wallet/" + wallet_name; if (!wallet_name.empty()) {
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));
if (!wallet_name.empty()) {
bitcoin_client->loadwallet(wallet_name);
}
listener = std::unique_ptr<zmq_listener>(new zmq_listener(bitcoin_node_ip, bitcoin_node_zmq_port));
} else {
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));
} }
bitcoin_client = std::unique_ptr<bitcoin_rpc_client>(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls)); std::string chain_info = bitcoin_client->getblockchaininfo();
if (!wallet_name.empty()) {
bitcoin_client->loadwallet(wallet_name);
}
std::string blockchain_info = bitcoin_client->getblockchaininfo();
if (blockchain_info.empty()) {
elog("No Bitcoin node running at ${url}", ("url", url));
FC_ASSERT(false);
}
std::stringstream bci_ss(std::string(blockchain_info.begin(), blockchain_info.end()));
boost::property_tree::ptree bci_json;
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"); network_type = bitcoin_address::network::testnet;
if (chain.length() > 0) { } else if (chain_info == "regtest") {
if (chain == "test") { network_type = bitcoin_address::network::regtest;
network_type = bitcoin_address::network::testnet;
} else if (chain == "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;
@ -450,7 +787,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
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 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 auto id = (swo_id.instance.value - std::distance(ast.begin(), ast.find(sidechain))) / ast.size();
const son_wallet_id_type op_id{ id }; 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(op_id); const auto swo = idx.find(op_id);
if (swo != idx.end()) { if (swo != idx.end()) {
@ -491,7 +828,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
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 ast = active_sidechain_types(database.head_block_time()); 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 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 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>();
@ -533,50 +870,35 @@ 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) { for (auto &address : input.tx_address) {
if (bitcoin_major_version > 21) { if (address == swdo_address) {
std::string address = input.second.get<std::string>("scriptPubKey.address"); tx_address = address;
if (address == swdo_address) { break;
tx_address = address;
}
} else {
for (auto &address : input.second.get_child("scriptPubKey.addresses")) {
if (address.second.data() == swdo_address) {
tx_address = address.second.data();
break;
}
}
} }
std::string tx_amount_s = input.second.get<std::string>("value");
tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end());
tx_amount = std::stoll(tx_amount_s);
break;
} }
tx_amount = input.tx_amount;
break;
} }
process_ok = (swdo_txid == tx_txid) &&
(swdo_address == tx_address) &&
(swdo_amount == tx_amount) &&
(swdo_vout == tx_vout) &&
(gpo.parameters.son_bitcoin_min_tx_confirmations() <= tx_confirmations);
} }
process_ok = (swdo_txid == tx_txid) &&
(swdo_address == tx_address) &&
(swdo_amount == tx_amount) &&
(swdo_vout == tx_vout) &&
(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;
@ -724,7 +1046,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() {
const auto ast = active_sidechain_types(database.head_block_time()); 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 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 }; 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)) { if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, op_id)) {
return; return;
@ -765,7 +1087,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() {
if (!tx_str.empty()) { if (!tx_str.empty()) {
const auto ast = active_sidechain_types(database.head_block_time()); 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 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 }; 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();
@ -820,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()) + "\" }";
@ -977,61 +1304,53 @@ 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()) {
return false; // This case will result with segmentation fault.
} // FIXME check if that happened before introducing libbitcoin
return false;
}
const chain::global_property_object &gpo = database.get_global_properties(); const chain::global_property_object &gpo = database.get_global_properties();
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;
for (auto si : sto.signers) { for (auto si : sto.signers) {
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) {
std::string tx_address = addr.get_address(); payment_type_address = payment_type::P2WSH;
int64_t tx_amount = -1; }
if (tx_confirmations >= gpo.parameters.son_bitcoin_min_tx_confirmations()) { btc_weighted_multisig_address addr(pubkey_weights, network_type, payment_type_address);
if (sto.object_id.is<son_wallet_deposit_id_type>()) {
for (auto &input : tx_json.get_child("result.vout")) { std::string tx_txid = tx.tx_txid;
if (bitcoin_major_version > 21) { uint32_t tx_confirmations = tx.tx_confirmations;
std::string address = input.second.get<std::string>("scriptPubKey.address"); std::string tx_address = addr.get_address();
if (address == tx_address) { int64_t tx_amount = -1;
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()); if (tx_confirmations >= gpo.parameters.son_bitcoin_min_tx_confirmations()) {
tx_amount = std::stoll(tx_amount_s); if (sto.object_id.is<son_wallet_deposit_id_type>()) {
} for (auto &input : tx.tx_in_list) {
} else { for (auto &address : input.tx_address) {
for (auto &address : input.second.get_child("scriptPubKey.addresses")) { if (address == tx_address) {
if (address.second.data() == tx_address) { tx_amount = input.tx_amount;
std::string tx_amount_s = input.second.get<std::string>("value"); break;
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;
}
}
} }
} }
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);
settle_amount = asset(swwo.withdraw_amount, database.get_global_properties().parameters.btc_asset()); settle_amount = asset(swwo.withdraw_amount, database.get_global_properties().parameters.btc_asset());
return true; return true;
}
} }
} }
return false; return false;
@ -1051,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;
@ -1287,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>();
@ -1335,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>();
@ -1355,56 +1698,14 @@ 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);
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) { payment_type payment_type_address = payment_type::P2SH_WSH;
std::stringstream ss(_block); if (use_bitcoind_client) {
boost::property_tree::ptree json; payment_type_address = payment_type::P2WSH;
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>());
}
}
} }
btc_one_or_weighted_multisig_address deposit_addr(user_pub_key, pubkey_weights, network_type, payment_type_address);
return result; return fc::to_hex(deposit_addr.get_redeem_script());
} }
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) {

View file

@ -148,8 +148,8 @@ 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("erc-20-address")) { if (options.count("ethereum-erc-20-address")) {
const std::vector<std::string> symbol_addresses = options["erc-20-address"].as<std::vector<std::string>>(); const std::vector<std::string> symbol_addresses = options["ethereum-erc-20-address"].as<std::vector<std::string>>();
for (const std::string &itr : symbol_addresses) { for (const std::string &itr : symbol_addresses) {
auto itr_pair = graphene::app::dejsonify<std::pair<std::string, std::string>>(itr, 5); 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)); ilog("ERC-20 symbol: ${symbol}, address: ${address}", ("symbol", itr_pair.first)("address", itr_pair.second));
@ -236,7 +236,7 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po)
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 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 auto id = (swo_id.instance.value - std::distance(ast.begin(), ast.find(sidechain))) / ast.size();
const son_wallet_id_type op_id{ id }; 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(op_id); const auto swo = idx.find(op_id);
if (swo != idx.end()) { if (swo != idx.end()) {
@ -259,7 +259,7 @@ 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 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 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>();
@ -459,7 +459,7 @@ void sidechain_net_handler_ethereum::process_primary_wallet() {
const auto ast = active_sidechain_types(database.head_block_time()); 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 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 }; 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)) { if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, op_id)) {
return; return;

View file

@ -211,7 +211,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) {
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 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 auto id = (swo_id.instance.value - std::distance(ast.begin(), ast.find(sidechain))) / ast.size();
const son_wallet_id_type op_id{ id }; 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(op_id); const auto swo = idx.find(op_id);
if (swo != idx.end()) { if (swo != idx.end()) {
@ -234,7 +234,7 @@ 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) {
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 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 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>();
@ -493,7 +493,7 @@ void sidechain_net_handler_hive::process_primary_wallet() {
const auto ast = active_sidechain_types(database.head_block_time()); 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 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 }; 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)) { if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, op_id)) {
return; return;