Compare commits
91 commits
1.5.20-alp
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3d967a2d7 | ||
|
|
178756bd34 | ||
|
|
1f70857d64 | ||
|
|
97e85a849d | ||
|
|
dc4cdd6e4b | ||
|
|
1472066af6 | ||
|
|
a641b8e93f | ||
|
|
aa099f960f | ||
|
|
f0654e5ffd | ||
|
|
9fe351300b | ||
|
|
5fd79c3e78 | ||
|
|
b56818b8ae | ||
|
|
bc0fbeb707 | ||
|
|
accd334a86 | ||
|
|
84a66c6722 | ||
|
|
abd446d80b | ||
|
|
93fb57c080 | ||
|
|
a8845ffde9 | ||
|
|
435c1f8e96 | ||
|
|
1123ff6f93 | ||
|
|
c34415b403 | ||
|
|
daca2813ef | ||
|
|
0b37a48b02 | ||
|
|
e3b10cf1ec | ||
|
|
f5c6a6310b | ||
|
|
75ee6fbed3 | ||
|
|
7516126d01 | ||
|
|
f32a51d03b | ||
|
|
c421453621 | ||
|
|
54ff842db1 | ||
|
|
16ba10ffab | ||
|
|
79974280c0 | ||
|
|
5867a8ae27 | ||
|
|
741534c47f | ||
|
|
f3227fb33d | ||
|
|
bfb961c7be | ||
|
|
5e08b793c5 | ||
|
|
7af3d037b5 | ||
|
|
2788281062 | ||
|
|
4e2850f826 | ||
|
|
f477af6771 | ||
|
|
80d168e5b6 | ||
|
|
e44ed0cfe5 | ||
|
|
142cf5b903 | ||
|
|
ebc1529c48 | ||
|
|
19e0911d64 | ||
|
|
70cd09495e | ||
|
|
e9c7021e16 | ||
|
|
9c9aaa03d3 | ||
|
|
038fa37cc6 | ||
|
|
73b2ba635b | ||
|
|
936f13d2a1 | ||
|
|
fc1cdf2629 | ||
|
|
71f0806b25 | ||
|
|
da3a858aa6 | ||
|
|
1883f97be2 | ||
|
|
559769db2b | ||
|
|
0b64f0cfcc | ||
|
|
96d737fbc2 | ||
|
|
d89e5e1f23 | ||
|
|
6f472d3d3b | ||
|
|
cb60cbe5d1 | ||
|
|
576c54a260 | ||
|
|
2f5e12b28e | ||
|
|
68fbd6f40b | ||
|
|
674b38910d | ||
|
|
d264398a6f | ||
|
|
c1d5691ce2 | ||
|
|
ca69a692cc | ||
|
|
bd041bc13f | ||
|
|
6037e89df0 | ||
|
|
bb7c534b10 | ||
|
|
1be6636bbf | ||
|
|
578edc56d8 | ||
|
|
d387e324fe | ||
|
|
1bf5c82101 | ||
|
|
4883dfe38d | ||
|
|
249276b009 | ||
|
|
ab1e08a756 | ||
|
|
440e4fbb43 | ||
|
|
42b3890a7c | ||
|
|
5dff0830fb | ||
|
|
c3eab0a80b | ||
|
|
12c0c66f4b | ||
|
|
b7113c4ff3 | ||
|
|
058937a3ee | ||
|
|
da73e31038 | ||
|
|
611a63076b | ||
|
|
6a59d9efba | ||
|
|
9fd18b32c4 | ||
|
|
632eb4a231 |
89 changed files with 5090 additions and 2148 deletions
|
|
@ -9,6 +9,8 @@ stages:
|
|||
- build
|
||||
- test
|
||||
- dockerize
|
||||
- python-test
|
||||
- deploy
|
||||
|
||||
build-mainnet:
|
||||
stage: build
|
||||
|
|
@ -48,6 +50,7 @@ dockerize-mainnet:
|
|||
IMAGE: $CI_REGISTRY_IMAGE/mainnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
|
||||
before_script:
|
||||
- docker info
|
||||
- docker builder prune -a -f
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||
script:
|
||||
- docker build --no-cache -t $IMAGE .
|
||||
|
|
@ -56,8 +59,6 @@ dockerize-mainnet:
|
|||
- docker rmi $IMAGE
|
||||
tags:
|
||||
- builder
|
||||
when:
|
||||
manual
|
||||
timeout:
|
||||
3h
|
||||
|
||||
|
|
@ -78,12 +79,27 @@ build-testnet:
|
|||
- build/libraries/
|
||||
- build/programs/
|
||||
- build/tests/
|
||||
when: manual
|
||||
tags:
|
||||
- builder
|
||||
when:
|
||||
manual
|
||||
timeout:
|
||||
3h
|
||||
|
||||
deploy-testnet:
|
||||
stage: deploy
|
||||
dependencies:
|
||||
- build-testnet
|
||||
script:
|
||||
- sudo systemctl stop witness
|
||||
- rm $WORK_DIR/peerplays/witness_node || true
|
||||
- cp build/programs/witness_node/witness_node $WORK_DIR/peerplays/
|
||||
- sudo systemctl restart witness
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "master"
|
||||
when: always
|
||||
environment:
|
||||
name: devnet
|
||||
url: $DEVNET_URL
|
||||
tags:
|
||||
- devnet
|
||||
|
||||
test-testnet:
|
||||
stage: test
|
||||
|
|
@ -119,3 +135,37 @@ dockerize-testnet:
|
|||
manual
|
||||
timeout:
|
||||
3h
|
||||
|
||||
test-e2e:
|
||||
stage: python-test
|
||||
variables:
|
||||
IMAGE: $CI_REGISTRY_IMAGE/mainnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
|
||||
before_script:
|
||||
- docker info
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||
script:
|
||||
- git clone https://gitlab.com/PBSA/tools-libs/peerplays-utils.git
|
||||
- cd peerplays-utils/peerplays-qa-environment
|
||||
- git checkout origin/feature/python-e2e-tests-for-CI
|
||||
- cd e2e-tests/
|
||||
- python3 -m venv venv
|
||||
- source venv/bin/activate
|
||||
- pip3 install -r requirements.txt
|
||||
- docker-compose down --remove-orphans
|
||||
- docker ps -a
|
||||
- docker pull $IMAGE
|
||||
- docker tag $IMAGE peerplays-base:latest
|
||||
- docker image ls -a
|
||||
- docker-compose build
|
||||
- python3 main.py --start all
|
||||
- docker ps -a
|
||||
- python3 -m pytest test_btc_init_state.py test_hive_inital_state.py test_pp_inital_state.py
|
||||
- python3 main.py --stop
|
||||
- deactivate
|
||||
- docker ps -a
|
||||
after_script:
|
||||
- docker rmi $(docker images -a | grep -v 'hive-for-peerplays\|ethereum-for-peerplays\|bitcoin-for-peerplays\|ubuntu-for-peerplays' | awk '{print $3}')
|
||||
tags:
|
||||
- python-tests
|
||||
when:
|
||||
manual
|
||||
|
|
|
|||
126
Dockerfile
126
Dockerfile
|
|
@ -1,5 +1,4 @@
|
|||
FROM ubuntu:20.04
|
||||
MAINTAINER Peerplays Blockchain Standards Association
|
||||
|
||||
#===============================================================================
|
||||
# Ubuntu setup
|
||||
|
|
@ -11,15 +10,14 @@ RUN \
|
|||
apt-utils \
|
||||
autoconf \
|
||||
bash \
|
||||
bison \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
cmake \
|
||||
dnsutils \
|
||||
doxygen \
|
||||
expect \
|
||||
flex \
|
||||
git \
|
||||
graphviz \
|
||||
libboost-all-dev \
|
||||
libbz2-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libncurses-dev \
|
||||
|
|
@ -35,7 +33,6 @@ RUN \
|
|||
ntp \
|
||||
openssh-server \
|
||||
pkg-config \
|
||||
perl \
|
||||
python3 \
|
||||
python3-jinja2 \
|
||||
sudo \
|
||||
|
|
@ -53,16 +50,105 @@ RUN echo 'peerplays:peerplays' | chpasswd
|
|||
# SSH
|
||||
EXPOSE 22
|
||||
|
||||
WORKDIR /home/peerplays/src
|
||||
|
||||
#===============================================================================
|
||||
# Boost setup
|
||||
#===============================================================================
|
||||
|
||||
RUN \
|
||||
wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz && \
|
||||
tar -xzf boost_1_72_0.tar.gz && \
|
||||
cd boost_1_72_0 && \
|
||||
./bootstrap.sh && \
|
||||
./b2 install && \
|
||||
ldconfig && \
|
||||
rm -rf /home/peerplays/src/*
|
||||
|
||||
#===============================================================================
|
||||
# cmake setup
|
||||
#===============================================================================
|
||||
|
||||
RUN \
|
||||
wget https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2-linux-x86_64.sh && \
|
||||
chmod 755 ./cmake-3.24.2-linux-x86_64.sh && \
|
||||
./cmake-3.24.2-linux-x86_64.sh --prefix=/usr --skip-license && \
|
||||
cmake --version && \
|
||||
rm -rf /home/peerplays/src/*
|
||||
|
||||
#===============================================================================
|
||||
# libzmq setup
|
||||
#===============================================================================
|
||||
|
||||
WORKDIR /home/peerplays/
|
||||
RUN \
|
||||
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.tar.gz && \
|
||||
tar -xzvf v4.3.4.tar.gz && \
|
||||
cd libzmq-4.3.4 && \
|
||||
mkdir build && \
|
||||
cd build && \
|
||||
cmake .. && \
|
||||
make -j$(nproc) && \
|
||||
make install && \
|
||||
ldconfig && \
|
||||
rm -rf /home/peerplays/src/*
|
||||
|
||||
#===============================================================================
|
||||
# cppzmq setup
|
||||
#===============================================================================
|
||||
|
||||
RUN \
|
||||
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip && \
|
||||
unzip v4.3.4.zip && \
|
||||
cd libzmq-4.3.4 && \
|
||||
wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.9.0.tar.gz && \
|
||||
tar -xzvf v4.9.0.tar.gz && \
|
||||
cd cppzmq-4.9.0 && \
|
||||
mkdir build && \
|
||||
cd build && \
|
||||
cmake .. && \
|
||||
make -j$(nproc) && \
|
||||
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 --branch version3.8.0 --depth 1 https://gitlab.com/PBSA/peerplays-1.0/libbitcoin-explorer.git && \
|
||||
cd libbitcoin-explorer && \
|
||||
./install.sh && \
|
||||
ldconfig && \
|
||||
rm -rf /home/peerplays/src/*
|
||||
|
||||
#===============================================================================
|
||||
# Doxygen setup
|
||||
#===============================================================================
|
||||
|
||||
RUN \
|
||||
sudo apt install -y bison flex && \
|
||||
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 .. && \
|
||||
|
|
@ -70,18 +156,14 @@ RUN \
|
|||
ldconfig
|
||||
|
||||
#===============================================================================
|
||||
# cppzmq setup
|
||||
# Perl setup
|
||||
#===============================================================================
|
||||
|
||||
WORKDIR /home/peerplays/
|
||||
|
||||
RUN \
|
||||
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 .. && \
|
||||
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) install && \
|
||||
ldconfig
|
||||
|
||||
|
|
@ -89,8 +171,6 @@ RUN \
|
|||
# Peerplays setup
|
||||
#===============================================================================
|
||||
|
||||
WORKDIR /home/peerplays/
|
||||
|
||||
## Clone Peerplays
|
||||
#RUN \
|
||||
# git clone https://gitlab.com/PBSA/peerplays.git && \
|
||||
|
|
@ -106,6 +186,8 @@ ADD . peerplays
|
|||
# Configure Peerplays
|
||||
RUN \
|
||||
cd peerplays && \
|
||||
git submodule update --init --recursive && \
|
||||
git log --oneline -n 5 && \
|
||||
mkdir build && \
|
||||
cd build && \
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
|
|
@ -119,8 +201,8 @@ WORKDIR /home/peerplays/peerplays-network
|
|||
|
||||
# Setup Peerplays runimage
|
||||
RUN \
|
||||
ln -s /home/peerplays/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/cli_wallet/cli_wallet ./ && \
|
||||
ln -s /home/peerplays/src/peerplays/build/programs/witness_node/witness_node ./
|
||||
|
||||
RUN ./witness_node --create-genesis-json genesis.json && \
|
||||
rm genesis.json
|
||||
|
|
|
|||
124
Dockerfile.18.04
124
Dockerfile.18.04
|
|
@ -1,5 +1,4 @@
|
|||
FROM ubuntu:18.04
|
||||
MAINTAINER Peerplays Blockchain Standards Association
|
||||
|
||||
#===============================================================================
|
||||
# Ubuntu setup
|
||||
|
|
@ -11,11 +10,12 @@ RUN \
|
|||
apt-utils \
|
||||
autoconf \
|
||||
bash \
|
||||
bison \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
dnsutils \
|
||||
doxygen \
|
||||
expect \
|
||||
flex \
|
||||
git \
|
||||
graphviz \
|
||||
libbz2-dev \
|
||||
|
|
@ -33,7 +33,6 @@ RUN \
|
|||
ntp \
|
||||
openssh-server \
|
||||
pkg-config \
|
||||
perl \
|
||||
python3 \
|
||||
python3-jinja2 \
|
||||
sudo \
|
||||
|
|
@ -51,41 +50,105 @@ RUN echo 'peerplays:peerplays' | chpasswd
|
|||
# SSH
|
||||
EXPOSE 22
|
||||
|
||||
WORKDIR /home/peerplays/src
|
||||
|
||||
#===============================================================================
|
||||
# Boost setup
|
||||
#===============================================================================
|
||||
|
||||
WORKDIR /home/peerplays/
|
||||
|
||||
RUN \
|
||||
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/ && \
|
||||
wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz && \
|
||||
tar -xzf boost_1_72_0.tar.gz && \
|
||||
cd boost_1_72_0 && \
|
||||
./bootstrap.sh && \
|
||||
./b2 install
|
||||
./b2 install && \
|
||||
ldconfig && \
|
||||
rm -rf /home/peerplays/src/*
|
||||
|
||||
#===============================================================================
|
||||
# cmake setup
|
||||
#===============================================================================
|
||||
|
||||
WORKDIR /home/peerplays/
|
||||
|
||||
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 && \
|
||||
chmod 755 ./cmake-3.23.1-linux-x86_64.sh && \
|
||||
./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license && \
|
||||
cmake --version
|
||||
wget https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2-linux-x86_64.sh && \
|
||||
chmod 755 ./cmake-3.24.2-linux-x86_64.sh && \
|
||||
./cmake-3.24.2-linux-x86_64.sh --prefix=/usr --skip-license && \
|
||||
cmake --version && \
|
||||
rm -rf /home/peerplays/src/*
|
||||
|
||||
#===============================================================================
|
||||
# libzmq setup
|
||||
#===============================================================================
|
||||
|
||||
WORKDIR /home/peerplays/
|
||||
RUN \
|
||||
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.tar.gz && \
|
||||
tar -xzvf v4.3.4.tar.gz && \
|
||||
cd libzmq-4.3.4 && \
|
||||
mkdir build && \
|
||||
cd build && \
|
||||
cmake .. && \
|
||||
make -j$(nproc) && \
|
||||
make install && \
|
||||
ldconfig && \
|
||||
rm -rf /home/peerplays/src/*
|
||||
|
||||
#===============================================================================
|
||||
# cppzmq setup
|
||||
#===============================================================================
|
||||
|
||||
RUN \
|
||||
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip && \
|
||||
unzip v4.3.4.zip && \
|
||||
cd libzmq-4.3.4 && \
|
||||
wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.9.0.tar.gz && \
|
||||
tar -xzvf v4.9.0.tar.gz && \
|
||||
cd cppzmq-4.9.0 && \
|
||||
mkdir build && \
|
||||
cd build && \
|
||||
cmake .. && \
|
||||
make -j$(nproc) && \
|
||||
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 --branch version3.8.0 --depth 1 https://gitlab.com/PBSA/peerplays-1.0/libbitcoin-explorer.git && \
|
||||
cd libbitcoin-explorer && \
|
||||
./install.sh && \
|
||||
ldconfig && \
|
||||
rm -rf /home/peerplays/src/*
|
||||
|
||||
#===============================================================================
|
||||
# Doxygen setup
|
||||
#===============================================================================
|
||||
|
||||
RUN \
|
||||
sudo apt install -y bison flex && \
|
||||
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 .. && \
|
||||
|
|
@ -93,18 +156,14 @@ RUN \
|
|||
ldconfig
|
||||
|
||||
#===============================================================================
|
||||
# cppzmq setup
|
||||
# Perl setup
|
||||
#===============================================================================
|
||||
|
||||
WORKDIR /home/peerplays/
|
||||
|
||||
RUN \
|
||||
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 .. && \
|
||||
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) install && \
|
||||
ldconfig
|
||||
|
||||
|
|
@ -112,8 +171,6 @@ RUN \
|
|||
# Peerplays setup
|
||||
#===============================================================================
|
||||
|
||||
WORKDIR /home/peerplays/
|
||||
|
||||
## Clone Peerplays
|
||||
#RUN \
|
||||
# git clone https://gitlab.com/PBSA/peerplays.git && \
|
||||
|
|
@ -129,6 +186,9 @@ ADD . peerplays
|
|||
# Configure Peerplays
|
||||
RUN \
|
||||
cd peerplays && \
|
||||
git submodule update --init --recursive && \
|
||||
git symbolic-ref --short HEAD && \
|
||||
git log --oneline -n 5 && \
|
||||
mkdir build && \
|
||||
cd build && \
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
|
|
@ -142,8 +202,8 @@ WORKDIR /home/peerplays/peerplays-network
|
|||
|
||||
# Setup Peerplays runimage
|
||||
RUN \
|
||||
ln -s /home/peerplays/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/cli_wallet/cli_wallet ./ && \
|
||||
ln -s /home/peerplays/src/peerplays/build/programs/witness_node/witness_node ./
|
||||
|
||||
RUN ./witness_node --create-genesis-json genesis.json && \
|
||||
rm genesis.json
|
||||
|
|
|
|||
150
README.md
150
README.md
|
|
@ -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.
|
||||
|
||||
## 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 \
|
||||
apt-utils autoconf bash build-essential ca-certificates clang-format cmake \
|
||||
dnsutils doxygen expect git graphviz libboost-all-dev libbz2-dev \
|
||||
libcurl4-openssl-dev libncurses-dev libsnappy-dev \
|
||||
libssl-dev libtool libzip-dev locales lsb-release mc nano net-tools ntp \
|
||||
openssh-server pkg-config perl python3 python3-jinja2 sudo \
|
||||
autoconf bash bison build-essential ca-certificates dnsutils expect flex git \
|
||||
graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev libpcre3-dev \
|
||||
libsnappy-dev libsodium-dev libssl-dev libtool libzip-dev locales lsb-release \
|
||||
mc nano net-tools ntp openssh-server pkg-config python3 python3-jinja2 sudo \
|
||||
systemd-coredump wget
|
||||
```
|
||||
|
||||
Install libzmq from source:
|
||||
Boost libraries setup:
|
||||
```
|
||||
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip
|
||||
unzip v4.3.4.zip
|
||||
cd libzmq-4.3.4
|
||||
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/
|
||||
wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz
|
||||
tar -xzf boost_1_72_0.tar.gz boost_1_72_0
|
||||
cd boost_1_72_0
|
||||
./bootstrap.sh
|
||||
./b2
|
||||
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
|
||||
chmod 755 ./cmake-3.23.1-linux-x86_64.sh
|
||||
sudo ./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license
|
||||
wget https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2-linux-x86_64.sh
|
||||
chmod 755 ./cmake-3.24.2-linux-x86_64.sh
|
||||
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
|
||||
unzip v4.3.4.zip
|
||||
wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.tar.gz
|
||||
tar -xzvf v4.3.4.tar.gz
|
||||
cd libzmq-4.3.4
|
||||
mkdir build
|
||||
cd build
|
||||
|
|
@ -111,11 +52,11 @@ sudo make install
|
|||
sudo ldconfig
|
||||
```
|
||||
|
||||
Install cppzmq from source:
|
||||
cppzmq setup:
|
||||
```
|
||||
wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip
|
||||
unzip v4.8.1.zip
|
||||
cd cppzmq-4.8.1
|
||||
wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.9.0.tar.gz
|
||||
tar -xzvf v4.9.0.tar.gz
|
||||
cd cppzmq-4.9.0
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
|
|
@ -124,6 +65,48 @@ sudo make install
|
|||
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 --branch version3.8.0 --depth 1 https://gitlab.com/PBSA/peerplays-1.0/libbitcoin-explorer.git
|
||||
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
|
||||
```
|
||||
git clone https://gitlab.com/PBSA/peerplays.git
|
||||
|
|
@ -146,7 +129,6 @@ make -j$(nproc)
|
|||
sudo make install # this can install the executable files under /usr/local
|
||||
```
|
||||
|
||||
|
||||
## Docker images
|
||||
|
||||
Install docker, and add current user to docker group.
|
||||
|
|
|
|||
|
|
@ -210,8 +210,8 @@ network_node_api::network_node_api(application &a) :
|
|||
}
|
||||
|
||||
/*
|
||||
* Remove expired transactions from pending_transactions
|
||||
*/
|
||||
* Remove expired transactions from pending_transactions
|
||||
*/
|
||||
for (const auto &transaction : _pending_transactions) {
|
||||
if (transaction.second.expiration < block.timestamp) {
|
||||
auto transaction_it = _pending_transactions.find(transaction.second.id());
|
||||
|
|
|
|||
|
|
@ -391,8 +391,8 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* If delegate has the item, the network has no need to fetch it.
|
||||
*/
|
||||
* If delegate has the item, the network has no need to fetch it.
|
||||
*/
|
||||
virtual bool has_item(const net::item_id &id) override {
|
||||
try {
|
||||
if (id.item_type == graphene::net::block_message_type)
|
||||
|
|
@ -404,13 +404,13 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief allows the application to validate an item prior to broadcasting to peers.
|
||||
*
|
||||
* @param sync_mode true if the message was fetched through the sync process, false during normal operation
|
||||
* @returns true if this message caused the blockchain to switch forks, false if it did not
|
||||
*
|
||||
* @throws exception if error validating the item, otherwise the item is safe to broadcast on.
|
||||
*/
|
||||
* @brief allows the application to validate an item prior to broadcasting to peers.
|
||||
*
|
||||
* @param sync_mode true if the message was fetched through the sync process, false during normal operation
|
||||
* @returns true if this message caused the blockchain to switch forks, false if it did not
|
||||
*
|
||||
* @throws exception if error validating the item, otherwise the item is safe to broadcast on.
|
||||
*/
|
||||
virtual bool handle_block(const graphene::net::block_message &blk_msg, bool sync_mode,
|
||||
std::vector<fc::uint160_t> &contained_transaction_message_ids) override {
|
||||
|
||||
|
|
@ -498,14 +498,14 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Assuming all data elements are ordered in some way, this method should
|
||||
* return up to limit ids that occur *after* the last ID in synopsis that
|
||||
* we recognize.
|
||||
*
|
||||
* On return, remaining_item_count will be set to the number of items
|
||||
* in our blockchain after the last item returned in the result,
|
||||
* or 0 if the result contains the last item in the blockchain
|
||||
*/
|
||||
* Assuming all data elements are ordered in some way, this method should
|
||||
* return up to limit ids that occur *after* the last ID in synopsis that
|
||||
* we recognize.
|
||||
*
|
||||
* On return, remaining_item_count will be set to the number of items
|
||||
* in our blockchain after the last item returned in the result,
|
||||
* or 0 if the result contains the last item in the blockchain
|
||||
*/
|
||||
virtual std::vector<item_hash_t> get_block_ids(const std::vector<item_hash_t> &blockchain_synopsis,
|
||||
uint32_t &remaining_item_count,
|
||||
uint32_t limit) override {
|
||||
|
|
@ -552,8 +552,8 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Given the hash of the requested data, fetch the body.
|
||||
*/
|
||||
* Given the hash of the requested data, fetch the body.
|
||||
*/
|
||||
virtual message get_item(const item_id &id) override {
|
||||
try {
|
||||
// ilog("Request for item ${id}", ("id", id));
|
||||
|
|
@ -576,63 +576,63 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a synopsis of the blockchain used for syncing. This consists of a list of
|
||||
* block hashes at intervals exponentially increasing towards the genesis block.
|
||||
* When syncing to a peer, the peer uses this data to determine if we're on the same
|
||||
* fork as they are, and if not, what blocks they need to send us to get us on their
|
||||
* fork.
|
||||
*
|
||||
* In the over-simplified case, this is a straighforward synopsis of our current
|
||||
* preferred blockchain; when we first connect up to a peer, this is what we will be sending.
|
||||
* It looks like this:
|
||||
* If the blockchain is empty, it will return the empty list.
|
||||
* If the blockchain has one block, it will return a list containing just that block.
|
||||
* If it contains more than one block:
|
||||
* the first element in the list will be the hash of the highest numbered block that
|
||||
* we cannot undo
|
||||
* the second element will be the hash of an item at the half way point in the undoable
|
||||
* segment of the blockchain
|
||||
* the third will be ~3/4 of the way through the undoable segment of the block chain
|
||||
* the fourth will be at ~7/8...
|
||||
* &c.
|
||||
* the last item in the list will be the hash of the most recent block on our preferred chain
|
||||
* so if the blockchain had 26 blocks labeled a - z, the synopsis would be:
|
||||
* a n u x z
|
||||
* the idea being that by sending a small (<30) number of block ids, we can summarize a huge
|
||||
* blockchain. The block ids are more dense near the end of the chain where because we are
|
||||
* more likely to be almost in sync when we first connect, and forks are likely to be short.
|
||||
* If the peer we're syncing with in our example is on a fork that started at block 'v',
|
||||
* then they will reply to our synopsis with a list of all blocks starting from block 'u',
|
||||
* the last block they know that we had in common.
|
||||
*
|
||||
* In the real code, there are several complications.
|
||||
*
|
||||
* First, as an optimization, we don't usually send a synopsis of the entire blockchain, we
|
||||
* send a synopsis of only the segment of the blockchain that we have undo data for. If their
|
||||
* fork doesn't build off of something in our undo history, we would be unable to switch, so there's
|
||||
* no reason to fetch the blocks.
|
||||
*
|
||||
* Second, when a peer replies to our initial synopsis and gives us a list of the blocks they think
|
||||
* we are missing, they only send a chunk of a few thousand blocks at once. After we get those
|
||||
* block ids, we need to request more blocks by sending another synopsis (we can't just say "send me
|
||||
* the next 2000 ids" because they may have switched forks themselves and they don't track what
|
||||
* they've sent us). For faster performance, we want to get a fairly long list of block ids first,
|
||||
* then start downloading the blocks.
|
||||
* The peer doesn't handle these follow-up block id requests any different from the initial request;
|
||||
* it treats the synopsis we send as our blockchain and bases its response entirely off that. So to
|
||||
* get the response we want (the next chunk of block ids following the last one they sent us, or,
|
||||
* failing that, the shortest fork off of the last list of block ids they sent), we need to construct
|
||||
* a synopsis as if our blockchain was made up of:
|
||||
* 1. the blocks in our block chain up to the fork point (if there is a fork) or the head block (if no fork)
|
||||
* 2. the blocks we've already pushed from their fork (if there's a fork)
|
||||
* 3. the block ids they've previously sent us
|
||||
* Segment 3 is handled in the p2p code, it just tells us the number of blocks it has (in
|
||||
* number_of_blocks_after_reference_point) so we can leave space in the synopsis for them.
|
||||
* We're responsible for constructing the synopsis of Segments 1 and 2 from our active blockchain and
|
||||
* fork database. The reference_point parameter is the last block from that peer that has been
|
||||
* successfully pushed to the blockchain, so that tells us whether the peer is on a fork or on
|
||||
* the main chain.
|
||||
*/
|
||||
* Returns a synopsis of the blockchain used for syncing. This consists of a list of
|
||||
* block hashes at intervals exponentially increasing towards the genesis block.
|
||||
* When syncing to a peer, the peer uses this data to determine if we're on the same
|
||||
* fork as they are, and if not, what blocks they need to send us to get us on their
|
||||
* fork.
|
||||
*
|
||||
* In the over-simplified case, this is a straighforward synopsis of our current
|
||||
* preferred blockchain; when we first connect up to a peer, this is what we will be sending.
|
||||
* It looks like this:
|
||||
* If the blockchain is empty, it will return the empty list.
|
||||
* If the blockchain has one block, it will return a list containing just that block.
|
||||
* If it contains more than one block:
|
||||
* the first element in the list will be the hash of the highest numbered block that
|
||||
* we cannot undo
|
||||
* the second element will be the hash of an item at the half way point in the undoable
|
||||
* segment of the blockchain
|
||||
* the third will be ~3/4 of the way through the undoable segment of the block chain
|
||||
* the fourth will be at ~7/8...
|
||||
* &c.
|
||||
* the last item in the list will be the hash of the most recent block on our preferred chain
|
||||
* so if the blockchain had 26 blocks labeled a - z, the synopsis would be:
|
||||
* a n u x z
|
||||
* the idea being that by sending a small (<30) number of block ids, we can summarize a huge
|
||||
* blockchain. The block ids are more dense near the end of the chain where because we are
|
||||
* more likely to be almost in sync when we first connect, and forks are likely to be short.
|
||||
* If the peer we're syncing with in our example is on a fork that started at block 'v',
|
||||
* then they will reply to our synopsis with a list of all blocks starting from block 'u',
|
||||
* the last block they know that we had in common.
|
||||
*
|
||||
* In the real code, there are several complications.
|
||||
*
|
||||
* First, as an optimization, we don't usually send a synopsis of the entire blockchain, we
|
||||
* send a synopsis of only the segment of the blockchain that we have undo data for. If their
|
||||
* fork doesn't build off of something in our undo history, we would be unable to switch, so there's
|
||||
* no reason to fetch the blocks.
|
||||
*
|
||||
* Second, when a peer replies to our initial synopsis and gives us a list of the blocks they think
|
||||
* we are missing, they only send a chunk of a few thousand blocks at once. After we get those
|
||||
* block ids, we need to request more blocks by sending another synopsis (we can't just say "send me
|
||||
* the next 2000 ids" because they may have switched forks themselves and they don't track what
|
||||
* they've sent us). For faster performance, we want to get a fairly long list of block ids first,
|
||||
* then start downloading the blocks.
|
||||
* The peer doesn't handle these follow-up block id requests any different from the initial request;
|
||||
* it treats the synopsis we send as our blockchain and bases its response entirely off that. So to
|
||||
* get the response we want (the next chunk of block ids following the last one they sent us, or,
|
||||
* failing that, the shortest fork off of the last list of block ids they sent), we need to construct
|
||||
* a synopsis as if our blockchain was made up of:
|
||||
* 1. the blocks in our block chain up to the fork point (if there is a fork) or the head block (if no fork)
|
||||
* 2. the blocks we've already pushed from their fork (if there's a fork)
|
||||
* 3. the block ids they've previously sent us
|
||||
* Segment 3 is handled in the p2p code, it just tells us the number of blocks it has (in
|
||||
* number_of_blocks_after_reference_point) so we can leave space in the synopsis for them.
|
||||
* We're responsible for constructing the synopsis of Segments 1 and 2 from our active blockchain and
|
||||
* fork database. The reference_point parameter is the last block from that peer that has been
|
||||
* successfully pushed to the blockchain, so that tells us whether the peer is on a fork or on
|
||||
* the main chain.
|
||||
*/
|
||||
virtual std::vector<item_hash_t> get_blockchain_synopsis(const item_hash_t &reference_point,
|
||||
uint32_t number_of_blocks_after_reference_point) override {
|
||||
try {
|
||||
|
|
@ -733,26 +733,26 @@ public:
|
|||
low_block_num += (true_high_block_num - low_block_num + 2) / 2;
|
||||
} while (low_block_num <= high_block_num);
|
||||
|
||||
//idump((synopsis));
|
||||
// idump((synopsis));
|
||||
return synopsis;
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW()
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this after the call to handle_message succeeds.
|
||||
*
|
||||
* @param item_type the type of the item we're synchronizing, will be the same as item passed to the sync_from() call
|
||||
* @param item_count the number of items known to the node that haven't been sent to handle_item() yet.
|
||||
* After `item_count` more calls to handle_item(), the node will be in sync
|
||||
*/
|
||||
* Call this after the call to handle_message succeeds.
|
||||
*
|
||||
* @param item_type the type of the item we're synchronizing, will be the same as item passed to the sync_from() call
|
||||
* @param item_count the number of items known to the node that haven't been sent to handle_item() yet.
|
||||
* After `item_count` more calls to handle_item(), the node will be in sync
|
||||
*/
|
||||
virtual void sync_status(uint32_t item_type, uint32_t item_count) override {
|
||||
// any status reports to GUI go here
|
||||
}
|
||||
|
||||
/**
|
||||
* Call any time the number of connected peers changes.
|
||||
*/
|
||||
* Call any time the number of connected peers changes.
|
||||
*/
|
||||
virtual void connection_count_changed(uint32_t c) override {
|
||||
// any status reports to GUI go here
|
||||
}
|
||||
|
|
@ -769,9 +769,9 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the time a block was produced (if block_id = 0, returns genesis time).
|
||||
* If we don't know about the block, returns time_point_sec::min()
|
||||
*/
|
||||
* Returns the time a block was produced (if block_id = 0, returns genesis time).
|
||||
* If we don't know about the block, returns time_point_sec::min()
|
||||
*/
|
||||
virtual fc::time_point_sec get_block_time(const item_hash_t &block_id) override {
|
||||
try {
|
||||
auto opt_block = _chain_db->fetch_block_by_id(block_id);
|
||||
|
|
|
|||
|
|
@ -71,6 +71,17 @@ std::string object_id_to_string(object_id_type id) {
|
|||
return object_id;
|
||||
}
|
||||
|
||||
signed_block_with_info::signed_block_with_info(){};
|
||||
|
||||
signed_block_with_info::signed_block_with_info(const signed_block &block) :
|
||||
signed_block(block) {
|
||||
block_id = id();
|
||||
signing_key = signee();
|
||||
transaction_ids.reserve(transactions.size());
|
||||
for (const processed_transaction &tx : transactions)
|
||||
transaction_ids.push_back(tx.id());
|
||||
}
|
||||
|
||||
class database_api_impl : public std::enable_shared_from_this<database_api_impl> {
|
||||
public:
|
||||
database_api_impl(graphene::chain::database &db);
|
||||
|
|
@ -89,6 +100,7 @@ public:
|
|||
optional<block_header> get_block_header(uint32_t block_num) const;
|
||||
map<uint32_t, optional<block_header>> get_block_header_batch(const vector<uint32_t> block_nums) const;
|
||||
optional<signed_block> get_block(uint32_t block_num) const;
|
||||
optional<signed_block_with_info> get_block2(uint32_t block_num) const;
|
||||
vector<optional<signed_block>> get_blocks(uint32_t block_num_from, uint32_t block_num_to) const;
|
||||
processed_transaction get_transaction(uint32_t block_num, uint32_t trx_in_block) const;
|
||||
|
||||
|
|
@ -184,8 +196,8 @@ public:
|
|||
fc::optional<son_object> get_son_by_account(const std::string account_id_or_name) const;
|
||||
map<string, son_id_type> lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const;
|
||||
uint64_t get_son_count() const;
|
||||
flat_map<sidechain_type, vector<son_info>> get_active_sons();
|
||||
vector<son_info> get_active_sons_by_sidechain(sidechain_type sidechain);
|
||||
flat_map<sidechain_type, vector<son_sidechain_info>> get_active_sons();
|
||||
vector<son_sidechain_info> get_active_sons_by_sidechain(sidechain_type sidechain);
|
||||
map<sidechain_type, map<son_id_type, string>> get_son_network_status();
|
||||
map<son_id_type, string> get_son_network_status_by_sidechain(sidechain_type sidechain);
|
||||
|
||||
|
|
@ -269,8 +281,9 @@ public:
|
|||
uint64_t nft_get_total_supply(const nft_metadata_id_type nft_metadata_id) const;
|
||||
nft_object nft_token_by_index(const nft_metadata_id_type nft_metadata_id, const uint64_t token_idx) const;
|
||||
nft_object nft_token_of_owner_by_index(const nft_metadata_id_type nft_metadata_id, const account_id_type owner, const uint64_t token_idx) const;
|
||||
vector<nft_object> nft_get_all_tokens() const;
|
||||
vector<nft_object> nft_get_tokens_by_owner(const account_id_type owner) const;
|
||||
vector<nft_object> nft_get_all_tokens(const nft_id_type lower_id, uint32_t limit) const;
|
||||
vector<nft_object> nft_get_tokens_by_owner(const account_id_type owner, const nft_id_type lower_id, uint32_t limit) const;
|
||||
vector<nft_metadata_object> nft_get_metadata_by_owner(const account_id_type owner, const nft_metadata_id_type lower_id, uint32_t limit) const;
|
||||
|
||||
// Marketplace
|
||||
vector<offer_object> list_offers(const offer_id_type lower_id, uint32_t limit) const;
|
||||
|
|
@ -291,6 +304,7 @@ public:
|
|||
uint32_t api_limit_get_limit_orders_by_account = 101;
|
||||
uint32_t api_limit_get_order_book = 50;
|
||||
uint32_t api_limit_all_offers_count = 100;
|
||||
uint32_t api_limit_nft_tokens = 100;
|
||||
uint32_t api_limit_lookup_accounts = 1000;
|
||||
uint32_t api_limit_lookup_witness_accounts = 1000;
|
||||
uint32_t api_limit_lookup_committee_member_accounts = 1000;
|
||||
|
|
@ -299,7 +313,7 @@ public:
|
|||
uint32_t api_limit_get_trade_history = 100;
|
||||
uint32_t api_limit_get_trade_history_by_sequence = 100;
|
||||
|
||||
//private:
|
||||
// private:
|
||||
const account_object *get_account_from_string(const std::string &name_or_id,
|
||||
bool throw_if_not_found = true) const;
|
||||
const asset_object *get_asset_from_string(const std::string &symbol_or_id,
|
||||
|
|
@ -456,7 +470,7 @@ void database_api::set_subscribe_callback(std::function<void(const variant &)> c
|
|||
}
|
||||
|
||||
void database_api_impl::set_subscribe_callback(std::function<void(const variant &)> cb, bool notify_remove_create) {
|
||||
//edump((clear_filter));
|
||||
// edump((clear_filter));
|
||||
_subscribe_callback = cb;
|
||||
_notify_remove_create = notify_remove_create;
|
||||
_subscribed_accounts.clear();
|
||||
|
|
@ -530,6 +544,17 @@ optional<signed_block> database_api_impl::get_block(uint32_t block_num) const {
|
|||
return _db.fetch_block_by_number(block_num);
|
||||
}
|
||||
|
||||
optional<signed_block_with_info> database_api::get_block2(uint32_t block_num) const {
|
||||
return my->get_block2(block_num);
|
||||
}
|
||||
|
||||
optional<signed_block_with_info> database_api_impl::get_block2(uint32_t block_num) const {
|
||||
auto result = _db.fetch_block_by_number(block_num);
|
||||
if (result)
|
||||
return signed_block_with_info(*result);
|
||||
return {};
|
||||
}
|
||||
|
||||
vector<optional<signed_block>> database_api::get_blocks(uint32_t block_num_from, uint32_t block_num_to) const {
|
||||
return my->get_blocks(block_num_from, block_num_to);
|
||||
}
|
||||
|
|
@ -1852,22 +1877,22 @@ uint64_t database_api_impl::get_son_count() const {
|
|||
return _db.get_index_type<son_index>().indices().size();
|
||||
}
|
||||
|
||||
flat_map<sidechain_type, vector<son_info>> database_api::get_active_sons() {
|
||||
flat_map<sidechain_type, vector<son_sidechain_info>> database_api::get_active_sons() {
|
||||
return my->get_active_sons();
|
||||
}
|
||||
|
||||
flat_map<sidechain_type, vector<son_info>> database_api_impl::get_active_sons() {
|
||||
flat_map<sidechain_type, vector<son_sidechain_info>> database_api_impl::get_active_sons() {
|
||||
return get_global_properties().active_sons;
|
||||
}
|
||||
|
||||
vector<son_info> database_api::get_active_sons_by_sidechain(sidechain_type sidechain) {
|
||||
vector<son_sidechain_info> database_api::get_active_sons_by_sidechain(sidechain_type sidechain) {
|
||||
return my->get_active_sons_by_sidechain(sidechain);
|
||||
}
|
||||
|
||||
vector<son_info> database_api_impl::get_active_sons_by_sidechain(sidechain_type sidechain) {
|
||||
vector<son_sidechain_info> database_api_impl::get_active_sons_by_sidechain(sidechain_type sidechain) {
|
||||
const global_property_object &gpo = get_global_properties();
|
||||
|
||||
vector<son_info> result;
|
||||
vector<son_sidechain_info> result;
|
||||
|
||||
if (gpo.active_sons.find(sidechain) != gpo.active_sons.end()) {
|
||||
result = gpo.active_sons.at(sidechain);
|
||||
|
|
@ -1883,7 +1908,7 @@ map<sidechain_type, map<son_id_type, string>> database_api::get_son_network_stat
|
|||
map<sidechain_type, map<son_id_type, string>> database_api_impl::get_son_network_status() {
|
||||
map<sidechain_type, map<son_id_type, string>> result;
|
||||
|
||||
for (auto active_sidechain_type : active_sidechain_types) {
|
||||
for (auto active_sidechain_type : active_sidechain_types(_db.head_block_time())) {
|
||||
result[active_sidechain_type] = get_son_network_status_by_sidechain(active_sidechain_type);
|
||||
}
|
||||
|
||||
|
|
@ -2172,7 +2197,7 @@ vector<variant> database_api_impl::lookup_vote_ids(const vector<vote_id_type> &v
|
|||
case vote_id_type::committee: {
|
||||
auto itr = committee_idx.find(id);
|
||||
if (itr != committee_idx.end())
|
||||
result.emplace_back(variant(*itr, 1));
|
||||
result.emplace_back(variant(*itr, 2)); // Depth of committee_member_object is 1, add 1 to be safe
|
||||
else
|
||||
result.emplace_back(variant());
|
||||
break;
|
||||
|
|
@ -2180,7 +2205,7 @@ vector<variant> database_api_impl::lookup_vote_ids(const vector<vote_id_type> &v
|
|||
case vote_id_type::witness: {
|
||||
auto itr = witness_idx.find(id);
|
||||
if (itr != witness_idx.end())
|
||||
result.emplace_back(variant(*itr, 1));
|
||||
result.emplace_back(variant(*itr, 2)); // Depth of witness_object is 1, add 1 here to be safe
|
||||
else
|
||||
result.emplace_back(variant());
|
||||
break;
|
||||
|
|
@ -2188,11 +2213,15 @@ vector<variant> database_api_impl::lookup_vote_ids(const vector<vote_id_type> &v
|
|||
case vote_id_type::worker: {
|
||||
auto itr = for_worker_idx.find(id);
|
||||
if (itr != for_worker_idx.end()) {
|
||||
result.emplace_back(variant(*itr, 1));
|
||||
result.emplace_back(variant(*itr, 4)); // Depth of worker_object is 3, add 1 here to be safe.
|
||||
// If we want to extract the balance object inside,
|
||||
// need to increase this value
|
||||
} else {
|
||||
auto itr = against_worker_idx.find(id);
|
||||
if (itr != against_worker_idx.end()) {
|
||||
result.emplace_back(variant(*itr, 1));
|
||||
result.emplace_back(variant(*itr, 4)); // Depth of worker_object is 3, add 1 here to be safe.
|
||||
// If we want to extract the balance object inside,
|
||||
// need to increase this value
|
||||
} else {
|
||||
result.emplace_back(variant());
|
||||
}
|
||||
|
|
@ -2315,7 +2344,9 @@ votes_info database_api_impl::get_votes(const string &account_name_or_id) const
|
|||
votes_for_sons[sidechain].reserve(sidechain_ids.size());
|
||||
for (const auto &son : sidechain_ids) {
|
||||
const auto &son_obj = son.as<son_object>(6);
|
||||
votes_for_sons[sidechain].emplace_back(votes_info_object{son_obj.get_sidechain_vote_id(sidechain), son_obj.id});
|
||||
if (son_obj.get_sidechain_vote_id(sidechain).valid()) {
|
||||
votes_for_sons[sidechain].emplace_back(votes_info_object{*son_obj.get_sidechain_vote_id(sidechain), son_obj.id});
|
||||
}
|
||||
}
|
||||
}
|
||||
result.votes_for_sons = std::move(votes_for_sons);
|
||||
|
|
@ -2876,7 +2907,7 @@ graphene::app::gpos_info database_api::get_gpos_info(const account_id_type accou
|
|||
}
|
||||
|
||||
graphene::app::gpos_info database_api_impl::get_gpos_info(const account_id_type account) const {
|
||||
FC_ASSERT(_db.head_block_time() > HARDFORK_GPOS_TIME); //Can be deleted after GPOS hardfork time
|
||||
FC_ASSERT(_db.head_block_time() > HARDFORK_GPOS_TIME); // Can be deleted after GPOS hardfork time
|
||||
gpos_info result;
|
||||
|
||||
result.vesting_factor = _db.calculate_vesting_factor(account(_db));
|
||||
|
|
@ -3102,30 +3133,61 @@ nft_object database_api_impl::nft_token_of_owner_by_index(const nft_metadata_id_
|
|||
return {};
|
||||
}
|
||||
|
||||
vector<nft_object> database_api::nft_get_all_tokens() const {
|
||||
return my->nft_get_all_tokens();
|
||||
vector<nft_object> database_api::nft_get_all_tokens(const nft_id_type lower_id, uint32_t limit) const {
|
||||
return my->nft_get_all_tokens(lower_id, limit);
|
||||
}
|
||||
|
||||
vector<nft_object> database_api_impl::nft_get_all_tokens() const {
|
||||
vector<nft_object> database_api_impl::nft_get_all_tokens(const nft_id_type lower_id, uint32_t limit) const {
|
||||
FC_ASSERT(limit <= api_limit_nft_tokens,
|
||||
"Number of queried nft tokens can not be greater than ${configured_limit}",
|
||||
("configured_limit", api_limit_nft_tokens));
|
||||
|
||||
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_id>();
|
||||
vector<nft_object> result;
|
||||
for (auto itr = idx_nft.begin(); itr != idx_nft.end(); ++itr) {
|
||||
result.push_back(*itr);
|
||||
}
|
||||
result.reserve(limit);
|
||||
auto itr = idx_nft.lower_bound(lower_id);
|
||||
while (limit-- && itr != idx_nft.end())
|
||||
result.emplace_back(*itr++);
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<nft_object> database_api::nft_get_tokens_by_owner(const account_id_type owner) const {
|
||||
return my->nft_get_tokens_by_owner(owner);
|
||||
vector<nft_object> database_api::nft_get_tokens_by_owner(const account_id_type owner, const nft_id_type lower_id, uint32_t limit) const {
|
||||
return my->nft_get_tokens_by_owner(owner, lower_id, limit);
|
||||
}
|
||||
|
||||
vector<nft_object> database_api_impl::nft_get_tokens_by_owner(const account_id_type owner) const {
|
||||
vector<nft_object> database_api_impl::nft_get_tokens_by_owner(const account_id_type owner, const nft_id_type lower_id, uint32_t limit) const {
|
||||
FC_ASSERT(limit <= api_limit_nft_tokens,
|
||||
"Number of queried nft tokens can not be greater than ${configured_limit}",
|
||||
("configured_limit", api_limit_nft_tokens));
|
||||
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_owner>();
|
||||
auto idx_nft_range = idx_nft.equal_range(owner);
|
||||
vector<nft_object> result;
|
||||
for (auto itr = idx_nft_range.first; itr != idx_nft_range.second; ++itr) {
|
||||
result.push_back(*itr);
|
||||
}
|
||||
result.reserve(limit);
|
||||
auto itr = std::find_if(idx_nft_range.first, idx_nft_range.second, [&lower_id](const nft_object &obj) {
|
||||
return !(obj.id.instance() < lower_id.instance);
|
||||
});
|
||||
while (limit-- && itr != idx_nft_range.second)
|
||||
result.emplace_back(*itr++);
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<nft_metadata_object> database_api::nft_get_metadata_by_owner(const account_id_type owner, const nft_metadata_id_type lower_id, uint32_t limit) const {
|
||||
return my->nft_get_metadata_by_owner(owner, lower_id, limit);
|
||||
}
|
||||
|
||||
vector<nft_metadata_object> database_api_impl::nft_get_metadata_by_owner(const account_id_type owner, const nft_metadata_id_type lower_id, uint32_t limit) const {
|
||||
FC_ASSERT(limit <= api_limit_nft_tokens,
|
||||
"Number of queried nft metadata objects can not be greater than ${configured_limit}",
|
||||
("configured_limit", api_limit_nft_tokens));
|
||||
const auto &idx_nft = _db.get_index_type<nft_metadata_index>().indices().get<by_owner>();
|
||||
auto idx_nft_range = idx_nft.equal_range(owner);
|
||||
vector<nft_metadata_object> result;
|
||||
result.reserve(limit);
|
||||
auto itr = std::find_if(idx_nft_range.first, idx_nft_range.second, [&lower_id](const nft_metadata_object &obj) {
|
||||
return !(obj.id.instance() < lower_id.instance);
|
||||
});
|
||||
while (limit-- && itr != idx_nft_range.second)
|
||||
result.emplace_back(*itr++);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -3492,9 +3554,9 @@ void database_api_impl::handle_object_changed(bool force_notify, bool full_objec
|
|||
/// pushing the future back / popping the prior future if it is complete.
|
||||
/// if a connection hangs then this could get backed up and result in
|
||||
/// a failure to exit cleanly.
|
||||
//fc::async([capture_this,this,updates,market_broadcast_queue](){
|
||||
//if( _subscribe_callback )
|
||||
// _subscribe_callback( updates );
|
||||
// fc::async([capture_this,this,updates,market_broadcast_queue](){
|
||||
// if( _subscribe_callback )
|
||||
// _subscribe_callback( updates );
|
||||
|
||||
for (auto id : ids) {
|
||||
if (id.is<call_order_object>()) {
|
||||
|
|
|
|||
|
|
@ -85,10 +85,10 @@ struct asset_holders {
|
|||
};
|
||||
|
||||
/**
|
||||
* @brief The history_api class implements the RPC API for account history
|
||||
*
|
||||
* This API contains methods to access account histories
|
||||
*/
|
||||
* @brief The history_api class implements the RPC API for account history
|
||||
*
|
||||
* This API contains methods to access account histories
|
||||
*/
|
||||
class history_api {
|
||||
public:
|
||||
history_api(application &app) :
|
||||
|
|
@ -97,27 +97,27 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Get operations relevant to the specificed account
|
||||
* @param account_id_or_name The account ID or name whose history should be queried
|
||||
* @param stop ID of the earliest operation to retrieve
|
||||
* @param limit Maximum number of operations to retrieve (must not exceed 100)
|
||||
* @param start ID of the most recent operation to retrieve
|
||||
* @return A list of operations performed by account, ordered from most recent to oldest.
|
||||
*/
|
||||
* @brief Get operations relevant to the specificed account
|
||||
* @param account_id_or_name The account ID or name whose history should be queried
|
||||
* @param stop ID of the earliest operation to retrieve
|
||||
* @param limit Maximum number of operations to retrieve (must not exceed 100)
|
||||
* @param start ID of the most recent operation to retrieve
|
||||
* @return A list of operations performed by account, ordered from most recent to oldest.
|
||||
*/
|
||||
vector<operation_history_object> get_account_history(const std::string account_id_or_name,
|
||||
operation_history_id_type stop = operation_history_id_type(),
|
||||
unsigned limit = 100,
|
||||
operation_history_id_type start = operation_history_id_type()) const;
|
||||
|
||||
/**
|
||||
* @brief Get only asked operations relevant to the specified account
|
||||
* @param account_id_or_name The account ID or name whose history should be queried
|
||||
* @param operation_id The ID of the operation we want to get operations in the account( 0 = transfer , 1 = limit order create, ...)
|
||||
* @param stop ID of the earliest operation to retrieve
|
||||
* @param limit Maximum number of operations to retrieve (must not exceed 100)
|
||||
* @param start ID of the most recent operation to retrieve
|
||||
* @return A list of operations performed by account, ordered from most recent to oldest.
|
||||
*/
|
||||
* @brief Get only asked operations relevant to the specified account
|
||||
* @param account_id_or_name The account ID or name whose history should be queried
|
||||
* @param operation_id The ID of the operation we want to get operations in the account( 0 = transfer , 1 = limit order create, ...)
|
||||
* @param stop ID of the earliest operation to retrieve
|
||||
* @param limit Maximum number of operations to retrieve (must not exceed 100)
|
||||
* @param start ID of the most recent operation to retrieve
|
||||
* @return A list of operations performed by account, ordered from most recent to oldest.
|
||||
*/
|
||||
vector<operation_history_object> get_account_history_operations(const std::string account_id_or_name,
|
||||
int operation_id,
|
||||
operation_history_id_type start = operation_history_id_type(),
|
||||
|
|
@ -125,17 +125,17 @@ public:
|
|||
unsigned limit = 100) const;
|
||||
|
||||
/**
|
||||
* @breif Get operations relevant to the specified account referenced
|
||||
* by an event numbering specific to the account. The current number of operations
|
||||
* for the account can be found in the account statistics (or use 0 for start).
|
||||
* @param account_id_or_name The account ID or name whose history should be queried
|
||||
* @param stop Sequence number of earliest operation. 0 is default and will
|
||||
* query 'limit' number of operations.
|
||||
* @param limit Maximum number of operations to retrieve (must not exceed 100)
|
||||
* @param start Sequence number of the most recent operation to retrieve.
|
||||
* 0 is default, which will start querying from the most recent operation.
|
||||
* @return A list of operations performed by account, ordered from most recent to oldest.
|
||||
*/
|
||||
* @breif Get operations relevant to the specified account referenced
|
||||
* by an event numbering specific to the account. The current number of operations
|
||||
* for the account can be found in the account statistics (or use 0 for start).
|
||||
* @param account_id_or_name The account ID or name whose history should be queried
|
||||
* @param stop Sequence number of earliest operation. 0 is default and will
|
||||
* query 'limit' number of operations.
|
||||
* @param limit Maximum number of operations to retrieve (must not exceed 100)
|
||||
* @param start Sequence number of the most recent operation to retrieve.
|
||||
* 0 is default, which will start querying from the most recent operation.
|
||||
* @return A list of operations performed by account, ordered from most recent to oldest.
|
||||
*/
|
||||
vector<operation_history_object> get_relative_account_history(const std::string account_id_or_name,
|
||||
uint32_t stop = 0,
|
||||
unsigned limit = 100,
|
||||
|
|
@ -156,8 +156,8 @@ private:
|
|||
};
|
||||
|
||||
/**
|
||||
* @brief Block api
|
||||
*/
|
||||
* @brief Block api
|
||||
*/
|
||||
class block_api {
|
||||
public:
|
||||
block_api(graphene::chain::database &db);
|
||||
|
|
@ -170,8 +170,8 @@ private:
|
|||
};
|
||||
|
||||
/**
|
||||
* @brief The network_broadcast_api class allows broadcasting of transactions.
|
||||
*/
|
||||
* @brief The network_broadcast_api class allows broadcasting of transactions.
|
||||
*/
|
||||
class network_broadcast_api : public std::enable_shared_from_this<network_broadcast_api> {
|
||||
public:
|
||||
network_broadcast_api(application &a);
|
||||
|
|
@ -186,36 +186,36 @@ public:
|
|||
typedef std::function<void(variant /*transaction_confirmation*/)> confirmation_callback;
|
||||
|
||||
/**
|
||||
* @brief Broadcast a transaction to the network
|
||||
* @param trx The transaction to broadcast
|
||||
*
|
||||
* The transaction will be checked for validity in the local database prior to broadcasting. If it fails to
|
||||
* apply locally, an error will be thrown and the transaction will not be broadcast.
|
||||
*/
|
||||
* @brief Broadcast a transaction to the network
|
||||
* @param trx The transaction to broadcast
|
||||
*
|
||||
* The transaction will be checked for validity in the local database prior to broadcasting. If it fails to
|
||||
* apply locally, an error will be thrown and the transaction will not be broadcast.
|
||||
*/
|
||||
void broadcast_transaction(const signed_transaction &trx);
|
||||
|
||||
/** this version of broadcast transaction registers a callback method that will be called when the transaction is
|
||||
* included into a block. The callback method includes the transaction id, block number, and transaction number in the
|
||||
* block.
|
||||
*/
|
||||
* included into a block. The callback method includes the transaction id, block number, and transaction number in the
|
||||
* block.
|
||||
*/
|
||||
void broadcast_transaction_with_callback(confirmation_callback cb, const signed_transaction &trx);
|
||||
|
||||
/** this version of broadcast transaction registers a callback method that will be called when the transaction is
|
||||
* included into a block. The callback method includes the transaction id, block number, and transaction number in the
|
||||
* block.
|
||||
*/
|
||||
* included into a block. The callback method includes the transaction id, block number, and transaction number in the
|
||||
* block.
|
||||
*/
|
||||
fc::variant broadcast_transaction_synchronous(const signed_transaction &trx);
|
||||
|
||||
void broadcast_block(const signed_block &block);
|
||||
|
||||
/**
|
||||
* @brief Not reflected, thus not accessible to API clients.
|
||||
*
|
||||
* This function is registered to receive the applied_block
|
||||
* signal from the chain database when a block is received.
|
||||
* It then dispatches callbacks to clients who have requested
|
||||
* to be notified when a particular txid is included in a block.
|
||||
*/
|
||||
* @brief Not reflected, thus not accessible to API clients.
|
||||
*
|
||||
* This function is registered to receive the applied_block
|
||||
* signal from the chain database when a block is received.
|
||||
* It then dispatches callbacks to clients who have requested
|
||||
* to be notified when a particular txid is included in a block.
|
||||
*/
|
||||
void on_applied_block(const signed_block &b);
|
||||
|
||||
private:
|
||||
|
|
@ -225,60 +225,60 @@ private:
|
|||
};
|
||||
|
||||
/**
|
||||
* @brief The network_node_api class allows maintenance of p2p connections.
|
||||
*/
|
||||
* @brief The network_node_api class allows maintenance of p2p connections.
|
||||
*/
|
||||
class network_node_api {
|
||||
public:
|
||||
network_node_api(application &a);
|
||||
|
||||
/**
|
||||
* @brief Return general network information, such as p2p port
|
||||
*/
|
||||
* @brief Return general network information, such as p2p port
|
||||
*/
|
||||
fc::variant_object get_info() const;
|
||||
|
||||
/**
|
||||
* @brief add_node Connect to a new peer
|
||||
* @param ep The IP/Port of the peer to connect to
|
||||
*/
|
||||
* @brief add_node Connect to a new peer
|
||||
* @param ep The IP/Port of the peer to connect to
|
||||
*/
|
||||
void add_node(const fc::ip::endpoint &ep);
|
||||
|
||||
/**
|
||||
* @brief Get status of all current connections to peers
|
||||
*/
|
||||
* @brief Get status of all current connections to peers
|
||||
*/
|
||||
std::vector<net::peer_status> get_connected_peers() const;
|
||||
|
||||
/**
|
||||
* @brief Get advanced node parameters, such as desired and max
|
||||
* number of connections
|
||||
*/
|
||||
* @brief Get advanced node parameters, such as desired and max
|
||||
* number of connections
|
||||
*/
|
||||
fc::variant_object get_advanced_node_parameters() const;
|
||||
|
||||
/**
|
||||
* @brief Set advanced node parameters, such as desired and max
|
||||
* number of connections
|
||||
* @param params a JSON object containing the name/value pairs for the parameters to set
|
||||
*/
|
||||
* @brief Set advanced node parameters, such as desired and max
|
||||
* number of connections
|
||||
* @param params a JSON object containing the name/value pairs for the parameters to set
|
||||
*/
|
||||
void set_advanced_node_parameters(const fc::variant_object ¶ms);
|
||||
|
||||
/**
|
||||
* @brief Return list of potential peers
|
||||
*/
|
||||
* @brief Return list of potential peers
|
||||
*/
|
||||
std::vector<net::potential_peer_record> get_potential_peers() const;
|
||||
|
||||
/**
|
||||
* @brief Return list of pending transactions.
|
||||
*/
|
||||
* @brief Return list of pending transactions.
|
||||
*/
|
||||
map<transaction_id_type, signed_transaction> list_pending_transactions() const;
|
||||
|
||||
/**
|
||||
* @brief Subscribes caller for notifications about pending transactions.
|
||||
* @param callback a functional object which will be called when new transaction is created.
|
||||
*/
|
||||
* @brief Subscribes caller for notifications about pending transactions.
|
||||
* @param callback a functional object which will be called when new transaction is created.
|
||||
*/
|
||||
void subscribe_to_pending_transactions(std::function<void(const variant &)> callback);
|
||||
|
||||
/**
|
||||
* @brief Unsubscribes caller from notifications about pending transactions.
|
||||
*/
|
||||
* @brief Unsubscribes caller from notifications about pending transactions.
|
||||
*/
|
||||
void unsubscribe_from_pending_transactions();
|
||||
|
||||
private:
|
||||
|
|
@ -290,33 +290,33 @@ private:
|
|||
};
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*/
|
||||
* @brief
|
||||
*/
|
||||
class asset_api {
|
||||
public:
|
||||
asset_api(graphene::app::application &app);
|
||||
~asset_api();
|
||||
|
||||
/**
|
||||
* @brief Get asset holders for a specific asset
|
||||
* @param asset The specific asset id or symbol
|
||||
* @param start The start index
|
||||
* @param limit Maximum limit must not exceed 100
|
||||
* @return A list of asset holders for the specified asset
|
||||
*/
|
||||
* @brief Get asset holders for a specific asset
|
||||
* @param asset The specific asset id or symbol
|
||||
* @param start The start index
|
||||
* @param limit Maximum limit must not exceed 100
|
||||
* @return A list of asset holders for the specified asset
|
||||
*/
|
||||
vector<account_asset_balance> get_asset_holders(std::string asset, uint32_t start, uint32_t limit) const;
|
||||
|
||||
/**
|
||||
* @brief Get asset holders count for a specific asset
|
||||
* @param asset The specific asset id or symbol
|
||||
* @return Holders count for the specified asset
|
||||
*/
|
||||
* @brief Get asset holders count for a specific asset
|
||||
* @param asset The specific asset id or symbol
|
||||
* @return Holders count for the specified asset
|
||||
*/
|
||||
int get_asset_holders_count(std::string asset) const;
|
||||
|
||||
/**
|
||||
* @brief Get all asset holders
|
||||
* @return A list of all asset holders
|
||||
*/
|
||||
* @brief Get all asset holders
|
||||
* @return A list of all asset holders
|
||||
*/
|
||||
vector<asset_holders> get_all_asset_holders() const;
|
||||
|
||||
uint32_t api_limit_get_asset_holders = 100;
|
||||
|
|
@ -337,24 +337,24 @@ extern template class fc::api<graphene::debug_witness::debug_api>;
|
|||
|
||||
namespace graphene { namespace app {
|
||||
/**
|
||||
* @brief The login_api class implements the bottom layer of the RPC API
|
||||
*
|
||||
* All other APIs must be requested from this API.
|
||||
*/
|
||||
* @brief The login_api class implements the bottom layer of the RPC API
|
||||
*
|
||||
* All other APIs must be requested from this API.
|
||||
*/
|
||||
class login_api {
|
||||
public:
|
||||
login_api(application &a);
|
||||
~login_api();
|
||||
|
||||
/**
|
||||
* @brief Authenticate to the RPC server
|
||||
* @param user Username to login with
|
||||
* @param password Password to login with
|
||||
* @return True if logged in successfully; false otherwise
|
||||
*
|
||||
* @note This must be called prior to requesting other APIs. Other APIs may not be accessible until the client
|
||||
* has sucessfully authenticated.
|
||||
*/
|
||||
* @brief Authenticate to the RPC server
|
||||
* @param user Username to login with
|
||||
* @param password Password to login with
|
||||
* @return True if logged in successfully; false otherwise
|
||||
*
|
||||
* @note This must be called prior to requesting other APIs. Other APIs may not be accessible until the client
|
||||
* has sucessfully authenticated.
|
||||
*/
|
||||
bool login(const string &user, const string &password);
|
||||
/// @brief Retrieve the network block API
|
||||
fc::api<block_api> block() const;
|
||||
|
|
|
|||
|
|
@ -82,6 +82,15 @@ using namespace std;
|
|||
|
||||
class database_api_impl;
|
||||
|
||||
struct signed_block_with_info : public signed_block {
|
||||
signed_block_with_info();
|
||||
signed_block_with_info(const signed_block &block);
|
||||
signed_block_with_info(const signed_block_with_info &block) = default;
|
||||
block_id_type block_id;
|
||||
public_key_type signing_key;
|
||||
vector<transaction_id_type> transaction_ids;
|
||||
};
|
||||
|
||||
struct order {
|
||||
double price;
|
||||
double quote;
|
||||
|
|
@ -189,10 +198,10 @@ public:
|
|||
optional<block_header> get_block_header(uint32_t block_num) const;
|
||||
|
||||
/**
|
||||
* @brief Retrieve multiple block header by block numbers
|
||||
* @param block_num vector containing heights of the block whose header should be returned
|
||||
* @return array of headers of the referenced blocks, or null if no matching block was found
|
||||
*/
|
||||
* @brief Retrieve multiple block header by block numbers
|
||||
* @param block_num vector containing heights of the block whose header should be returned
|
||||
* @return array of headers of the referenced blocks, or null if no matching block was found
|
||||
*/
|
||||
map<uint32_t, optional<block_header>> get_block_header_batch(const vector<uint32_t> block_nums) const;
|
||||
|
||||
/**
|
||||
|
|
@ -202,6 +211,13 @@ public:
|
|||
*/
|
||||
optional<signed_block> get_block(uint32_t block_num) const;
|
||||
|
||||
/**
|
||||
* @brief Retrieve a full, signed block, with some extra info
|
||||
* @param block_num Height of the block to be returned
|
||||
* @return the referenced block, or null if no matching block was found
|
||||
*/
|
||||
optional<signed_block_with_info> get_block2(uint32_t block_num) const;
|
||||
|
||||
/**
|
||||
* @brief Retrieve a list of signed blocks
|
||||
* @param block_num_from start
|
||||
|
|
@ -263,12 +279,12 @@ public:
|
|||
vector<vector<account_id_type>> get_key_references(vector<public_key_type> key) const;
|
||||
|
||||
/**
|
||||
* Determine whether a textual representation of a public key
|
||||
* (in Base-58 format) is *currently* linked
|
||||
* to any *registered* (i.e. non-stealth) account on the blockchain
|
||||
* @param public_key Public key
|
||||
* @return Whether a public key is known
|
||||
*/
|
||||
* Determine whether a textual representation of a public key
|
||||
* (in Base-58 format) is *currently* linked
|
||||
* to any *registered* (i.e. non-stealth) account on the blockchain
|
||||
* @param public_key Public key
|
||||
* @return Whether a public key is known
|
||||
*/
|
||||
bool is_public_key_registered(string public_key) const;
|
||||
|
||||
//////////////
|
||||
|
|
@ -679,14 +695,14 @@ public:
|
|||
* @brief Get list of active sons
|
||||
* @return List of active SONs
|
||||
*/
|
||||
flat_map<sidechain_type, vector<son_info>> get_active_sons();
|
||||
flat_map<sidechain_type, vector<son_sidechain_info>> get_active_sons();
|
||||
|
||||
/**
|
||||
* @brief Get list of active sons
|
||||
* @param sidechain Sidechain type [bitcoin|ethereum|hive]
|
||||
* @return List of active SONs
|
||||
*/
|
||||
vector<son_info> get_active_sons_by_sidechain(sidechain_type sidechain);
|
||||
vector<son_sidechain_info> get_active_sons_by_sidechain(sidechain_type sidechain);
|
||||
|
||||
/**
|
||||
* @brief Get SON network status
|
||||
|
|
@ -1027,14 +1043,25 @@ public:
|
|||
* @brief Returns list of all available NTF's
|
||||
* @return List of all available NFT's
|
||||
*/
|
||||
vector<nft_object> nft_get_all_tokens() const;
|
||||
vector<nft_object> nft_get_all_tokens(const nft_id_type lower_id, uint32_t limit) const;
|
||||
|
||||
/**
|
||||
* @brief Returns NFT's owned by owner
|
||||
* @param owner NFT owner
|
||||
* @param lower_id ID of the first NFT to return
|
||||
* @param limit Maximum number of results to return
|
||||
* @return List of NFT owned by owner
|
||||
*/
|
||||
vector<nft_object> nft_get_tokens_by_owner(const account_id_type owner) const;
|
||||
vector<nft_object> nft_get_tokens_by_owner(const account_id_type owner, const nft_id_type lower_id, uint32_t limit) const;
|
||||
|
||||
/**
|
||||
* @brief Returns NFT metadata owned by owner
|
||||
* @param owner NFT owner
|
||||
* @param lower_id ID of the first NFT metadata to return
|
||||
* @param limit Maximum number of results to return
|
||||
* @return List of NFT owned by owner
|
||||
*/
|
||||
vector<nft_metadata_object> nft_get_metadata_by_owner(const account_id_type owner, const nft_metadata_id_type lower_id, uint32_t limit) const;
|
||||
|
||||
//////////////////
|
||||
// MARKET PLACE //
|
||||
|
|
@ -1064,6 +1091,8 @@ extern template class fc::api<graphene::app::database_api>;
|
|||
|
||||
// clang-format off
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::app::signed_block_with_info, (graphene::chain::signed_block), (block_id)(signing_key)(transaction_ids));
|
||||
|
||||
FC_REFLECT(graphene::app::order, (price)(quote)(base));
|
||||
FC_REFLECT(graphene::app::order_book, (base)(quote)(bids)(asks));
|
||||
FC_REFLECT(graphene::app::market_ticker, (base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume));
|
||||
|
|
@ -1086,6 +1115,7 @@ FC_API(graphene::app::database_api,
|
|||
(get_block_header)
|
||||
(get_block_header_batch)
|
||||
(get_block)
|
||||
(get_block2)
|
||||
(get_blocks)
|
||||
(get_transaction)
|
||||
(get_recent_transaction_by_id)
|
||||
|
|
@ -1249,6 +1279,7 @@ FC_API(graphene::app::database_api,
|
|||
(nft_token_of_owner_by_index)
|
||||
(nft_get_all_tokens)
|
||||
(nft_get_tokens_by_owner)
|
||||
(nft_get_metadata_by_owner)
|
||||
|
||||
// Marketplace
|
||||
(list_offers)
|
||||
|
|
|
|||
|
|
@ -53,7 +53,54 @@ void verify_authority_accounts( const database& db, const authority& a )
|
|||
}
|
||||
}
|
||||
|
||||
void verify_account_votes( const database& db, const account_options& options )
|
||||
// Overwrites the num_son values from the origin to the destination for those sidechains which are found in the origin.
|
||||
// Keeps the values of num_son for the sidechains which are found in the destination, but not in the origin.
|
||||
// Returns false if an error is detected.
|
||||
bool merge_num_sons( flat_map<sidechain_type, uint16_t>& destination,
|
||||
const flat_map<sidechain_type, uint16_t>& origin,
|
||||
fc::optional<time_point_sec> head_block_time = {})
|
||||
{
|
||||
const auto active_sidechains = head_block_time.valid() ? active_sidechain_types(*head_block_time) : all_sidechain_types;
|
||||
bool success = true;
|
||||
|
||||
for (const auto &ns : origin)
|
||||
{
|
||||
destination[ns.first] = ns.second;
|
||||
if (active_sidechains.find(ns.first) == active_sidechains.end())
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
flat_map<sidechain_type, uint16_t> count_SON_votes_per_sidechain( const flat_set<vote_id_type>& votes )
|
||||
{
|
||||
flat_map<sidechain_type, uint16_t> SON_votes_per_sidechain = account_options::ext::empty_num_son();
|
||||
|
||||
for (const auto &vote : votes)
|
||||
{
|
||||
switch (vote.type())
|
||||
{
|
||||
case vote_id_type::son_bitcoin:
|
||||
SON_votes_per_sidechain[sidechain_type::bitcoin]++;
|
||||
break;
|
||||
case vote_id_type::son_hive:
|
||||
SON_votes_per_sidechain[sidechain_type::hive]++;
|
||||
break;
|
||||
case vote_id_type::son_ethereum:
|
||||
SON_votes_per_sidechain[sidechain_type::ethereum]++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SON_votes_per_sidechain;
|
||||
}
|
||||
|
||||
void verify_account_votes( const database& db, const account_options& options, fc::optional<account_object> account = {} )
|
||||
{
|
||||
// ensure account's votes satisfy requirements
|
||||
// NB only the part of vote checking that requires chain state is here,
|
||||
|
|
@ -69,12 +116,40 @@ void verify_account_votes( const database& db, const account_options& options )
|
|||
FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count,
|
||||
"Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) );
|
||||
FC_ASSERT( chain_params.extensions.value.maximum_son_count.valid() , "Invalid maximum son count" );
|
||||
FC_ASSERT( options.extensions.value.num_son.valid() , "Invalid son number" );
|
||||
for(const auto& num_sons : *options.extensions.value.num_son)
|
||||
|
||||
flat_map<sidechain_type, uint16_t> merged_num_sons = account_options::ext::empty_num_son();
|
||||
|
||||
// Merge with existing account if exists
|
||||
if ( account.valid() && account->options.extensions.value.num_son.valid())
|
||||
{
|
||||
merge_num_sons( merged_num_sons, *account->options.extensions.value.num_son, db.head_block_time() );
|
||||
}
|
||||
|
||||
// Apply update operation on top
|
||||
if ( options.extensions.value.num_son.valid() )
|
||||
{
|
||||
merge_num_sons( merged_num_sons, *options.extensions.value.num_son, db.head_block_time() );
|
||||
}
|
||||
|
||||
for(const auto& num_sons : merged_num_sons)
|
||||
{
|
||||
FC_ASSERT( num_sons.second <= *chain_params.extensions.value.maximum_son_count,
|
||||
"Voted for more sons than currently allowed (${c})", ("c", *chain_params.extensions.value.maximum_son_count) );
|
||||
}
|
||||
|
||||
// Count the votes for SONs and confirm that the account did not vote for less SONs than num_son
|
||||
flat_map<sidechain_type, uint16_t> SON_votes_per_sidechain = count_SON_votes_per_sidechain(options.votes);
|
||||
|
||||
for (const auto& number_of_votes : SON_votes_per_sidechain)
|
||||
{
|
||||
// Number of votes of account_options are also checked in account_options::do_evaluate,
|
||||
// but there we are checking the value before merging num_sons, so the values should be checked again
|
||||
const auto sidechain = number_of_votes.first;
|
||||
FC_ASSERT( number_of_votes.second >= merged_num_sons[sidechain],
|
||||
"Voted for less sons than specified in num_son (votes ${v} < num_son ${ns}) for sidechain ${s}",
|
||||
("v", number_of_votes.second) ("ns", merged_num_sons[sidechain]) ("s", sidechain) );
|
||||
}
|
||||
|
||||
FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." );
|
||||
|
||||
uint32_t max_vote_id = gpo.next_available_vote_id;
|
||||
|
|
@ -188,6 +263,13 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio
|
|||
obj.owner = o.owner;
|
||||
obj.active = o.active;
|
||||
obj.options = o.options;
|
||||
|
||||
obj.options.extensions.value.num_son = account_options::ext::empty_num_son();
|
||||
if ( o.options.extensions.value.num_son.valid() )
|
||||
{
|
||||
merge_num_sons( *obj.options.extensions.value.num_son, *o.options.extensions.value.num_son );
|
||||
}
|
||||
|
||||
obj.statistics = d.create<account_statistics_object>([&obj](account_statistics_object& s){
|
||||
s.owner = obj.id;
|
||||
s.name = obj.name;
|
||||
|
|
@ -287,7 +369,7 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio
|
|||
acnt = &o.account(d);
|
||||
|
||||
if( o.new_options.valid() )
|
||||
verify_account_votes( d, *o.new_options );
|
||||
verify_account_votes( d, *o.new_options, *acnt );
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
|
@ -326,7 +408,31 @@ void_result account_update_evaluator::do_apply( const account_update_operation&
|
|||
a.active = *o.active;
|
||||
a.top_n_control_flags = 0;
|
||||
}
|
||||
if( o.new_options ) a.options = *o.new_options;
|
||||
|
||||
// New num_son structure initialized to 0
|
||||
flat_map<sidechain_type, uint16_t> new_num_son = account_options::ext::empty_num_son();
|
||||
|
||||
// If num_son of existing object is valid, we should merge the existing data
|
||||
if ( a.options.extensions.value.num_son.valid() )
|
||||
{
|
||||
merge_num_sons( new_num_son, *a.options.extensions.value.num_son );
|
||||
}
|
||||
|
||||
// If num_son of the operation are valid, they should merge the existing data
|
||||
if ( o.new_options )
|
||||
{
|
||||
const auto new_options = *o.new_options;
|
||||
|
||||
if ( new_options.extensions.value.num_son.valid() )
|
||||
{
|
||||
merge_num_sons( new_num_son, *new_options.extensions.value.num_son );
|
||||
}
|
||||
|
||||
a.options = *o.new_options;
|
||||
}
|
||||
|
||||
a.options.extensions.value.num_son = new_num_son;
|
||||
|
||||
if( o.extensions.value.owner_special_authority.valid() )
|
||||
{
|
||||
a.owner_special_authority = *(o.extensions.value.owner_special_authority);
|
||||
|
|
|
|||
|
|
@ -33,45 +33,163 @@ namespace graphene { namespace chain {
|
|||
|
||||
void_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_operation& o )
|
||||
{ try {
|
||||
return void_result();
|
||||
const auto& d = db();
|
||||
if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME )
|
||||
{
|
||||
const auto& atype = o.amount.asset_id(d);
|
||||
FC_ASSERT( atype.allow_confidential() );
|
||||
FC_ASSERT( !atype.is_transfer_restricted() );
|
||||
FC_ASSERT( !(atype.options.flags & white_list) );
|
||||
|
||||
for( const auto& out : o.outputs )
|
||||
{
|
||||
for( const auto& a : out.owner.account_auths )
|
||||
a.first(d); // verify all accounts exist and are valid
|
||||
}
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
|
||||
void_result transfer_to_blind_evaluator::do_apply( const transfer_to_blind_operation& o )
|
||||
{ try {
|
||||
return void_result();
|
||||
if( db().head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
|
||||
db().adjust_balance(o.from, -o.amount);
|
||||
|
||||
const auto &add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset
|
||||
db().modify(add, [&](asset_dynamic_data_object &obj) {
|
||||
obj.confidential_supply += o.amount.amount;
|
||||
FC_ASSERT(obj.confidential_supply >= 0);
|
||||
});
|
||||
for (const auto &out : o.outputs) {
|
||||
db().create<blinded_balance_object>([&](blinded_balance_object &obj) {
|
||||
obj.asset_id = o.amount.asset_id;
|
||||
obj.owner = out.owner;
|
||||
obj.commitment = out.commitment;
|
||||
});
|
||||
}
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void transfer_to_blind_evaluator::pay_fee()
|
||||
{
|
||||
const auto& d = db();
|
||||
if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
|
||||
if (d.head_block_time() >= HARDFORK_563_TIME)
|
||||
pay_fba_fee(fba_accumulator_id_transfer_to_blind);
|
||||
else
|
||||
generic_evaluator::pay_fee();
|
||||
}
|
||||
}
|
||||
|
||||
void_result transfer_from_blind_evaluator::do_evaluate( const transfer_from_blind_operation& o )
|
||||
{ try {
|
||||
return void_result();
|
||||
const auto& d = db();
|
||||
if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
|
||||
o.fee.asset_id(d); // verify fee is a legit asset
|
||||
const auto &bbi = d.get_index_type<blinded_balance_index>();
|
||||
const auto &cidx = bbi.indices().get<by_commitment>();
|
||||
for (const auto &in : o.inputs) {
|
||||
auto itr = cidx.find(in.commitment);
|
||||
FC_ASSERT(itr != cidx.end());
|
||||
FC_ASSERT(itr->asset_id == o.fee.asset_id);
|
||||
FC_ASSERT(itr->owner == in.owner);
|
||||
}
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void_result transfer_from_blind_evaluator::do_apply( const transfer_from_blind_operation& o )
|
||||
{ try {
|
||||
return void_result();
|
||||
if( db().head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
|
||||
db().adjust_balance(o.fee_payer(), o.fee);
|
||||
db().adjust_balance(o.to, o.amount);
|
||||
const auto &bbi = db().get_index_type<blinded_balance_index>();
|
||||
const auto &cidx = bbi.indices().get<by_commitment>();
|
||||
for (const auto &in : o.inputs) {
|
||||
auto itr = cidx.find(in.commitment);
|
||||
FC_ASSERT(itr != cidx.end());
|
||||
db().remove(*itr);
|
||||
}
|
||||
const auto &add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset
|
||||
db().modify(add, [&](asset_dynamic_data_object &obj) {
|
||||
obj.confidential_supply -= o.amount.amount + o.fee.amount;
|
||||
FC_ASSERT(obj.confidential_supply >= 0);
|
||||
});
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void transfer_from_blind_evaluator::pay_fee()
|
||||
{
|
||||
const auto& d = db();
|
||||
if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
|
||||
if (d.head_block_time() >= HARDFORK_563_TIME)
|
||||
pay_fba_fee(fba_accumulator_id_transfer_from_blind);
|
||||
else
|
||||
generic_evaluator::pay_fee();
|
||||
}
|
||||
}
|
||||
|
||||
void_result blind_transfer_evaluator::do_evaluate( const blind_transfer_operation& o )
|
||||
{ try {
|
||||
return void_result();
|
||||
const auto& d = db();
|
||||
if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
|
||||
o.fee.asset_id(d); // verify fee is a legit asset
|
||||
const auto &bbi = d.get_index_type<blinded_balance_index>();
|
||||
const auto &cidx = bbi.indices().get<by_commitment>();
|
||||
for (const auto &out : o.outputs) {
|
||||
for (const auto &a : out.owner.account_auths)
|
||||
a.first(d); // verify all accounts exist and are valid
|
||||
}
|
||||
for (const auto &in : o.inputs) {
|
||||
auto itr = cidx.find(in.commitment);
|
||||
GRAPHENE_ASSERT(itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment", in.commitment));
|
||||
FC_ASSERT(itr->asset_id == o.fee.asset_id);
|
||||
FC_ASSERT(itr->owner == in.owner);
|
||||
}
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void_result blind_transfer_evaluator::do_apply( const blind_transfer_operation& o )
|
||||
{ try {
|
||||
return void_result();
|
||||
if( db().head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
|
||||
db().adjust_balance(o.fee_payer(), o.fee); // deposit the fee to the temp account
|
||||
const auto &bbi = db().get_index_type<blinded_balance_index>();
|
||||
const auto &cidx = bbi.indices().get<by_commitment>();
|
||||
for (const auto &in : o.inputs) {
|
||||
auto itr = cidx.find(in.commitment);
|
||||
GRAPHENE_ASSERT(itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment", in.commitment));
|
||||
db().remove(*itr);
|
||||
}
|
||||
for (const auto &out : o.outputs) {
|
||||
db().create<blinded_balance_object>([&](blinded_balance_object &obj) {
|
||||
obj.asset_id = o.fee.asset_id;
|
||||
obj.owner = out.owner;
|
||||
obj.commitment = out.commitment;
|
||||
});
|
||||
}
|
||||
const auto &add = o.fee.asset_id(db()).dynamic_asset_data_id(db());
|
||||
db().modify(add, [&](asset_dynamic_data_object &obj) {
|
||||
obj.confidential_supply -= o.fee.amount;
|
||||
FC_ASSERT(obj.confidential_supply >= 0);
|
||||
});
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void blind_transfer_evaluator::pay_fee()
|
||||
{
|
||||
const auto& d = db();
|
||||
if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
|
||||
if (d.head_block_time() >= HARDFORK_563_TIME)
|
||||
pay_fba_fee(fba_accumulator_id_blind_transfer);
|
||||
else
|
||||
generic_evaluator::pay_fee();
|
||||
}
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
|
|||
|
|
@ -739,13 +739,11 @@ void database::_apply_block( const signed_block& next_block )
|
|||
|
||||
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) {
|
||||
update_witness_schedule(next_block);
|
||||
bool need_to_update_son_schedule = false;
|
||||
for(const auto& active_sons : global_props.active_sons){
|
||||
if(!active_sons.second.empty())
|
||||
need_to_update_son_schedule = true;
|
||||
}
|
||||
if(need_to_update_son_schedule) {
|
||||
update_son_schedule(next_block);
|
||||
|
||||
for(const auto& active_sons : global_props.active_sons) {
|
||||
if(!active_sons.second.empty()) {
|
||||
update_son_schedule(active_sons.first, next_block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -783,15 +781,11 @@ void database::_apply_block( const signed_block& next_block )
|
|||
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) {
|
||||
update_witness_schedule();
|
||||
|
||||
bool need_update_son_schedule = false;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types) {
|
||||
for(const auto& active_sidechain_type : active_sidechain_types(dynamic_global_props.time)) {
|
||||
if(global_props.active_sons.at(active_sidechain_type).size() > 0) {
|
||||
need_update_son_schedule = true;
|
||||
update_son_schedule(active_sidechain_type);
|
||||
}
|
||||
}
|
||||
if(need_update_son_schedule) {
|
||||
update_son_schedule();
|
||||
}
|
||||
}
|
||||
|
||||
if( !_node_property_object.debug_updates.empty() )
|
||||
|
|
|
|||
|
|
@ -236,8 +236,10 @@ std::set<son_id_type> database::get_sons_to_be_deregistered()
|
|||
// TODO : We need to add a function that returns if we can deregister SON
|
||||
// i.e. with introduction of PW code, we have to make a decision if the SON
|
||||
// is needed for release of funds from the PW
|
||||
if (head_block_time() - stats.last_active_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) {
|
||||
need_to_be_deregistered = false;
|
||||
if(stats.last_active_timestamp.contains(sidechain)) {
|
||||
if (head_block_time() - stats.last_active_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) {
|
||||
need_to_be_deregistered = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -303,17 +305,16 @@ bool database::is_son_dereg_valid( son_id_type son_id )
|
|||
}
|
||||
|
||||
bool status_son_dereg_valid = true;
|
||||
for(const auto& status : son->statuses)
|
||||
{
|
||||
const auto& sidechain = status.first;
|
||||
if(status.second != son_status::in_maintenance)
|
||||
for (const auto &active_sidechain_type : active_sidechain_types(head_block_time())) {
|
||||
if(son->statuses.at(active_sidechain_type) != son_status::in_maintenance)
|
||||
status_son_dereg_valid = false;
|
||||
|
||||
if(status_son_dereg_valid)
|
||||
{
|
||||
if(head_block_time() - son->statistics(*this).last_active_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time()))
|
||||
{
|
||||
status_son_dereg_valid = false;
|
||||
if(son->statistics(*this).last_active_timestamp.contains(active_sidechain_type)) {
|
||||
if (head_block_time() - son->statistics(*this).last_active_timestamp.at(active_sidechain_type) < fc::seconds(get_global_properties().parameters.son_deregister_time())) {
|
||||
status_son_dereg_valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -339,7 +340,7 @@ bool database::is_son_active( sidechain_type type, son_id_type son_id )
|
|||
active_son_ids.reserve(gpo_as.size());
|
||||
std::transform(gpo_as.cbegin(), gpo_as.cend(),
|
||||
std::inserter(active_son_ids, active_son_ids.end()),
|
||||
[](const son_info& swi) {
|
||||
[](const son_sidechain_info& swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
|
|
@ -385,23 +386,14 @@ vector<uint64_t> database::get_random_numbers(uint64_t minimum, uint64_t maximum
|
|||
|
||||
bool database::is_asset_creation_allowed(const string &symbol)
|
||||
{
|
||||
time_point_sec now = head_block_time();
|
||||
std::unordered_set<std::string> post_son_hf_symbols = {"ETH", "USDT", "BNB", "ADA", "DOGE", "XRP", "USDC", "DOT", "UNI", "BUSD", "BCH", "LTC", "SOL", "LINK", "MATIC", "THETA",
|
||||
"WBTC", "XLM", "ICP", "DAI", "VET", "ETC", "TRX", "FIL", "XMR", "EGR", "EOS", "SHIB", "AAVE", "CRO", "ALGO", "AMP", "BTCB",
|
||||
"BSV", "KLAY", "CAKE", "FTT", "LEO", "XTZ", "TFUEL", "MIOTA", "LUNA", "NEO", "ATOM", "MKR", "FEI", "WBNB", "UST", "AVAX",
|
||||
"STEEM", "HIVE", "HBD", "SBD", "BTS"};
|
||||
if (symbol == "BTC")
|
||||
{
|
||||
if (now < HARDFORK_SON_TIME)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (post_son_hf_symbols.find(symbol) != post_son_hf_symbols.end())
|
||||
{
|
||||
if (now >= HARDFORK_SON_TIME)
|
||||
if (head_block_time() < HARDFORK_SON_TIME)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -365,7 +365,7 @@ void database::initialize_hardforks()
|
|||
_hardfork_times.emplace_back(HARDFORK_SON_FOR_HIVE_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_SON_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_SON2_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_SON3_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_SON_FOR_ETHEREUM_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_SWEEPS_TIME);
|
||||
|
||||
std::sort(_hardfork_times.begin(), _hardfork_times.end());
|
||||
|
|
@ -1168,6 +1168,11 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
});
|
||||
assert( ssohive.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::hive)) );
|
||||
|
||||
// Enable fees
|
||||
modify(get_global_properties(), [&genesis_state](global_property_object& p) {
|
||||
p.parameters.current_fees = genesis_state.initial_parameters.current_fees;
|
||||
});
|
||||
|
||||
// Create FBA counters
|
||||
create<fba_accumulator_object>([&]( fba_accumulator_object& acc )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ vector<std::reference_wrapper<const son_object>> database::sort_votable_objects<
|
|||
std::vector<std::reference_wrapper<const son_object>> refs;
|
||||
for( auto& son : all_sons )
|
||||
{
|
||||
if(son.has_valid_config(head_block_time()) && son.statuses.at(sidechain) != son_status::deregistered)
|
||||
if(son.has_valid_config(head_block_time(), sidechain) && son.statuses.at(sidechain) != son_status::deregistered)
|
||||
{
|
||||
refs.push_back(std::cref(son));
|
||||
}
|
||||
|
|
@ -97,8 +97,10 @@ vector<std::reference_wrapper<const son_object>> database::sort_votable_objects<
|
|||
sidechain == sidechain_type::hive,
|
||||
"Unexpected sidechain type");
|
||||
|
||||
const share_type oa_vote = _vote_tally_buffer[a.get_sidechain_vote_id(sidechain)];
|
||||
const share_type ob_vote = _vote_tally_buffer[b.get_sidechain_vote_id(sidechain)];
|
||||
FC_ASSERT(a.get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", a));
|
||||
FC_ASSERT(b.get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", b));
|
||||
const share_type oa_vote = _vote_tally_buffer.size() > *a.get_sidechain_vote_id(sidechain) ? _vote_tally_buffer[*a.get_sidechain_vote_id(sidechain)] : 0;
|
||||
const share_type ob_vote = _vote_tally_buffer.size() > *b.get_sidechain_vote_id(sidechain) ? _vote_tally_buffer[*b.get_sidechain_vote_id(sidechain)] : 0;
|
||||
|
||||
if( oa_vote != ob_vote )
|
||||
return oa_vote > ob_vote;
|
||||
|
|
@ -168,6 +170,7 @@ struct worker_pay_visitor
|
|||
worker.pay_worker(pay, db);
|
||||
}
|
||||
};
|
||||
|
||||
void database::update_worker_votes()
|
||||
{
|
||||
auto& idx = get_index_type<worker_index>();
|
||||
|
|
@ -183,14 +186,132 @@ void database::update_worker_votes()
|
|||
}
|
||||
}
|
||||
|
||||
void database::pay_sons()
|
||||
void database::hotfix_2024()
|
||||
{
|
||||
if (head_block_time() >= HARDFORK_HOTFIX_2024_TIME)
|
||||
{
|
||||
if (get_chain_id().str() == "6b6b5f0ce7a36d323768e534f3edb41c6d6332a541a95725b98e28d140850134")
|
||||
{
|
||||
const auto& vb_idx = get_index_type<vesting_balance_index>().indices().get<by_id>();
|
||||
auto vbo = vb_idx.find(vesting_balance_id_type(388));
|
||||
if (vbo != vb_idx.end())
|
||||
{
|
||||
if (vbo->owner == account_id_type(14786))
|
||||
{
|
||||
modify(*vbo, [&]( vesting_balance_object& _vbo)
|
||||
{
|
||||
_vbo.owner = account_id_type(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void database::pay_sons_before_hf_ethereum()
|
||||
{
|
||||
const auto now = head_block_time();
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
// Current requirement is that we have to pay every 24 hours, so the following check
|
||||
if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time())))
|
||||
{
|
||||
const sidechain_type st = sidechain_type::bitcoin;
|
||||
const auto sons = sort_votable_objects<son_index>(st, get_global_properties().parameters.maximum_son_count());
|
||||
|
||||
// After SON2 HF
|
||||
uint64_t total_votes = 0;
|
||||
for( const son_object& son : sons )
|
||||
{
|
||||
FC_ASSERT(son.get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", son));
|
||||
total_votes += _vote_tally_buffer[*son.get_sidechain_vote_id(st)];
|
||||
}
|
||||
const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
|
||||
auto get_weight = [&bits_to_drop]( uint64_t son_votes ) {
|
||||
const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) );
|
||||
return weight;
|
||||
};
|
||||
|
||||
// Before SON2 HF
|
||||
auto get_weight_before_son2_hf = []( uint64_t son_votes ) {
|
||||
const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(son_votes)) - 15, 0);
|
||||
const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) );
|
||||
return weight;
|
||||
};
|
||||
|
||||
uint64_t weighted_total_txs_signed = 0;
|
||||
const share_type son_budget = dpo.son_budget;
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf, &st](const object& o) {
|
||||
const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
|
||||
const auto& idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
const auto son_obj = idx.find( s.owner );
|
||||
uint16_t son_weight = 0;
|
||||
FC_ASSERT(son_obj->get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", *son_obj));
|
||||
if( now >= HARDFORK_SON2_TIME ) {
|
||||
son_weight += get_weight(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]);
|
||||
}
|
||||
else {
|
||||
son_weight += get_weight_before_son2_hf(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]);
|
||||
}
|
||||
const uint64_t txs_signed_bitcoin = s.txs_signed.contains(sidechain_type::bitcoin) ? s.txs_signed.at(sidechain_type::bitcoin) : 0;
|
||||
const uint64_t txs_signed_hive = s.txs_signed.contains(sidechain_type::hive) ? s.txs_signed.at(sidechain_type::hive) : 0;
|
||||
weighted_total_txs_signed += ((txs_signed_bitcoin + txs_signed_hive) * son_weight);
|
||||
});
|
||||
|
||||
// Now pay off each SON proportional to the number of transactions signed.
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now, &st](const object& o) {
|
||||
const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
|
||||
const uint64_t txs_signed_bitcoin = s.txs_signed.contains(sidechain_type::bitcoin) ? s.txs_signed.at(sidechain_type::bitcoin) : 0;
|
||||
const uint64_t txs_signed_hive = s.txs_signed.contains(sidechain_type::hive) ? s.txs_signed.at(sidechain_type::hive) : 0;
|
||||
|
||||
if(txs_signed_bitcoin > 0 || txs_signed_hive > 0) {
|
||||
const auto& idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find( s.owner );
|
||||
uint16_t son_weight = 0;
|
||||
FC_ASSERT(son_obj->get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", *son_obj));
|
||||
if( now >= HARDFORK_SON2_TIME ) {
|
||||
son_weight += get_weight(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]);
|
||||
}
|
||||
else {
|
||||
son_weight += get_weight_before_son2_hf(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]);
|
||||
}
|
||||
const share_type pay = ((txs_signed_bitcoin + txs_signed_hive) * son_weight * son_budget.value)/weighted_total_txs_signed;
|
||||
modify( *son_obj, [&]( son_object& _son_obj)
|
||||
{
|
||||
_son_obj.pay_son_fee(pay, *this);
|
||||
});
|
||||
//Remove the amount paid out to SON from global SON Budget
|
||||
modify( dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.son_budget -= pay;
|
||||
} );
|
||||
//Reset the tx counter in each son statistics object
|
||||
modify( s, [&]( son_statistics_object& _s)
|
||||
{
|
||||
if(_s.txs_signed.contains(sidechain_type::bitcoin))
|
||||
_s.txs_signed.at(sidechain_type::bitcoin) = 0;
|
||||
if(_s.txs_signed.contains(sidechain_type::hive))
|
||||
_s.txs_signed.at(sidechain_type::hive) = 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//Note the last son pay out time
|
||||
modify( dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.last_son_payout_time = now;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database::pay_sons_after_hf_ethereum()
|
||||
{
|
||||
const time_point_sec now = head_block_time();
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
// Current requirement is that we have to pay every 24 hours, so the following check
|
||||
if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time())))
|
||||
{
|
||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
||||
flat_map<sidechain_type, int8_t> bits_to_drop;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types(now))
|
||||
{
|
||||
assert( _son_count_histogram_buffer.at(active_sidechain_type).size() > 0 );
|
||||
const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer.at(active_sidechain_type)[0]) / 2;
|
||||
|
|
@ -207,87 +328,76 @@ void database::pay_sons()
|
|||
}
|
||||
}
|
||||
|
||||
const auto sons = sort_votable_objects<son_index>(active_sidechain_type,
|
||||
(std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count))
|
||||
);
|
||||
const auto sons = sort_votable_objects<son_index>(active_sidechain_type, (std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count)));
|
||||
|
||||
// After SON2 HF
|
||||
uint64_t total_votes = 0;
|
||||
for( const son_object& son : sons )
|
||||
{
|
||||
total_votes += _vote_tally_buffer[son.sidechain_vote_ids.at(active_sidechain_type)];
|
||||
FC_ASSERT(son.get_sidechain_vote_id(active_sidechain_type).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", active_sidechain_type)("son", son));
|
||||
total_votes += _vote_tally_buffer[*son.get_sidechain_vote_id(active_sidechain_type)];
|
||||
}
|
||||
const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
|
||||
auto get_weight = [&bits_to_drop]( uint64_t son_votes ) {
|
||||
const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) );
|
||||
return weight;
|
||||
};
|
||||
// Before SON2 HF
|
||||
auto get_weight_before_son2_hf = []( uint64_t son_votes ) {
|
||||
const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(son_votes)) - 15, 0);
|
||||
const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) );
|
||||
return weight;
|
||||
};
|
||||
uint64_t weighted_total_txs_signed = 0;
|
||||
const share_type son_budget = dpo.son_budget;
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf, &active_sidechain_type](const object& o) {
|
||||
const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
|
||||
const auto& idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
const auto son_obj = idx.find( s.owner );
|
||||
uint16_t son_weight = 0;
|
||||
if( now >= HARDFORK_SON2_TIME ) {
|
||||
son_weight += get_weight(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]);
|
||||
}
|
||||
else {
|
||||
son_weight += get_weight_before_son2_hf(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]);
|
||||
}
|
||||
bits_to_drop[active_sidechain_type] = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
|
||||
}
|
||||
|
||||
auto get_weight = [&bits_to_drop]( sidechain_type sidechain, uint64_t son_votes ) {
|
||||
const uint16_t weight = std::max((son_votes >> bits_to_drop.at(sidechain)), uint64_t(1) );
|
||||
return weight;
|
||||
};
|
||||
|
||||
// Calculate weighted_total_txs_signed
|
||||
uint64_t weighted_total_txs_signed = 0;
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now](const object& o) {
|
||||
for(const auto& active_sidechain_type : active_sidechain_types(now)) {
|
||||
const son_statistics_object &s = static_cast<const son_statistics_object &>(o);
|
||||
const auto &idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
const auto son_obj = idx.find(s.owner);
|
||||
FC_ASSERT(son_obj->get_sidechain_vote_id(active_sidechain_type).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", active_sidechain_type)("son", *son_obj));
|
||||
const uint16_t son_weight = get_weight(active_sidechain_type, _vote_tally_buffer[*son_obj->get_sidechain_vote_id(active_sidechain_type)]);
|
||||
const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0;
|
||||
weighted_total_txs_signed += (txs_signed * son_weight);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Now pay off each SON proportional to the number of transactions signed
|
||||
const share_type son_budget = dpo.son_budget;
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &now, &get_weight, &weighted_total_txs_signed, &dpo, &son_budget](const object& o) {
|
||||
for(const auto& active_sidechain_type : active_sidechain_types(now)) {
|
||||
const son_statistics_object &s = static_cast<const son_statistics_object &>(o);
|
||||
|
||||
// Now pay off each SON proportional to the number of transactions signed.
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now, &active_sidechain_type](const object& o) {
|
||||
const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
|
||||
const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0;
|
||||
|
||||
if(txs_signed > 0){
|
||||
const auto& idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find( s.owner );
|
||||
if (txs_signed > 0) {
|
||||
const auto &idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find(s.owner);
|
||||
uint16_t son_weight = 0;
|
||||
if( now >= HARDFORK_SON2_TIME ) {
|
||||
son_weight += get_weight(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]);
|
||||
}
|
||||
else {
|
||||
son_weight += get_weight_before_son2_hf(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]);
|
||||
}
|
||||
const share_type pay = (txs_signed * son_weight * son_budget.value)/weighted_total_txs_signed;
|
||||
modify( *son_obj, [&]( son_object& _son_obj)
|
||||
{
|
||||
_son_obj.pay_son_fee(pay, *this);
|
||||
FC_ASSERT(son_obj->get_sidechain_vote_id(active_sidechain_type).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", active_sidechain_type)("son", *son_obj));
|
||||
son_weight += get_weight(active_sidechain_type, _vote_tally_buffer[*son_obj->get_sidechain_vote_id(active_sidechain_type)]);
|
||||
const share_type pay = (txs_signed * son_weight * son_budget.value) / weighted_total_txs_signed;
|
||||
modify(*son_obj, [&](son_object &_son_obj) {
|
||||
_son_obj.pay_son_fee(pay, *this);
|
||||
});
|
||||
//Remove the amount paid out to SON from global SON Budget
|
||||
modify( dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.son_budget -= pay;
|
||||
} );
|
||||
//Reset the tx counter in each son statistics object
|
||||
modify( s, [&]( son_statistics_object& _s)
|
||||
{
|
||||
if(_s.txs_signed.contains(active_sidechain_type))
|
||||
// Remove the amount paid out to SON from global SON Budget
|
||||
modify(dpo, [&](dynamic_global_property_object &_dpo) {
|
||||
_dpo.son_budget -= pay;
|
||||
});
|
||||
// Reset the tx counter in each son statistics object
|
||||
modify(s, [&](son_statistics_object &_s) {
|
||||
if (_s.txs_signed.contains(active_sidechain_type))
|
||||
_s.txs_signed.at(active_sidechain_type) = 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
//Note the last son pay out time
|
||||
modify( dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.last_son_payout_time = now;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Note the last son pay out time
|
||||
modify( dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.last_son_payout_time = now;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database::update_son_metrics(const flat_map<sidechain_type, vector<son_info> >& curr_active_sons)
|
||||
void database::update_son_metrics(const flat_map<sidechain_type, vector<son_sidechain_info> >& curr_active_sons)
|
||||
{
|
||||
for(const auto& curr_active_sidechain_sons : curr_active_sons) {
|
||||
const auto& sidechain = curr_active_sidechain_sons.first;
|
||||
|
|
@ -298,7 +408,7 @@ void database::update_son_metrics(const flat_map<sidechain_type, vector<son_info
|
|||
current_sons.reserve(_curr_active_sidechain_sons.size());
|
||||
std::transform(_curr_active_sidechain_sons.cbegin(), _curr_active_sidechain_sons.cend(),
|
||||
std::inserter(current_sons, current_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
[](const son_sidechain_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
|
|
@ -310,7 +420,10 @@ void database::update_son_metrics(const flat_map<sidechain_type, vector<son_info
|
|||
if (is_active_son) {
|
||||
_stats.total_voted_time[sidechain] = _stats.total_voted_time[sidechain] + get_global_properties().parameters.maintenance_interval;
|
||||
}
|
||||
_stats.total_downtime[sidechain] += _stats.current_interval_downtime[sidechain];
|
||||
if(!_stats.current_interval_downtime.contains(sidechain))
|
||||
_stats.current_interval_downtime[sidechain] = 0;
|
||||
|
||||
_stats.total_downtime[sidechain] += _stats.current_interval_downtime.at(sidechain);
|
||||
_stats.current_interval_downtime[sidechain] = 0;
|
||||
_stats.sidechain_txs_reported[sidechain] = 0;
|
||||
});
|
||||
|
|
@ -318,8 +431,8 @@ void database::update_son_metrics(const flat_map<sidechain_type, vector<son_info
|
|||
}
|
||||
}
|
||||
|
||||
void database::update_son_statuses( const flat_map<sidechain_type, vector<son_info> >& curr_active_sons,
|
||||
const flat_map<sidechain_type, vector<son_info> >& new_active_sons )
|
||||
void database::update_son_statuses( const flat_map<sidechain_type, vector<son_sidechain_info> >& curr_active_sons,
|
||||
const flat_map<sidechain_type, vector<son_sidechain_info> >& new_active_sons )
|
||||
{
|
||||
for(const auto& new_active_sidechain_sons : new_active_sons) {
|
||||
const auto& sidechain = new_active_sidechain_sons.first;
|
||||
|
|
@ -332,7 +445,7 @@ void database::update_son_statuses( const flat_map<sidechain_type, vector<son_in
|
|||
current_sons.reserve(curr_active_sons.at(sidechain).size());
|
||||
std::transform(curr_active_sons.at(sidechain).cbegin(), curr_active_sons.at(sidechain).cend(),
|
||||
std::inserter(current_sons, current_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
[](const son_sidechain_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
}
|
||||
|
|
@ -340,7 +453,7 @@ void database::update_son_statuses( const flat_map<sidechain_type, vector<son_in
|
|||
new_sons.reserve(new_active_sons.at(sidechain).size());
|
||||
std::transform(new_active_sons.at(sidechain).cbegin(), new_active_sons.at(sidechain).cend(),
|
||||
std::inserter(new_sons, new_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
[](const son_sidechain_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
|
|
@ -376,7 +489,7 @@ void database::update_son_statuses( const flat_map<sidechain_type, vector<son_in
|
|||
|
||||
for (const auto &sid : sons_to_add) {
|
||||
auto son = idx.find(sid);
|
||||
FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", sid));
|
||||
FC_ASSERT(son != idx.end(), "Invalid SON in active list, id = ${sonid}.", ("sonid", sid));
|
||||
// keep maintenance status for new nodes
|
||||
if (son->statuses.at(sidechain) == son_status::inactive) {
|
||||
modify(*son, [&](son_object &obj) {
|
||||
|
|
@ -411,7 +524,7 @@ void database::update_son_statuses( const flat_map<sidechain_type, vector<son_in
|
|||
}
|
||||
}
|
||||
|
||||
void database::update_son_wallet(const flat_map<sidechain_type, vector<son_info> >& new_active_sons)
|
||||
void database::update_son_wallet(const flat_map<sidechain_type, vector<son_sidechain_info> >& new_active_sons)
|
||||
{
|
||||
bool should_recreate_pw = true;
|
||||
|
||||
|
|
@ -726,39 +839,22 @@ void database::update_active_sons()
|
|||
}
|
||||
#endif
|
||||
|
||||
const flat_map<sidechain_type, share_type> stake_target = [this]{
|
||||
flat_map<sidechain_type, share_type> stake_target;
|
||||
for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){
|
||||
const auto sidechain = son_count_histogram_buffer.first;
|
||||
stake_target[sidechain] = (_total_voting_stake-son_count_histogram_buffer.second[0]) / 2;
|
||||
}
|
||||
return stake_target;
|
||||
}();
|
||||
const auto supported_active_sidechain_types = active_sidechain_types(head_block_time());
|
||||
flat_map<sidechain_type, size_t> son_count;
|
||||
for(const auto& active_sidechain_type : supported_active_sidechain_types)
|
||||
{
|
||||
const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer.at(active_sidechain_type)[0]) / 2;
|
||||
|
||||
/// accounts that vote for 0 or 1 son do not get to express an opinion on
|
||||
/// the number of sons to have (they abstain and are non-voting accounts)
|
||||
flat_map<sidechain_type, share_type> stake_tally = []{
|
||||
flat_map<sidechain_type, share_type> stake_tally;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types){
|
||||
stake_tally[active_sidechain_type] = 0;
|
||||
}
|
||||
return stake_tally;
|
||||
}();
|
||||
flat_map<sidechain_type, size_t> son_count = []{
|
||||
flat_map<sidechain_type, size_t> son_count;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types){
|
||||
son_count[active_sidechain_type] = 0;
|
||||
}
|
||||
return son_count;
|
||||
}();
|
||||
for( const auto& stake_target_sidechain : stake_target ){
|
||||
const auto sidechain = stake_target_sidechain.first;
|
||||
if( stake_target_sidechain.second > 0 )
|
||||
/// accounts that vote for 0 or 1 son do not get to express an opinion on
|
||||
/// the number of sons to have (they abstain and are non-voting accounts)
|
||||
share_type stake_tally = 0;
|
||||
son_count[active_sidechain_type] = 0;
|
||||
if( stake_target > 0 )
|
||||
{
|
||||
while( (son_count[sidechain] < _son_count_histogram_buffer.at(sidechain).size() - 1)
|
||||
&& (stake_tally[sidechain] <= stake_target_sidechain.second) )
|
||||
while( (son_count.at(active_sidechain_type) < _son_count_histogram_buffer.at(active_sidechain_type).size() - 1)
|
||||
&& (stake_tally <= stake_target) )
|
||||
{
|
||||
stake_tally[sidechain] += _son_count_histogram_buffer.at(sidechain)[ ++son_count[sidechain] ];
|
||||
stake_tally += _son_count_histogram_buffer.at(active_sidechain_type)[ ++son_count[active_sidechain_type] ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -767,18 +863,17 @@ void database::update_active_sons()
|
|||
const chain_property_object& cpo = get_chain_properties();
|
||||
const auto& all_sons = get_index_type<son_index>().indices();
|
||||
flat_map<sidechain_type, vector<std::reference_wrapper<const son_object> > > sons;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
||||
for(const auto& active_sidechain_type : supported_active_sidechain_types)
|
||||
{
|
||||
if(head_block_time() >= HARDFORK_SON3_TIME) {
|
||||
if(head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) {
|
||||
sons[active_sidechain_type] = sort_votable_objects<son_index>(active_sidechain_type,
|
||||
(std::max(son_count.at(active_sidechain_type) * 2 + 1, (size_t)cpo.immutable_parameters.min_son_count)));
|
||||
}
|
||||
else {
|
||||
sons[active_sidechain_type] = sort_votable_objects<son_index>(active_sidechain_type, get_global_properties().parameters.maximum_son_count());
|
||||
sons[active_sidechain_type] = sort_votable_objects<son_index>(sidechain_type::bitcoin, get_global_properties().parameters.maximum_son_count());
|
||||
}
|
||||
}
|
||||
|
||||
auto& local_vote_buffer_ref = _vote_tally_buffer;
|
||||
for( const son_object& son : all_sons )
|
||||
{
|
||||
for(const auto& status: son.statuses)
|
||||
|
|
@ -793,9 +888,9 @@ void database::update_active_sons()
|
|||
}
|
||||
}
|
||||
|
||||
modify( son, [local_vote_buffer_ref]( son_object& obj ){
|
||||
modify( son, [this]( son_object& obj ){
|
||||
for(const auto& sidechain_vote_id : obj.sidechain_vote_ids ){
|
||||
obj.total_votes[sidechain_vote_id.first] = local_vote_buffer_ref[sidechain_vote_id.second];
|
||||
obj.total_votes[sidechain_vote_id.first] = _vote_tally_buffer.size() > sidechain_vote_id.second ? _vote_tally_buffer[sidechain_vote_id.second] : 0;
|
||||
}
|
||||
for(auto& status: obj.statuses)
|
||||
{
|
||||
|
|
@ -850,7 +945,7 @@ void database::update_active_sons()
|
|||
|
||||
// Compare current and to-be lists of active sons
|
||||
const auto cur_active_sons = gpo.active_sons;
|
||||
flat_map<sidechain_type, vector<son_info> > new_active_sons;
|
||||
flat_map<sidechain_type, vector<son_sidechain_info> > new_active_sons;
|
||||
const auto &acc = get(gpo.parameters.son_account());
|
||||
for( const auto& sidechain_sons : sons ){
|
||||
const auto& sidechain = sidechain_sons.first;
|
||||
|
|
@ -858,11 +953,12 @@ void database::update_active_sons()
|
|||
|
||||
new_active_sons[sidechain].reserve(sons_array.size());
|
||||
for( const son_object& son : sons_array ) {
|
||||
son_info swi;
|
||||
son_sidechain_info swi;
|
||||
swi.son_id = son.id;
|
||||
swi.weight = acc.active.account_auths.at(son.son_account);
|
||||
swi.signing_key = son.signing_key;
|
||||
swi.public_key = son.sidechain_public_keys.at(sidechain);
|
||||
if (son.sidechain_public_keys.find(sidechain) != son.sidechain_public_keys.end())
|
||||
swi.public_key = son.sidechain_public_keys.at(sidechain);
|
||||
new_active_sons[sidechain].push_back(swi);
|
||||
}
|
||||
}
|
||||
|
|
@ -902,7 +998,7 @@ void database::update_active_sons()
|
|||
}
|
||||
});
|
||||
|
||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
||||
for(const auto& active_sidechain_type : supported_active_sidechain_types)
|
||||
{
|
||||
const son_schedule_object& sidechain_sso = son_schedule_id_type(get_son_schedule_id(active_sidechain_type))(*this);
|
||||
modify(sidechain_sso, [&](son_schedule_object& _sso)
|
||||
|
|
@ -911,12 +1007,13 @@ void database::update_active_sons()
|
|||
active_sons.reserve(gpo.active_sons.at(active_sidechain_type).size());
|
||||
std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(),
|
||||
std::inserter(active_sons, active_sons.end()),
|
||||
[](const son_info& swi) {
|
||||
[](const son_sidechain_info& swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
_sso.scheduler.update(active_sons);
|
||||
// similar to witness, produce schedule for sons
|
||||
if(cur_active_sons.at(active_sidechain_type).size() == 0 && new_active_sons.at(active_sidechain_type).size() > 0)
|
||||
if( ((cur_active_sons.contains(active_sidechain_type) && cur_active_sons.at(active_sidechain_type).size() == 0) ||
|
||||
!cur_active_sons.contains(active_sidechain_type)) && new_active_sons.at(active_sidechain_type).size() > 0 )
|
||||
{
|
||||
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
|
||||
for( size_t i=0; i<new_active_sons.at(active_sidechain_type).size(); ++i )
|
||||
|
|
@ -2153,7 +2250,7 @@ void database::perform_son_tasks()
|
|||
});
|
||||
}
|
||||
// create HIVE asset here because son_account is the issuer of the HIVE
|
||||
if (gpo.parameters.hive_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_FOR_HIVE_TIME)
|
||||
if (gpo.parameters.hive_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_FOR_HIVE_TIME)
|
||||
{
|
||||
const asset_dynamic_data_object& dyn_asset =
|
||||
create<asset_dynamic_data_object>([](asset_dynamic_data_object& a) {
|
||||
|
|
@ -2186,26 +2283,69 @@ void database::perform_son_tasks()
|
|||
gpo.pending_parameters->extensions.value.hive_asset = hive_asset.get_id();
|
||||
});
|
||||
}
|
||||
|
||||
// Pay the SONs
|
||||
if (head_block_time() >= HARDFORK_SON_TIME)
|
||||
{
|
||||
// Before making a budget we should pay out SONs
|
||||
// This function should check if its time to pay sons
|
||||
// and modify the global son funds accordingly, whatever is left is passed on to next budget
|
||||
pay_sons();
|
||||
if(head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME)
|
||||
pay_sons_before_hf_ethereum();
|
||||
else
|
||||
pay_sons_after_hf_ethereum();
|
||||
}
|
||||
|
||||
// Split vote_ids
|
||||
if (head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) {
|
||||
// Get SON 1.33.0 and check if it has HIVE vote_id
|
||||
const son_id_type sid = son_id_type(0);
|
||||
const auto p_son = find(sid);
|
||||
if(p_son != nullptr) {
|
||||
if (p_son->sidechain_vote_ids.find(sidechain_type::hive) == p_son->sidechain_vote_ids.end()) {
|
||||
// Add vote_ids for HIVE and ETHEREUM to all existing SONs
|
||||
const auto &all_sons = get_index_type<son_index>().indices().get<by_id>();
|
||||
for (const son_object &son : all_sons) {
|
||||
const auto existing_vote_id_bitcoin = son.get_bitcoin_vote_id();
|
||||
vote_id_type new_vote_id_hive;
|
||||
vote_id_type new_vote_id_eth;
|
||||
|
||||
modify(gpo, [&new_vote_id_hive, &new_vote_id_eth](global_property_object &p) {
|
||||
new_vote_id_hive = get_next_vote_id(p, vote_id_type::son_hive);
|
||||
new_vote_id_eth = get_next_vote_id(p, vote_id_type::son_ethereum);
|
||||
});
|
||||
|
||||
modify(son, [new_vote_id_hive, new_vote_id_eth](son_object &obj) {
|
||||
obj.sidechain_vote_ids[sidechain_type::hive] = new_vote_id_hive;
|
||||
obj.sidechain_vote_ids[sidechain_type::ethereum] = new_vote_id_eth;
|
||||
});
|
||||
|
||||
// Duplicate all votes from bitcoin to hive
|
||||
const auto &all_accounts = get_index_type<account_index>().indices().get<by_id>();
|
||||
for (const auto &account : all_accounts) {
|
||||
if (existing_vote_id_bitcoin.valid() && account.options.votes.count(*existing_vote_id_bitcoin) != 0) {
|
||||
modify(account, [new_vote_id_hive](account_object &a) {
|
||||
a.options.votes.insert(new_vote_id_hive);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_son_params(database& db)
|
||||
{
|
||||
if( (db.head_block_time() >= HARDFORK_SON2_TIME) && (db.head_block_time() < HARDFORK_SON3_TIME) )
|
||||
if( (db.head_block_time() >= HARDFORK_SON2_TIME) && (db.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME) )
|
||||
{
|
||||
const auto& gpo = db.get_global_properties();
|
||||
db.modify( gpo, []( global_property_object& gpo ) {
|
||||
gpo.parameters.extensions.value.maximum_son_count = 7;
|
||||
});
|
||||
}
|
||||
else
|
||||
|
||||
if( (db.head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) )
|
||||
{
|
||||
const auto& gpo = db.get_global_properties();
|
||||
db.modify( gpo, []( global_property_object& gpo ) {
|
||||
|
|
@ -2340,20 +2480,23 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
// same rationale as for witnesses
|
||||
d._committee_count_histogram_buffer[offset] += voting_stake;
|
||||
}
|
||||
FC_ASSERT( opinion_account.options.extensions.value.num_son.valid() , "Invalid son number" );
|
||||
for(const auto& num_sidechain_son : *opinion_account.options.extensions.value.num_son) {
|
||||
const auto sidechain = num_sidechain_son.first;
|
||||
const auto& num_son = num_sidechain_son.second;
|
||||
if (num_son <= props.parameters.maximum_son_count()) {
|
||||
uint16_t offset = std::min(size_t(num_son / 2),
|
||||
d._son_count_histogram_buffer.at(sidechain).size() - 1);
|
||||
// votes for a number greater than maximum_son_count
|
||||
// are turned into votes for maximum_son_count.
|
||||
//
|
||||
// in particular, this takes care of the case where a
|
||||
// member was voting for a high number, then the
|
||||
// parameter was lowered.
|
||||
d._son_count_histogram_buffer.at(sidechain)[offset] += voting_stake;
|
||||
|
||||
if ( opinion_account.options.extensions.value.num_son.valid() )
|
||||
{
|
||||
for(const auto& num_sidechain_son : *opinion_account.options.extensions.value.num_son) {
|
||||
const auto sidechain = num_sidechain_son.first;
|
||||
const auto& num_son = num_sidechain_son.second;
|
||||
if (num_son <= props.parameters.maximum_son_count()) {
|
||||
uint16_t offset = std::min(size_t(num_son / 2),
|
||||
d._son_count_histogram_buffer.at(sidechain).size() - 1);
|
||||
// votes for a number greater than maximum_son_count
|
||||
// are turned into votes for maximum_son_count.
|
||||
//
|
||||
// in particular, this takes care of the case where a
|
||||
// member was voting for a high number, then the
|
||||
// parameter was lowered.
|
||||
d._son_count_histogram_buffer.at(sidechain)[offset] += voting_stake;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2390,6 +2533,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
update_active_committee_members();
|
||||
update_active_sons();
|
||||
update_worker_votes();
|
||||
hotfix_2024();
|
||||
|
||||
const dynamic_global_property_object& dgpo = get_dynamic_global_properties();
|
||||
|
||||
|
|
|
|||
|
|
@ -112,7 +112,6 @@ void database::reindex( fc::path data_dir )
|
|||
uint32_t undo_point = last_block_num < 50 ? 0 : last_block_num - 50;
|
||||
|
||||
ilog( "Replaying blocks, starting at ${next}...", ("next",head_block_num() + 1) );
|
||||
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
|
||||
auto_undo_enabler undo(_slow_replays, _undo_db);
|
||||
if( head_block_num() >= undo_point )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -200,43 +200,41 @@ void database::update_witness_schedule()
|
|||
}
|
||||
}
|
||||
|
||||
void database::update_son_schedule()
|
||||
void database::update_son_schedule(sidechain_type type)
|
||||
{
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
|
||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
||||
const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(type)));
|
||||
if( gpo.active_sons.at(type).size() != 0 &&
|
||||
head_block_num() % gpo.active_sons.at(type).size() == 0)
|
||||
{
|
||||
const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type)));
|
||||
if( head_block_num() % gpo.active_sons.at(active_sidechain_type).size() == 0)
|
||||
modify( sidechain_sso, [&]( son_schedule_object& _sso )
|
||||
{
|
||||
modify( sidechain_sso, [&]( son_schedule_object& _sso )
|
||||
_sso.current_shuffled_sons.clear();
|
||||
_sso.current_shuffled_sons.reserve( gpo.active_sons.at(type).size() );
|
||||
|
||||
for ( const auto &w : gpo.active_sons.at(type) ) {
|
||||
_sso.current_shuffled_sons.push_back(w.son_id);
|
||||
}
|
||||
|
||||
auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32;
|
||||
|
||||
for (uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i)
|
||||
{
|
||||
_sso.current_shuffled_sons.clear();
|
||||
_sso.current_shuffled_sons.reserve( gpo.active_sons.at(active_sidechain_type).size() );
|
||||
/// High performance random generator
|
||||
/// http://xorshift.di.unimi.it/
|
||||
uint64_t k = now_hi + uint64_t(i) * 2685821657736338717ULL;
|
||||
k ^= (k >> 12);
|
||||
k ^= (k << 25);
|
||||
k ^= (k >> 27);
|
||||
k *= 2685821657736338717ULL;
|
||||
|
||||
for ( const son_info &w : gpo.active_sons.at(active_sidechain_type) ) {
|
||||
_sso.current_shuffled_sons.push_back(w.son_id);
|
||||
}
|
||||
|
||||
auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32;
|
||||
|
||||
for (uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i)
|
||||
{
|
||||
/// High performance random generator
|
||||
/// http://xorshift.di.unimi.it/
|
||||
uint64_t k = now_hi + uint64_t(i) * 2685821657736338717ULL;
|
||||
k ^= (k >> 12);
|
||||
k ^= (k << 25);
|
||||
k ^= (k >> 27);
|
||||
k *= 2685821657736338717ULL;
|
||||
|
||||
uint32_t jmax = _sso.current_shuffled_sons.size() - i;
|
||||
uint32_t j = i + k % jmax;
|
||||
std::swap(_sso.current_shuffled_sons[i],
|
||||
_sso.current_shuffled_sons[j]);
|
||||
}
|
||||
});
|
||||
}
|
||||
uint32_t jmax = _sso.current_shuffled_sons.size() - i;
|
||||
uint32_t j = i + k % jmax;
|
||||
std::swap(_sso.current_shuffled_sons[i],
|
||||
_sso.current_shuffled_sons[j]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -320,23 +318,15 @@ void database::update_witness_schedule(const signed_block& next_block)
|
|||
idump( ( double(total_time/1000000.0)/calls) );
|
||||
}
|
||||
|
||||
void database::update_son_schedule(const signed_block& next_block)
|
||||
void database::update_son_schedule(sidechain_type type, const signed_block& next_block)
|
||||
{
|
||||
auto start = fc::time_point::now();
|
||||
#ifndef NDEBUG
|
||||
const son_schedule_object& sso = get(son_schedule_id_type());
|
||||
#endif
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
const flat_map<sidechain_type, uint32_t> schedule_needs_filled = [&gpo]()
|
||||
{
|
||||
flat_map<sidechain_type, uint32_t> schedule_needs_filled;
|
||||
for(const auto& sidechain_active_sons : gpo.active_sons)
|
||||
{
|
||||
schedule_needs_filled[sidechain_active_sons.first] = sidechain_active_sons.second.size();
|
||||
}
|
||||
return schedule_needs_filled;
|
||||
}();
|
||||
uint32_t schedule_slot = get_slot_at_time(next_block.timestamp);
|
||||
const uint32_t schedule_needs_filled = gpo.active_sons.at(type).size();
|
||||
const uint32_t schedule_slot = get_slot_at_time(next_block.timestamp);
|
||||
|
||||
// We shouldn't be able to generate _pending_block with timestamp
|
||||
// in the past, and incoming blocks from the network with timestamp
|
||||
|
|
@ -350,46 +340,43 @@ void database::update_son_schedule(const signed_block& next_block)
|
|||
assert( dpo.random.data_size() == witness_scheduler_rng::seed_length );
|
||||
assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() );
|
||||
|
||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
||||
const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(type)));
|
||||
son_id_type first_son;
|
||||
bool slot_is_near = sidechain_sso.scheduler.get_slot( schedule_slot-1, first_son );
|
||||
son_id_type son_id;
|
||||
|
||||
modify(sidechain_sso, [&](son_schedule_object& _sso)
|
||||
{
|
||||
const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type)));
|
||||
son_id_type first_son;
|
||||
bool slot_is_near = sidechain_sso.scheduler.get_slot( schedule_slot-1, first_son );
|
||||
son_id_type son_id;
|
||||
_sso.slots_since_genesis += schedule_slot;
|
||||
witness_scheduler_rng rng(_sso.rng_seed.data, _sso.slots_since_genesis);
|
||||
|
||||
modify(sidechain_sso, [&](son_schedule_object& _sso)
|
||||
{
|
||||
_sso.slots_since_genesis += schedule_slot;
|
||||
witness_scheduler_rng rng(_sso.rng_seed.data, _sso.slots_since_genesis);
|
||||
_sso.scheduler._min_token_count = std::max(int(gpo.active_sons.at(type).size()) / 2, 1);
|
||||
|
||||
_sso.scheduler._min_token_count = std::max(int(gpo.active_sons.at(active_sidechain_type).size()) / 2, 1);
|
||||
|
||||
if( slot_is_near )
|
||||
if( slot_is_near )
|
||||
{
|
||||
uint32_t drain = schedule_slot;
|
||||
while( drain > 0 )
|
||||
{
|
||||
uint32_t drain = schedule_slot;
|
||||
while( drain > 0 )
|
||||
{
|
||||
if( _sso.scheduler.size() == 0 )
|
||||
break;
|
||||
_sso.scheduler.consume_schedule();
|
||||
--drain;
|
||||
}
|
||||
if( _sso.scheduler.size() == 0 )
|
||||
break;
|
||||
_sso.scheduler.consume_schedule();
|
||||
--drain;
|
||||
}
|
||||
else
|
||||
{
|
||||
_sso.scheduler.reset_schedule( first_son );
|
||||
}
|
||||
while( !_sso.scheduler.get_slot(schedule_needs_filled.at(active_sidechain_type), son_id) )
|
||||
{
|
||||
if( _sso.scheduler.produce_schedule(rng) & emit_turn )
|
||||
memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size());
|
||||
}
|
||||
_sso.last_scheduling_block = next_block.block_num();
|
||||
_sso.recent_slots_filled = (
|
||||
(_sso.recent_slots_filled << 1)
|
||||
+ 1) << (schedule_slot - 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_sso.scheduler.reset_schedule( first_son );
|
||||
}
|
||||
while( !_sso.scheduler.get_slot(schedule_needs_filled, son_id) )
|
||||
{
|
||||
if( _sso.scheduler.produce_schedule(rng) & emit_turn )
|
||||
memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size());
|
||||
}
|
||||
_sso.last_scheduling_block = next_block.block_num();
|
||||
_sso.recent_slots_filled = (
|
||||
(_sso.recent_slots_filled << 1)
|
||||
+ 1) << (schedule_slot - 1);
|
||||
});
|
||||
|
||||
auto end = fc::time_point::now();
|
||||
static uint64_t total_time = 0;
|
||||
|
|
|
|||
7
libraries/chain/hardfork.d/HOTFIX_2024.hf
Normal file
7
libraries/chain/hardfork.d/HOTFIX_2024.hf
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef HARDFORK_HOTFIX_2024_TIME
|
||||
#ifdef BUILD_PEERPLAYS_TESTNET
|
||||
#define HARDFORK_HOTFIX_2024_TIME (fc::time_point_sec::from_iso_string("2023-12-20T00:00:00"))
|
||||
#else
|
||||
#define HARDFORK_HOTFIX_2024_TIME (fc::time_point_sec::from_iso_string("2023-12-20T00:00:00"))
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
#ifndef HARDFORK_SON3_TIME
|
||||
#ifdef BUILD_PEERPLAYS_TESTNET
|
||||
#define HARDFORK_SON3_TIME (fc::time_point_sec::from_iso_string("2022-07-16T00:00:00"))
|
||||
#else
|
||||
#define HARDFORK_SON3_TIME (fc::time_point_sec::from_iso_string("2022-07-16T00:00:00"))
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef HARDFORK_SON_FOR_ETHEREUM_TIME
|
||||
#ifdef BUILD_PEERPLAYS_TESTNET
|
||||
#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2022-07-01T00:00:00"))
|
||||
#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-07-17T12:00:00"))
|
||||
#else
|
||||
#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2022-07-01T00:00:00"))
|
||||
#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-10-24T12:00:00"))
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@
|
|||
#define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4
|
||||
#define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3
|
||||
|
||||
#define GRAPHENE_CURRENT_DB_VERSION "PPY2.4"
|
||||
#define GRAPHENE_CURRENT_DB_VERSION "PPY2.5"
|
||||
|
||||
#define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT)
|
||||
|
||||
|
|
|
|||
|
|
@ -292,8 +292,8 @@ namespace graphene { namespace chain {
|
|||
vector<witness_id_type> get_near_witness_schedule()const;
|
||||
void update_witness_schedule();
|
||||
void update_witness_schedule(const signed_block& next_block);
|
||||
void update_son_schedule();
|
||||
void update_son_schedule(const signed_block& next_block);
|
||||
void update_son_schedule(sidechain_type type);
|
||||
void update_son_schedule(sidechain_type type, const signed_block& next_block);
|
||||
|
||||
void check_lottery_end_by_participants( asset_id_type asset_id );
|
||||
void check_ending_lotteries();
|
||||
|
|
@ -579,20 +579,22 @@ namespace graphene { namespace chain {
|
|||
void initialize_budget_record( fc::time_point_sec now, budget_record& rec )const;
|
||||
void process_budget();
|
||||
void pay_workers( share_type& budget );
|
||||
void pay_sons();
|
||||
void pay_sons_before_hf_ethereum();
|
||||
void pay_sons_after_hf_ethereum();
|
||||
void perform_son_tasks();
|
||||
void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props);
|
||||
void update_active_witnesses();
|
||||
void update_active_committee_members();
|
||||
void update_son_metrics( const flat_map<sidechain_type, vector<son_info> >& curr_active_sons );
|
||||
void update_son_metrics( const flat_map<sidechain_type, vector<son_sidechain_info> >& curr_active_sons );
|
||||
void update_active_sons();
|
||||
void remove_son_proposal( const proposal_object& proposal );
|
||||
void remove_inactive_son_down_proposals( const vector<son_id_type>& son_ids_to_remove );
|
||||
void remove_inactive_son_proposals( const vector<son_id_type>& son_ids_to_remove );
|
||||
void update_son_statuses( const flat_map<sidechain_type, vector<son_info> >& curr_active_sons,
|
||||
const flat_map<sidechain_type, vector<son_info> >& new_active_sons );
|
||||
void update_son_wallet( const flat_map<sidechain_type, vector<son_info> >& new_active_sons );
|
||||
void update_son_statuses( const flat_map<sidechain_type, vector<son_sidechain_info> >& curr_active_sons,
|
||||
const flat_map<sidechain_type, vector<son_sidechain_info> >& new_active_sons );
|
||||
void update_son_wallet( const flat_map<sidechain_type, vector<son_sidechain_info> >& new_active_sons );
|
||||
void update_worker_votes();
|
||||
void hotfix_2024();
|
||||
|
||||
public:
|
||||
double calculate_vesting_factor(const account_object& stake_account);
|
||||
|
|
@ -636,7 +638,7 @@ namespace graphene { namespace chain {
|
|||
vector<uint64_t> _committee_count_histogram_buffer;
|
||||
flat_map<sidechain_type, vector<uint64_t> > _son_count_histogram_buffer = []{
|
||||
flat_map<sidechain_type, vector<uint64_t> > son_count_histogram_buffer;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types){
|
||||
for(const auto& active_sidechain_type : all_sidechain_types){
|
||||
son_count_histogram_buffer[active_sidechain_type] = vector<uint64_t>{};
|
||||
}
|
||||
return son_count_histogram_buffer;
|
||||
|
|
|
|||
|
|
@ -182,6 +182,9 @@ namespace graphene { namespace chain {
|
|||
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( override_transfer );
|
||||
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( not_permitted, override_transfer, 1, "not permitted" )
|
||||
|
||||
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( blind_transfer );
|
||||
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unknown_commitment, blind_transfer, 1, "Attempting to claim an unknown prior commitment" );
|
||||
|
||||
/*
|
||||
FC_DECLARE_DERIVED_EXCEPTION( addition_overflow, graphene::chain::chain_exception, 30002, "addition overflow" )
|
||||
FC_DECLARE_DERIVED_EXCEPTION( subtraction_overflow, graphene::chain::chain_exception, 30003, "subtraction overflow" )
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
#include <graphene/chain/protocol/chain_parameters.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/chain/son_sidechain_info.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
|
@ -49,15 +49,15 @@ namespace graphene { namespace chain {
|
|||
chain_parameters parameters;
|
||||
optional<chain_parameters> pending_parameters;
|
||||
|
||||
uint32_t next_available_vote_id = 0;
|
||||
vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval
|
||||
flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval
|
||||
flat_map<sidechain_type, vector<son_info> > active_sons = []() // updated once per maintenance interval
|
||||
uint32_t next_available_vote_id = 0;
|
||||
vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval
|
||||
flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval
|
||||
flat_map<sidechain_type, vector<son_sidechain_info> > active_sons = []() // updated once per maintenance interval
|
||||
{
|
||||
flat_map<sidechain_type, vector<son_info> > active_sons;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
||||
flat_map<sidechain_type, vector<son_sidechain_info> > active_sons;
|
||||
for(const auto& active_sidechain_type : all_sidechain_types)
|
||||
{
|
||||
active_sons[active_sidechain_type] = vector<son_info>();
|
||||
active_sons[active_sidechain_type] = vector<son_sidechain_info>();
|
||||
}
|
||||
return active_sons;
|
||||
}();
|
||||
|
|
|
|||
|
|
@ -130,6 +130,9 @@ namespace graphene { namespace chain {
|
|||
std::greater< uint32_t >,
|
||||
std::greater< object_id_type >
|
||||
>
|
||||
>,
|
||||
ordered_non_unique< tag<by_owner>,
|
||||
member<nft_metadata_object, account_id_type, &nft_metadata_object::owner>
|
||||
>
|
||||
>
|
||||
>;
|
||||
|
|
|
|||
|
|
@ -36,21 +36,28 @@ namespace graphene { namespace chain {
|
|||
bool is_cheap_name( const string& n );
|
||||
|
||||
/// These are the fields which can be updated by the active authority.
|
||||
struct account_options
|
||||
struct account_options
|
||||
{
|
||||
struct ext
|
||||
{
|
||||
/// The number of active son members this account votes the blockchain should appoint
|
||||
/// Must not exceed the actual number of son members voted for in @ref votes
|
||||
optional< flat_map<sidechain_type, uint16_t> > num_son = []{
|
||||
optional< flat_map<sidechain_type, uint16_t> > num_son;
|
||||
|
||||
/// Returns and empty num_son map with all sidechains
|
||||
static flat_map<sidechain_type, uint16_t> empty_num_son()
|
||||
{
|
||||
flat_map<sidechain_type, uint16_t> num_son;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types){
|
||||
for(const auto& active_sidechain_type : all_sidechain_types)
|
||||
{
|
||||
num_son[active_sidechain_type] = 0;
|
||||
}
|
||||
|
||||
return num_son;
|
||||
}();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non-
|
||||
/// validated account activities. This field is here to prevent confusion if the active authority has zero or
|
||||
/// multiple keys in it.
|
||||
|
|
|
|||
|
|
@ -158,9 +158,7 @@ struct transfer_to_blind_operation : public base_operation
|
|||
blind_factor_type blinding_factor;
|
||||
vector<blind_output> outputs;
|
||||
|
||||
account_id_type fee_payer()const { return account_id_type{}; }
|
||||
|
||||
//account_id_type fee_payer()const { return from; }
|
||||
account_id_type fee_payer()const { return from; }
|
||||
//void validate()const;
|
||||
//share_type calculate_fee(const fee_parameters_type& )const;
|
||||
};
|
||||
|
|
@ -181,9 +179,7 @@ struct transfer_from_blind_operation : public base_operation
|
|||
blind_factor_type blinding_factor;
|
||||
vector<blind_input> inputs;
|
||||
|
||||
account_id_type fee_payer()const { return account_id_type{}; }
|
||||
|
||||
//account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; }
|
||||
account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; }
|
||||
//void validate()const;
|
||||
//void get_required_authorities( vector<authority>& a )const
|
||||
//{
|
||||
|
|
@ -246,10 +242,8 @@ struct blind_transfer_operation : public base_operation
|
|||
vector<blind_input> inputs;
|
||||
vector<blind_output> outputs;
|
||||
|
||||
account_id_type fee_payer()const { return account_id_type{}; }
|
||||
|
||||
/** graphene TEMP account */
|
||||
//account_id_type fee_payer()const;
|
||||
account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; }
|
||||
//void validate()const;
|
||||
//share_type calculate_fee( const fee_parameters_type& k )const;
|
||||
//void get_required_authorities( vector<authority>& a )const
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace graphene
|
|||
// Buyer purchasing lottery tickets
|
||||
account_id_type buyer;
|
||||
// count of tickets to buy
|
||||
uint64_t tickets_to_buy;
|
||||
share_type tickets_to_buy;
|
||||
// amount that can spent
|
||||
asset amount;
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ FC_REFLECT( graphene::chain::sidechain_transaction_create_operation, (fee)(payer
|
|||
(sidechain)
|
||||
(object_id)
|
||||
(transaction)
|
||||
(signers) )
|
||||
(signers))
|
||||
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation, (fee)(signer)(payer)
|
||||
|
|
|
|||
|
|
@ -1,40 +1,47 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/chain/son_sidechain_info.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct son_wallet_recreate_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
struct son_wallet_recreate_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
struct ext
|
||||
{
|
||||
optional<flat_map<sidechain_type, vector<son_sidechain_info> > > sidechain_sons;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
flat_map<sidechain_type, vector<son_info> > sons;
|
||||
vector<son_info> sons;
|
||||
extension< ext > extensions;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_wallet_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
struct son_wallet_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
son_wallet_id_type son_wallet_id;
|
||||
sidechain_type sidechain;
|
||||
string address;
|
||||
son_wallet_id_type son_wallet_id;
|
||||
sidechain_type sidechain;
|
||||
string address;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::son_wallet_recreate_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_recreate_operation, (fee)(payer)(sons) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_recreate_operation::ext, (sidechain_sons))
|
||||
FC_REFLECT(graphene::chain::son_wallet_recreate_operation, (fee)(payer)(sons)(extensions) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_update_operation, (fee)(payer)(son_wallet_id)(sidechain)(address) )
|
||||
|
|
|
|||
|
|
@ -36,6 +36,15 @@ namespace graphene { namespace chain {
|
|||
deposit_address(""),
|
||||
withdraw_public_key(""),
|
||||
withdraw_address("") {}
|
||||
|
||||
inline string get_deposit_address() const {
|
||||
if(sidechain_type::ethereum != sidechain)
|
||||
return deposit_address;
|
||||
|
||||
auto deposit_address_lower = deposit_address;
|
||||
std::transform(deposit_address_lower.begin(), deposit_address_lower.end(), deposit_address_lower.begin(), ::tolower);
|
||||
return deposit_address_lower;
|
||||
}
|
||||
};
|
||||
|
||||
struct by_account;
|
||||
|
|
@ -76,7 +85,7 @@ namespace graphene { namespace chain {
|
|||
ordered_non_unique< tag<by_sidechain_and_deposit_address_and_expires>,
|
||||
composite_key<sidechain_address_object,
|
||||
member<sidechain_address_object, sidechain_type, &sidechain_address_object::sidechain>,
|
||||
member<sidechain_address_object, string, &sidechain_address_object::deposit_address>,
|
||||
const_mem_fun<sidechain_address_object, string, &sidechain_address_object::get_deposit_address>,
|
||||
member<sidechain_address_object, time_point_sec, &sidechain_address_object::expires>
|
||||
>
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
#include <fc/time.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -14,9 +18,23 @@ enum class sidechain_type {
|
|||
hive
|
||||
};
|
||||
|
||||
static const std::set<sidechain_type> active_sidechain_types = {sidechain_type::bitcoin, sidechain_type::ethereum, sidechain_type::hive};
|
||||
static const std::set<sidechain_type> all_sidechain_types = {sidechain_type::bitcoin, sidechain_type::ethereum, sidechain_type::hive};
|
||||
|
||||
} }
|
||||
inline std::set<sidechain_type> active_sidechain_types(const fc::time_point_sec block_time) {
|
||||
std::set<sidechain_type> active_sidechain_types{};
|
||||
|
||||
if (block_time >= HARDFORK_SON_TIME)
|
||||
active_sidechain_types.insert(sidechain_type::bitcoin);
|
||||
if (block_time >= HARDFORK_SON_FOR_HIVE_TIME)
|
||||
active_sidechain_types.insert(sidechain_type::hive);
|
||||
if (block_time >= HARDFORK_SON_FOR_ETHEREUM_TIME)
|
||||
active_sidechain_types.insert(sidechain_type::ethereum);
|
||||
|
||||
return active_sidechain_types;
|
||||
}
|
||||
|
||||
} // namespace chain
|
||||
} // namespace graphene
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::sidechain_type,
|
||||
(unknown)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#include <boost/multi_index/composite_key.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/chain/son_sidechain_info.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
|
@ -30,7 +30,7 @@ namespace graphene { namespace chain {
|
|||
sidechain_type sidechain = sidechain_type::unknown;
|
||||
object_id_type object_id;
|
||||
std::string transaction;
|
||||
std::vector<son_info> signers;
|
||||
std::vector<son_sidechain_info> signers;
|
||||
std::vector<std::pair<son_id_type, std::string>> signatures;
|
||||
std::string sidechain_transaction;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,34 +3,40 @@
|
|||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class son_info
|
||||
* @brief tracks information about a SON info required to re/create primary wallet
|
||||
* @ingroup object
|
||||
* @class son_info
|
||||
* @brief tracks information about a SON info required to re/create primary wallet
|
||||
* @ingroup object
|
||||
*/
|
||||
struct son_info {
|
||||
son_id_type son_id;
|
||||
weight_type weight = 0;
|
||||
public_key_type signing_key;
|
||||
string public_key;
|
||||
flat_map<sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
bool operator==(const son_info& rhs) const {
|
||||
bool operator==(const son_info& rhs) {
|
||||
bool son_sets_equal =
|
||||
(son_id == rhs.son_id) &&
|
||||
(weight == rhs.weight) &&
|
||||
(signing_key == rhs.signing_key) &&
|
||||
(public_key == rhs.public_key);
|
||||
(sidechain_public_keys.size() == rhs.sidechain_public_keys.size());
|
||||
|
||||
if (son_sets_equal) {
|
||||
bool sidechain_public_keys_equal = true;
|
||||
for (size_t i = 0; i < sidechain_public_keys.size(); i++) {
|
||||
const auto lhs_scpk = sidechain_public_keys.nth(i);
|
||||
const auto rhs_scpk = rhs.sidechain_public_keys.nth(i);
|
||||
sidechain_public_keys_equal = sidechain_public_keys_equal &&
|
||||
(lhs_scpk->first == rhs_scpk->first) &&
|
||||
(lhs_scpk->second == rhs_scpk->second);
|
||||
}
|
||||
son_sets_equal = son_sets_equal && sidechain_public_keys_equal;
|
||||
}
|
||||
return son_sets_equal;
|
||||
}
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::son_info,
|
||||
(son_id)
|
||||
(weight)
|
||||
(signing_key)
|
||||
(public_key) )
|
||||
FC_REFLECT( graphene::chain::son_info, (son_id) (weight) (signing_key) (sidechain_public_keys) )
|
||||
|
|
|
|||
|
|
@ -65,7 +65,15 @@ namespace graphene { namespace chain {
|
|||
|
||||
account_id_type son_account;
|
||||
flat_map<sidechain_type, vote_id_type> sidechain_vote_ids;
|
||||
flat_map<sidechain_type, uint64_t> total_votes;
|
||||
flat_map<sidechain_type, uint64_t> total_votes = []()
|
||||
{
|
||||
flat_map<sidechain_type, uint64_t> total_votes;
|
||||
for(const auto& active_sidechain_type : all_sidechain_types)
|
||||
{
|
||||
total_votes[active_sidechain_type] = 0;
|
||||
}
|
||||
return total_votes;
|
||||
}();
|
||||
string url;
|
||||
vesting_balance_id_type deposit;
|
||||
public_key_type signing_key;
|
||||
|
|
@ -74,7 +82,7 @@ namespace graphene { namespace chain {
|
|||
flat_map<sidechain_type, son_status> statuses = []()
|
||||
{
|
||||
flat_map<sidechain_type, son_status> statuses;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
||||
for(const auto& active_sidechain_type : all_sidechain_types)
|
||||
{
|
||||
statuses[active_sidechain_type] = son_status::inactive;
|
||||
}
|
||||
|
|
@ -83,13 +91,15 @@ namespace graphene { namespace chain {
|
|||
flat_map<sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
void pay_son_fee(share_type pay, database& db);
|
||||
bool has_valid_config()const;
|
||||
bool has_valid_config(time_point_sec head_block_time)const;
|
||||
bool has_valid_config(time_point_sec head_block_time, sidechain_type sidechain) const;
|
||||
|
||||
inline vote_id_type get_sidechain_vote_id(sidechain_type sidechain) const { return sidechain_vote_ids.at(sidechain); }
|
||||
inline vote_id_type get_bitcoin_vote_id() const { return get_sidechain_vote_id(sidechain_type::bitcoin); }
|
||||
inline vote_id_type get_hive_vote_id() const { return get_sidechain_vote_id(sidechain_type::hive); }
|
||||
inline vote_id_type get_ethereum_vote_id() const { return get_sidechain_vote_id(sidechain_type::ethereum); }
|
||||
inline optional<vote_id_type> get_sidechain_vote_id(sidechain_type sidechain) const { return sidechain_vote_ids.contains(sidechain) ? sidechain_vote_ids.at(sidechain) : optional<vote_id_type>{}; }
|
||||
inline optional<vote_id_type> get_bitcoin_vote_id() const { return get_sidechain_vote_id(sidechain_type::bitcoin); }
|
||||
inline optional<vote_id_type> get_hive_vote_id() const { return get_sidechain_vote_id(sidechain_type::hive); }
|
||||
inline optional<vote_id_type> get_ethereum_vote_id() const { return get_sidechain_vote_id(sidechain_type::ethereum); }
|
||||
|
||||
private:
|
||||
bool has_valid_config(sidechain_type sidechain) const;
|
||||
};
|
||||
|
||||
struct by_account;
|
||||
|
|
@ -105,14 +115,14 @@ namespace graphene { namespace chain {
|
|||
ordered_unique< tag<by_account>,
|
||||
member<son_object, account_id_type, &son_object::son_account>
|
||||
>,
|
||||
ordered_unique< tag<by_vote_id_bitcoin>,
|
||||
const_mem_fun<son_object, vote_id_type, &son_object::get_bitcoin_vote_id>
|
||||
ordered_non_unique< tag<by_vote_id_bitcoin>,
|
||||
const_mem_fun<son_object, optional<vote_id_type>, &son_object::get_bitcoin_vote_id>
|
||||
>,
|
||||
ordered_unique< tag<by_vote_id_hive>,
|
||||
const_mem_fun<son_object, vote_id_type, &son_object::get_hive_vote_id>
|
||||
ordered_non_unique< tag<by_vote_id_hive>,
|
||||
const_mem_fun<son_object, optional<vote_id_type>, &son_object::get_hive_vote_id>
|
||||
>,
|
||||
ordered_unique< tag<by_vote_id_ethereum>,
|
||||
const_mem_fun<son_object, vote_id_type, &son_object::get_ethereum_vote_id>
|
||||
ordered_non_unique< tag<by_vote_id_ethereum>,
|
||||
const_mem_fun<son_object, optional<vote_id_type>, &son_object::get_ethereum_vote_id>
|
||||
>
|
||||
>
|
||||
>;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
/**
|
||||
* @class son_sidechain_info
|
||||
* @brief tracks information about a SON info required to re/create primary wallet
|
||||
* @ingroup object
|
||||
*/
|
||||
struct son_sidechain_info {
|
||||
son_id_type son_id;
|
||||
weight_type weight = 0;
|
||||
public_key_type signing_key;
|
||||
string public_key;
|
||||
|
||||
bool operator==(const son_sidechain_info& rhs) const {
|
||||
bool son_sets_equal =
|
||||
(son_id == rhs.son_id) &&
|
||||
(weight == rhs.weight) &&
|
||||
(signing_key == rhs.signing_key) &&
|
||||
(public_key == rhs.public_key);
|
||||
|
||||
return son_sets_equal;
|
||||
}
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::son_sidechain_info, (son_id) (weight) (signing_key) (public_key) )
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/chain/son_sidechain_info.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
|
@ -21,7 +21,7 @@ namespace graphene { namespace chain {
|
|||
time_point_sec expires;
|
||||
|
||||
flat_map<sidechain_type, string> addresses;
|
||||
flat_map<sidechain_type, vector<son_info> > sons;
|
||||
flat_map<sidechain_type, vector<son_sidechain_info> > sons;
|
||||
};
|
||||
|
||||
struct by_valid_from;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace graphene
|
|||
|
||||
auto lottery_options = lottery_md_obj.lottery_data->lottery_options;
|
||||
FC_ASSERT(lottery_options.ticket_price.asset_id == op.amount.asset_id);
|
||||
FC_ASSERT((double)op.amount.amount.value / lottery_options.ticket_price.amount.value == (double)op.tickets_to_buy);
|
||||
FC_ASSERT(op.tickets_to_buy * lottery_options.ticket_price.amount.value == op.amount.amount.value);
|
||||
return void_result();
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
|
|
|
|||
|
|
@ -174,31 +174,37 @@ void account_options::validate() const
|
|||
{
|
||||
auto needed_witnesses = num_witness;
|
||||
auto needed_committee = num_committee;
|
||||
FC_ASSERT( extensions.value.num_son.valid() , "Invalid son number" );
|
||||
flat_map<sidechain_type, uint16_t> needed_sons = *extensions.value.num_son;
|
||||
|
||||
for( vote_id_type id : votes )
|
||||
if( id.type() == vote_id_type::witness && needed_witnesses )
|
||||
--needed_witnesses;
|
||||
else if ( id.type() == vote_id_type::committee && needed_committee )
|
||||
--needed_committee;
|
||||
else if ( id.type() == vote_id_type::son_bitcoin && needed_sons[sidechain_type::bitcoin] )
|
||||
--needed_sons[sidechain_type::bitcoin];
|
||||
else if ( id.type() == vote_id_type::son_hive && needed_sons[sidechain_type::hive] )
|
||||
--needed_sons[sidechain_type::hive];
|
||||
else if ( id.type() == vote_id_type::son_ethereum && needed_sons[sidechain_type::ethereum] )
|
||||
--needed_sons[sidechain_type::ethereum];
|
||||
|
||||
FC_ASSERT( needed_witnesses == 0,
|
||||
"May not specify fewer witnesses than the number voted for.");
|
||||
FC_ASSERT( needed_committee == 0,
|
||||
"May not specify fewer committee members than the number voted for.");
|
||||
FC_ASSERT( needed_sons[sidechain_type::bitcoin] == 0,
|
||||
"May not specify fewer Bitcoin SONs than the number voted for.");
|
||||
FC_ASSERT( needed_sons[sidechain_type::hive] == 0,
|
||||
"May not specify fewer Hive SONs than the number voted for.");
|
||||
FC_ASSERT( needed_sons[sidechain_type::ethereum] == 0,
|
||||
"May not specify fewer Ethereum SONs than the number voted for.");
|
||||
|
||||
if ( extensions.value.num_son.valid() )
|
||||
{
|
||||
flat_map<sidechain_type, uint16_t> needed_sons = *extensions.value.num_son;
|
||||
|
||||
for( vote_id_type id : votes )
|
||||
if ( id.type() == vote_id_type::son_bitcoin && needed_sons[sidechain_type::bitcoin] )
|
||||
--needed_sons[sidechain_type::bitcoin];
|
||||
else if ( id.type() == vote_id_type::son_hive && needed_sons[sidechain_type::hive] )
|
||||
--needed_sons[sidechain_type::hive];
|
||||
else if ( id.type() == vote_id_type::son_ethereum && needed_sons[sidechain_type::ethereum] )
|
||||
--needed_sons[sidechain_type::ethereum];
|
||||
|
||||
FC_ASSERT( needed_sons[sidechain_type::bitcoin] == 0,
|
||||
"May not specify fewer Bitcoin SONs than the number voted for.");
|
||||
FC_ASSERT( needed_sons[sidechain_type::hive] == 0,
|
||||
"May not specify fewer Hive SONs than the number voted for.");
|
||||
FC_ASSERT( needed_sons[sidechain_type::ethereum] == 0,
|
||||
"May not specify fewer Ethereum SONs than the number voted for.");
|
||||
}
|
||||
}
|
||||
|
||||
void affiliate_reward_distribution::validate() const
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace graphene { namespace chain {
|
|||
void_result sidechain_transaction_create_evaluator::do_evaluate(const sidechain_transaction_create_operation &op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer.");
|
||||
|
||||
FC_ASSERT((op.object_id.is<son_wallet_id_type>() || op.object_id.is<son_wallet_deposit_id_type>() || op.object_id.is<son_wallet_withdraw_id_type>()), "Invalid object id");
|
||||
|
||||
|
|
@ -28,15 +28,26 @@ void_result sidechain_transaction_create_evaluator::do_evaluate(const sidechain_
|
|||
object_id_type sidechain_transaction_create_evaluator::do_apply(const sidechain_transaction_create_operation &op)
|
||||
{ try {
|
||||
const auto &new_sidechain_transaction_object = db().create<sidechain_transaction_object>([&](sidechain_transaction_object &sto) {
|
||||
|
||||
sto.timestamp = db().head_block_time();
|
||||
sto.sidechain = op.sidechain;
|
||||
sto.object_id = op.object_id;
|
||||
sto.transaction = op.transaction;
|
||||
sto.signers = op.signers;
|
||||
std::transform(op.signers.begin(), op.signers.end(), std::inserter(sto.signatures, sto.signatures.end()), [](const son_info &si) {
|
||||
std::vector<son_sidechain_info> signers;
|
||||
for(const auto& signer : op.signers){
|
||||
son_sidechain_info ssi;
|
||||
ssi.son_id = signer.son_id;
|
||||
ssi.weight = signer.weight;
|
||||
ssi.signing_key = signer.signing_key;
|
||||
ssi.public_key = signer.sidechain_public_keys.at(op.sidechain);
|
||||
signers.emplace_back(std::move(ssi));
|
||||
}
|
||||
sto.signers = std::move(signers);
|
||||
|
||||
std::transform(sto.signers.begin(), sto.signers.end(), std::inserter(sto.signatures, sto.signatures.end()), [](const son_sidechain_info &si) {
|
||||
return std::make_pair(si.son_id, std::string());
|
||||
});
|
||||
for (const auto &si : op.signers) {
|
||||
for (const auto &si : sto.signers) {
|
||||
sto.total_weight = sto.total_weight + si.weight;
|
||||
}
|
||||
sto.sidechain_transaction = "";
|
||||
|
|
|
|||
|
|
@ -38,20 +38,29 @@ void_result create_son_evaluator::do_evaluate(const son_create_operation& op)
|
|||
|
||||
object_id_type create_son_evaluator::do_apply(const son_create_operation& op)
|
||||
{ try {
|
||||
vote_id_type vote_id_bitcoin;
|
||||
vote_id_type vote_id_hive;
|
||||
vote_id_type vote_id_ethereum;
|
||||
db().modify(db().get_global_properties(), [&vote_id_bitcoin, &vote_id_hive, &vote_id_ethereum](global_property_object& p) {
|
||||
vote_id_bitcoin = get_next_vote_id(p, vote_id_type::son_bitcoin);
|
||||
vote_id_hive = get_next_vote_id(p, vote_id_type::son_hive);
|
||||
vote_id_ethereum = get_next_vote_id(p, vote_id_type::son_ethereum);
|
||||
});
|
||||
vote_id_type vote_id;
|
||||
flat_map<sidechain_type, vote_id_type> vote_ids;
|
||||
|
||||
const auto& new_son_object = db().create<son_object>( [&]( son_object& obj ){
|
||||
const auto now = db().head_block_time();
|
||||
if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
|
||||
db().modify(db().get_global_properties(), [&vote_id](global_property_object &p) {
|
||||
vote_id = get_next_vote_id(p, vote_id_type::son_bitcoin);
|
||||
});
|
||||
}
|
||||
else {
|
||||
db().modify(db().get_global_properties(), [&vote_ids](global_property_object &p) {
|
||||
vote_ids[sidechain_type::bitcoin] = get_next_vote_id(p, vote_id_type::son_bitcoin);
|
||||
vote_ids[sidechain_type::hive] = get_next_vote_id(p, vote_id_type::son_hive);
|
||||
vote_ids[sidechain_type::ethereum] = get_next_vote_id(p, vote_id_type::son_ethereum);
|
||||
});
|
||||
}
|
||||
|
||||
const auto& new_son_object = db().create<son_object>( [&]( son_object& obj ) {
|
||||
obj.son_account = op.owner_account;
|
||||
obj.sidechain_vote_ids[sidechain_type::bitcoin] = vote_id_bitcoin;
|
||||
obj.sidechain_vote_ids[sidechain_type::hive] = vote_id_hive;
|
||||
obj.sidechain_vote_ids[sidechain_type::ethereum] = vote_id_ethereum;
|
||||
if( now < HARDFORK_SON_FOR_ETHEREUM_TIME )
|
||||
obj.sidechain_vote_ids[sidechain_type::bitcoin] = vote_id;
|
||||
else
|
||||
obj.sidechain_vote_ids = vote_ids;
|
||||
obj.url = op.url;
|
||||
obj.deposit = op.deposit;
|
||||
obj.signing_key = op.signing_key;
|
||||
|
|
@ -168,7 +177,7 @@ void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation&
|
|||
fc::time_point_sec min_ts = db().head_block_time() - fc::seconds(5 * db().block_interval());
|
||||
// Account for server ntp sync difference
|
||||
fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(5 * db().block_interval());
|
||||
for(const auto& active_sidechain_type : active_sidechain_types) {
|
||||
for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())) {
|
||||
if(stats.last_active_timestamp.contains(active_sidechain_type))
|
||||
FC_ASSERT(op.ts > stats.last_active_timestamp.at(active_sidechain_type), "Heartbeat sent for sidechain = ${sidechain} without waiting minimum time", ("sidechain", active_sidechain_type));
|
||||
if(stats.last_down_timestamp.contains(active_sidechain_type))
|
||||
|
|
@ -195,7 +204,7 @@ object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation&
|
|||
active_son_ids.reserve(active_sons.size());
|
||||
std::transform(active_sons.cbegin(), active_sons.cend(),
|
||||
std::inserter(active_son_ids, active_son_ids.end()),
|
||||
[](const son_info &swi) {
|
||||
[](const son_sidechain_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
|
|
@ -208,7 +217,7 @@ object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation&
|
|||
|
||||
if (itr->statuses.at(sidechain) == son_status::in_maintenance) {
|
||||
db().modify(itr->statistics(db()), [&](son_statistics_object &sso) {
|
||||
sso.current_interval_downtime[sidechain] += op.ts.sec_since_epoch() - sso.last_down_timestamp.at(sidechain).sec_since_epoch();
|
||||
sso.current_interval_downtime[sidechain] += op.ts.sec_since_epoch() - (sso.last_down_timestamp.contains(sidechain) ? sso.last_down_timestamp.at(sidechain).sec_since_epoch() : op.ts.sec_since_epoch());
|
||||
sso.last_active_timestamp[sidechain] = op.ts;
|
||||
});
|
||||
|
||||
|
|
@ -244,8 +253,9 @@ void_result son_report_down_evaluator::do_evaluate(const son_report_down_operati
|
|||
status_need_to_report_down = true;
|
||||
}
|
||||
FC_ASSERT(status_need_to_report_down, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down");
|
||||
for(const auto& active_sidechain_type : active_sidechain_types) {
|
||||
FC_ASSERT(op.down_ts >= stats.last_active_timestamp.at(active_sidechain_type), "sidechain = ${sidechain} down_ts should be greater than last_active_timestamp", ("sidechain", active_sidechain_type));
|
||||
for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())) {
|
||||
if(stats.last_active_timestamp.contains(active_sidechain_type))
|
||||
FC_ASSERT(op.down_ts >= stats.last_active_timestamp.at(active_sidechain_type), "sidechain = ${sidechain} down_ts should be greater than last_active_timestamp", ("sidechain", active_sidechain_type));
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
|
|||
|
|
@ -6,24 +6,22 @@ namespace graphene { namespace chain {
|
|||
db.adjust_balance(son_account, pay);
|
||||
}
|
||||
|
||||
bool son_object::has_valid_config()const {
|
||||
return ((std::string(signing_key).length() > 0) &&
|
||||
(sidechain_public_keys.size() > 0) &&
|
||||
(sidechain_public_keys.find( sidechain_type::bitcoin ) != sidechain_public_keys.end()) &&
|
||||
(sidechain_public_keys.at(sidechain_type::bitcoin).length() > 0));
|
||||
bool son_object::has_valid_config(sidechain_type sidechain) const {
|
||||
return (sidechain_public_keys.find( sidechain ) != sidechain_public_keys.end()) &&
|
||||
(sidechain_public_keys.at(sidechain).length() > 0);
|
||||
}
|
||||
|
||||
bool son_object::has_valid_config(time_point_sec head_block_time)const {
|
||||
bool retval = has_valid_config();
|
||||
bool son_object::has_valid_config(time_point_sec head_block_time, sidechain_type sidechain) const {
|
||||
bool retval = (std::string(signing_key).length() > 0) && (sidechain_public_keys.size() > 0);
|
||||
|
||||
if (head_block_time >= HARDFORK_SON_FOR_HIVE_TIME) {
|
||||
retval = retval &&
|
||||
(sidechain_public_keys.find( sidechain_type::hive ) != sidechain_public_keys.end()) &&
|
||||
(sidechain_public_keys.at(sidechain_type::hive).length() > 0);
|
||||
|
||||
retval = retval &&
|
||||
(sidechain_public_keys.find( sidechain_type::ethereum ) != sidechain_public_keys.end()) &&
|
||||
(sidechain_public_keys.at(sidechain_type::ethereum).length() > 0);
|
||||
if (head_block_time < HARDFORK_SON_FOR_HIVE_TIME) {
|
||||
retval = retval && has_valid_config(sidechain_type::bitcoin);
|
||||
}
|
||||
if (head_block_time >= HARDFORK_SON_FOR_HIVE_TIME && head_block_time < HARDFORK_SON_FOR_ETHEREUM_TIME) {
|
||||
retval = retval && has_valid_config(sidechain_type::bitcoin) && has_valid_config(sidechain_type::hive);
|
||||
}
|
||||
else if (head_block_time >= HARDFORK_SON_FOR_ETHEREUM_TIME) {
|
||||
retval = retval && has_valid_config(sidechain);
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ namespace graphene { namespace chain {
|
|||
|
||||
void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
const auto now = db().head_block_time();
|
||||
FC_ASSERT(now >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer.");
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto itr = idx.rbegin();
|
||||
|
|
@ -16,7 +17,23 @@ void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate
|
|||
{
|
||||
// Compare current wallet SONs and to-be lists of active sons
|
||||
auto cur_wallet_sons = (*itr).sons;
|
||||
auto new_wallet_sons = op.sons;
|
||||
flat_map<sidechain_type, vector<son_sidechain_info> > new_wallet_sons;
|
||||
if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
|
||||
for(const auto& son : op.sons){
|
||||
for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())){
|
||||
son_sidechain_info ssi;
|
||||
ssi.son_id = son.son_id;
|
||||
ssi.weight = son.weight;
|
||||
ssi.signing_key = son.signing_key;
|
||||
ssi.public_key = son.sidechain_public_keys.at(active_sidechain_type);
|
||||
new_wallet_sons[active_sidechain_type].emplace_back(std::move(ssi));
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
FC_ASSERT(op.extensions.value.sidechain_sons.valid(), "Sons is not valid");
|
||||
new_wallet_sons = *op.extensions.value.sidechain_sons;
|
||||
}
|
||||
|
||||
bool son_sets_equal = (cur_wallet_sons.size() == new_wallet_sons.size());
|
||||
if (son_sets_equal) {
|
||||
|
|
@ -51,9 +68,26 @@ object_id_type recreate_son_wallet_evaluator::do_apply(const son_wallet_recreate
|
|||
}
|
||||
|
||||
const auto& new_son_wallet_object = db().create<son_wallet_object>( [&]( son_wallet_object& obj ){
|
||||
obj.valid_from = db().head_block_time();
|
||||
const auto now = db().head_block_time();
|
||||
obj.valid_from = now;
|
||||
obj.expires = time_point_sec::maximum();
|
||||
obj.sons = op.sons;
|
||||
if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) {
|
||||
flat_map<sidechain_type, vector<son_sidechain_info> > sons;
|
||||
for(const auto& son : op.sons){
|
||||
for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())){
|
||||
son_sidechain_info ssi;
|
||||
ssi.son_id = son.son_id;
|
||||
ssi.weight = son.weight;
|
||||
ssi.signing_key = son.signing_key;
|
||||
ssi.public_key = son.sidechain_public_keys.at(active_sidechain_type);
|
||||
sons[active_sidechain_type].emplace_back(std::move(ssi));
|
||||
}
|
||||
}
|
||||
obj.sons = std::move(sons);
|
||||
}
|
||||
else{
|
||||
obj.sons = *op.extensions.value.sidechain_sons;
|
||||
}
|
||||
});
|
||||
return new_son_wallet_object.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
@ -63,8 +97,19 @@ void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_ope
|
|||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
|
||||
const son_wallet_id_type son_wallet_id = [&]{
|
||||
if(db().head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME)
|
||||
{
|
||||
const auto ast = active_sidechain_types(db().head_block_time());
|
||||
const auto id = (op.son_wallet_id.instance.value - std::distance(ast.begin(), ast.find(op.sidechain))) / ast.size();
|
||||
return son_wallet_id_type{ id };
|
||||
}
|
||||
|
||||
return op.son_wallet_id;
|
||||
}();
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.son_wallet_id) != idx.end() );
|
||||
FC_ASSERT( idx.find(son_wallet_id) != idx.end() );
|
||||
//auto itr = idx.find(op.son_wallet_id);
|
||||
//FC_ASSERT( itr->addresses.find(op.sidechain) == itr->addresses.end() ||
|
||||
// itr->addresses.at(op.sidechain).empty(), "Sidechain wallet address already set");
|
||||
|
|
@ -73,8 +118,19 @@ void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_ope
|
|||
|
||||
object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_operation& op)
|
||||
{ try {
|
||||
const son_wallet_id_type son_wallet_id = [&]{
|
||||
if(db().head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME)
|
||||
{
|
||||
const auto ast = active_sidechain_types(db().head_block_time());
|
||||
const auto id = (op.son_wallet_id.instance.value - std::distance(ast.begin(), ast.find(op.sidechain))) / ast.size();
|
||||
return son_wallet_id_type{ id };
|
||||
}
|
||||
|
||||
return op.son_wallet_id;
|
||||
}();
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_wallet_id);
|
||||
auto itr = idx.find(son_wallet_id);
|
||||
if (itr != idx.end())
|
||||
{
|
||||
if (itr->addresses.find(op.sidechain) == itr->addresses.end()) {
|
||||
|
|
|
|||
|
|
@ -10,12 +10,13 @@ namespace graphene { namespace chain {
|
|||
|
||||
void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_create_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
|
||||
const auto now = db().head_block_time();
|
||||
FC_ASSERT(now >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
const auto &son_idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
const auto so = son_idx.find(op.son_id);
|
||||
FC_ASSERT(so != son_idx.end(), "SON not found");
|
||||
FC_ASSERT(so->son_account == op.payer, "Payer is not SON account owner");
|
||||
FC_ASSERT(!(op.sidechain == sidechain_type::peerplays && now >= HARDFORK_SON_FOR_ETHEREUM_TIME), "Peerplays sidechain type is not allowed");
|
||||
|
||||
const auto &ss_idx = db().get_index_type<son_stats_index>().indices().get<by_owner>();
|
||||
FC_ASSERT(ss_idx.find(op.son_id) != ss_idx.end(), "Statistic object for a given SON ID does not exists");
|
||||
|
|
@ -23,9 +24,17 @@ void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_w
|
|||
const auto &swwo_idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_peerplays_uid>();
|
||||
const auto swwo = swwo_idx.find(op.peerplays_uid);
|
||||
if (swwo == swwo_idx.end()) {
|
||||
const sidechain_type sidechain = [&op]{
|
||||
if(op.sidechain == sidechain_type::peerplays){
|
||||
return op.withdraw_sidechain;
|
||||
}
|
||||
else
|
||||
return op.sidechain;
|
||||
}();
|
||||
|
||||
const auto &gpo = db().get_global_properties();
|
||||
bool expected = false;
|
||||
for (auto &si : gpo.active_sons.at(op.sidechain)) {
|
||||
for (auto &si : gpo.active_sons.at(sidechain)) {
|
||||
if (op.son_id == si.son_id) {
|
||||
expected = true;
|
||||
break;
|
||||
|
|
@ -76,8 +85,16 @@ object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_w
|
|||
swwo.withdraw_currency = op.withdraw_currency;
|
||||
swwo.withdraw_amount = op.withdraw_amount;
|
||||
|
||||
const sidechain_type sidechain = [&op]{
|
||||
if(op.sidechain == sidechain_type::peerplays){
|
||||
return op.withdraw_sidechain;
|
||||
}
|
||||
else
|
||||
return op.sidechain;
|
||||
}();
|
||||
|
||||
const auto &gpo = db().get_global_properties();
|
||||
for (auto &si : gpo.active_sons.at(op.sidechain)) {
|
||||
for (auto &si : gpo.active_sons.at(sidechain)) {
|
||||
swwo.expected_reports.insert(std::make_pair(si.son_id, si.weight));
|
||||
|
||||
auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(si.son_id);
|
||||
|
|
@ -138,13 +155,17 @@ object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_w
|
|||
|
||||
void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_process_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
const auto now = db().head_block_time();
|
||||
FC_ASSERT(now >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
|
||||
const auto& itr = idx.find(op.son_wallet_withdraw_id);
|
||||
FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found");
|
||||
FC_ASSERT(db().get_global_properties().active_sons.at(itr->sidechain).size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present");
|
||||
FC_ASSERT(!(itr->sidechain == sidechain_type::peerplays && now >= HARDFORK_SON_FOR_ETHEREUM_TIME), "Peerplays sidechain type is not allowed");
|
||||
if(itr->sidechain != sidechain_type::peerplays) {
|
||||
FC_ASSERT(db().get_global_properties().active_sons.at(itr->sidechain).size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present");
|
||||
}
|
||||
FC_ASSERT(!itr->processed, "Son wallet withdraw is already processed");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
4
libraries/plugins/peerplays_sidechain/CMakeLists.txt
Executable file → Normal file
4
libraries/plugins/peerplays_sidechain/CMakeLists.txt
Executable file → Normal file
|
|
@ -16,6 +16,8 @@ add_library( peerplays_sidechain
|
|||
bitcoin/segwit_addr.cpp
|
||||
bitcoin/utils.cpp
|
||||
bitcoin/sign_bitcoin_transaction.cpp
|
||||
bitcoin/libbitcoin_client.cpp
|
||||
bitcoin/estimate_fee_external.cpp
|
||||
common/rpc_client.cpp
|
||||
common/utils.cpp
|
||||
ethereum/encoders.cpp
|
||||
|
|
@ -42,7 +44,7 @@ endif()
|
|||
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS)
|
||||
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
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
network ntype) {
|
||||
network ntype, payment_type type) {
|
||||
network_type = ntype;
|
||||
this->type = type;
|
||||
create_redeem_script(keys_data);
|
||||
create_witness_script();
|
||||
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) {
|
||||
|
|
@ -278,26 +278,43 @@ void btc_weighted_multisig_address::create_witness_script() {
|
|||
script_builder builder;
|
||||
builder << op::_0;
|
||||
builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size());
|
||||
|
||||
witness_script_ = builder;
|
||||
}
|
||||
|
||||
void btc_weighted_multisig_address::create_segwit_address() {
|
||||
std::string hrp;
|
||||
address_types byte_version;
|
||||
switch (network_type) {
|
||||
case (network::mainnet):
|
||||
hrp = "bc";
|
||||
byte_version = address_types::MAINNET_SCRIPT;
|
||||
break;
|
||||
case (network::testnet):
|
||||
hrp = "tb";
|
||||
byte_version = address_types::TESTNET_SCRIPT;
|
||||
break;
|
||||
case (network::regtest):
|
||||
hrp = "bcrt";
|
||||
byte_version = address_types::TESTNET_SCRIPT;
|
||||
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());
|
||||
address = segwit_addr::encode(hrp, 0, hash_data);
|
||||
|
||||
if (type == payment_type::P2WSH) {
|
||||
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,
|
||||
|
|
@ -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,
|
||||
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;
|
||||
this->type = type;
|
||||
create_redeem_script(user_key_data, keys_data);
|
||||
create_witness_script();
|
||||
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) {
|
||||
|
|
@ -398,20 +415,39 @@ void btc_one_or_weighted_multisig_address::create_witness_script() {
|
|||
|
||||
void btc_one_or_weighted_multisig_address::create_segwit_address() {
|
||||
std::string hrp;
|
||||
address_types byte_version;
|
||||
switch (network_type) {
|
||||
case (network::mainnet):
|
||||
byte_version = address_types::MAINNET_SCRIPT;
|
||||
hrp = "bc";
|
||||
break;
|
||||
case (network::testnet):
|
||||
byte_version = address_types::TESTNET_SCRIPT;
|
||||
hrp = "tb";
|
||||
break;
|
||||
case (network::regtest):
|
||||
byte_version = address_types::TESTNET_SCRIPT;
|
||||
hrp = "bcrt";
|
||||
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());
|
||||
address = segwit_addr::encode(hrp, 0, hash_data);
|
||||
|
||||
if (type == payment_type::P2WSH) {
|
||||
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) :
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ void bitcoin_transaction_builder::add_in(payment_type type, tx_in txin, const by
|
|||
txin.scriptSig = script_code;
|
||||
break;
|
||||
default: {
|
||||
if (txin.prevout.hash == fc::sha256("0000000000000000000000000000000000000000000000000000000000000000")) { //coinbase
|
||||
if (txin.prevout.hash == fc::sha256("0000000000000000000000000000000000000000000000000000000000000000")) { // coinbase
|
||||
FC_ASSERT(script_code != bytes());
|
||||
txin.scriptSig = script_code;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -18,10 +18,40 @@
|
|||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
rpc_client::rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls) :
|
||||
url(_url),
|
||||
user(_user),
|
||||
password(_password),
|
||||
struct rpc_reply {
|
||||
uint16_t status;
|
||||
std::string body;
|
||||
};
|
||||
|
||||
class rpc_connection {
|
||||
public:
|
||||
rpc_connection(const rpc_credentials &_credentials, bool _debug_rpc_calls);
|
||||
|
||||
std::string send_post_request(std::string method, std::string params, bool show_log);
|
||||
std::string get_url() const;
|
||||
|
||||
protected:
|
||||
rpc_credentials credentials;
|
||||
bool debug_rpc_calls;
|
||||
|
||||
std::string protocol;
|
||||
std::string host;
|
||||
std::string port;
|
||||
std::string target;
|
||||
std::string authorization;
|
||||
|
||||
uint32_t request_id;
|
||||
|
||||
private:
|
||||
rpc_reply send_post_request(std::string body, bool show_log);
|
||||
|
||||
boost::beast::net::io_context ioc;
|
||||
boost::beast::net::ip::tcp::resolver resolver;
|
||||
boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp> results;
|
||||
};
|
||||
|
||||
rpc_connection::rpc_connection(const rpc_credentials &_credentials, bool _debug_rpc_calls) :
|
||||
credentials(_credentials),
|
||||
debug_rpc_calls(_debug_rpc_calls),
|
||||
request_id(0),
|
||||
resolver(ioc) {
|
||||
|
|
@ -31,7 +61,7 @@ rpc_client::rpc_client(std::string _url, std::string _user, std::string _passwor
|
|||
|
||||
boost::xpressive::smatch sm;
|
||||
|
||||
if (boost::xpressive::regex_search(url, sm, sr)) {
|
||||
if (boost::xpressive::regex_search(credentials.url, sm, sr)) {
|
||||
protocol = sm["Protocol"];
|
||||
if (protocol.empty()) {
|
||||
protocol = "http";
|
||||
|
|
@ -52,15 +82,19 @@ rpc_client::rpc_client(std::string _url, std::string _user, std::string _passwor
|
|||
target = "/";
|
||||
}
|
||||
|
||||
authorization = "Basic " + base64_encode(user + ":" + password);
|
||||
authorization = "Basic " + base64_encode(credentials.user + ":" + credentials.password);
|
||||
|
||||
results = resolver.resolve(host, port);
|
||||
|
||||
} else {
|
||||
elog("Invalid URL: ${url}", ("url", url));
|
||||
elog("Invalid URL: ${url}", ("url", credentials.url));
|
||||
}
|
||||
}
|
||||
|
||||
std::string rpc_connection::get_url() const {
|
||||
return credentials.url;
|
||||
}
|
||||
|
||||
std::string rpc_client::retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx) {
|
||||
if (reply_str.empty()) {
|
||||
wlog("RPC call ${function}, empty reply string", ("function", __FUNCTION__));
|
||||
|
|
@ -125,7 +159,7 @@ std::string rpc_client::retrieve_value_from_reply(std::string reply_str, std::st
|
|||
return "";
|
||||
}
|
||||
|
||||
std::string rpc_client::send_post_request(std::string method, std::string params, bool show_log) {
|
||||
std::string rpc_connection::send_post_request(std::string method, std::string params, bool show_log) {
|
||||
std::stringstream body;
|
||||
|
||||
request_id = request_id + 1;
|
||||
|
|
@ -164,7 +198,7 @@ std::string rpc_client::send_post_request(std::string method, std::string params
|
|||
return "";
|
||||
}
|
||||
|
||||
rpc_reply rpc_client::send_post_request(std::string body, bool show_log) {
|
||||
rpc_reply rpc_connection::send_post_request(std::string body, bool show_log) {
|
||||
|
||||
// These object is used as a context for ssl connection
|
||||
boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12_client);
|
||||
|
|
@ -239,7 +273,7 @@ rpc_reply rpc_client::send_post_request(std::string body, bool show_log) {
|
|||
reply.body = rbody;
|
||||
|
||||
if (show_log) {
|
||||
ilog("### Request URL: ${url}", ("url", url));
|
||||
ilog("### Request URL: ${url}", ("url", credentials.url));
|
||||
ilog("### Request: ${body}", ("body", body));
|
||||
ilog("### Response: ${rbody}", ("rbody", rbody));
|
||||
}
|
||||
|
|
@ -247,4 +281,113 @@ rpc_reply rpc_client::send_post_request(std::string body, bool show_log) {
|
|||
return reply;
|
||||
}
|
||||
|
||||
rpc_client::rpc_client(sidechain_type _sidechain, const std::vector<rpc_credentials> &_credentials, bool _debug_rpc_calls, bool _simulate_connection_reselection) :
|
||||
sidechain(_sidechain),
|
||||
debug_rpc_calls(_debug_rpc_calls),
|
||||
simulate_connection_reselection(_simulate_connection_reselection) {
|
||||
FC_ASSERT(_credentials.size());
|
||||
for (size_t i = 0; i < _credentials.size(); i++)
|
||||
connections.push_back(new rpc_connection(_credentials[i], _debug_rpc_calls));
|
||||
n_active_conn = 0;
|
||||
if (connections.size() > 1)
|
||||
schedule_connection_selection();
|
||||
}
|
||||
|
||||
void rpc_client::schedule_connection_selection() {
|
||||
fc::time_point now = fc::time_point::now();
|
||||
static const int64_t time_to_next_conn_selection = 10 * 1000 * 1000; // 10 sec
|
||||
fc::time_point next_wakeup = now + fc::microseconds(time_to_next_conn_selection);
|
||||
connection_selection_task = fc::schedule([this] {
|
||||
select_connection();
|
||||
},
|
||||
next_wakeup, "SON RPC connection selection");
|
||||
}
|
||||
|
||||
void rpc_client::select_connection() {
|
||||
FC_ASSERT(connections.size() > 1);
|
||||
|
||||
const std::lock_guard<std::mutex> lock(conn_mutex);
|
||||
|
||||
static const int t_limit = 5 * 1000 * 1000, // 5 sec
|
||||
quality_diff_threshold = 10 * 1000; // 10 ms
|
||||
|
||||
int best_n = -1;
|
||||
int best_quality = -1;
|
||||
|
||||
std::vector<uint64_t> head_block_numbers;
|
||||
head_block_numbers.resize(connections.size());
|
||||
|
||||
std::vector<int> qualities;
|
||||
qualities.resize(connections.size());
|
||||
|
||||
for (size_t n = 0; n < connections.size(); n++) {
|
||||
rpc_connection &conn = *connections[n];
|
||||
int quality = 0;
|
||||
head_block_numbers[n] = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
// ping n'th node
|
||||
if (debug_rpc_calls)
|
||||
ilog("### Ping ${sidechain} node #${n}, ${url}", ("sidechain", fc::reflector<sidechain_type>::to_string(sidechain))("n", n)("url", conn.get_url()));
|
||||
fc::time_point t_sent = fc::time_point::now();
|
||||
uint64_t head_block_number = ping(conn);
|
||||
fc::time_point t_received = fc::time_point::now();
|
||||
int t = (t_received - t_sent).count();
|
||||
|
||||
// evaluate n'th node reply quality and switch to it if it's better
|
||||
if (head_block_number != std::numeric_limits<uint64_t>::max()) {
|
||||
if (simulate_connection_reselection)
|
||||
t += rand() % 10;
|
||||
FC_ASSERT(t != -1);
|
||||
head_block_numbers[n] = head_block_number;
|
||||
if (t < t_limit)
|
||||
quality = t_limit - t; // the less time, the higher quality
|
||||
|
||||
// look for the best quality
|
||||
if (quality > best_quality) {
|
||||
best_n = n;
|
||||
best_quality = quality;
|
||||
}
|
||||
}
|
||||
qualities[n] = quality;
|
||||
}
|
||||
|
||||
FC_ASSERT(best_n != -1 && best_quality != -1);
|
||||
if (best_n != n_active_conn) { // if the best client is not the current one, ...
|
||||
uint64_t active_head_block_number = head_block_numbers[n_active_conn];
|
||||
if ((active_head_block_number == std::numeric_limits<uint64_t>::max() // ...and the current one has no known head block...
|
||||
|| head_block_numbers[best_n] >= active_head_block_number) // ...or the best client's head is more recent than the current, ...
|
||||
&& best_quality > qualities[n_active_conn] + quality_diff_threshold) { // ...and the new client's quality exceeds current more than by threshold
|
||||
n_active_conn = best_n; // ...then select new one
|
||||
if (debug_rpc_calls)
|
||||
ilog("### Reselected ${sidechain} node to #${n}, ${url}", ("sidechain", fc::reflector<sidechain_type>::to_string(sidechain))("n", n_active_conn)("url", connections[n_active_conn]->get_url()));
|
||||
}
|
||||
}
|
||||
|
||||
schedule_connection_selection();
|
||||
}
|
||||
|
||||
rpc_connection &rpc_client::get_active_connection() const {
|
||||
return *connections[n_active_conn];
|
||||
}
|
||||
|
||||
std::string rpc_client::send_post_request(std::string method, std::string params, bool show_log) {
|
||||
const std::lock_guard<std::mutex> lock(conn_mutex);
|
||||
return send_post_request(get_active_connection(), method, params, show_log);
|
||||
}
|
||||
|
||||
std::string rpc_client::send_post_request(rpc_connection &conn, std::string method, std::string params, bool show_log) {
|
||||
return conn.send_post_request(method, params, show_log);
|
||||
}
|
||||
|
||||
rpc_client::~rpc_client() {
|
||||
try {
|
||||
if (connection_selection_task.valid())
|
||||
connection_selection_task.cancel_and_wait(__FUNCTION__);
|
||||
} catch (fc::canceled_exception &) {
|
||||
// Expected exception. Move along.
|
||||
} catch (fc::exception &e) {
|
||||
edump((e.to_detail_string()));
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include <graphene/peerplays_sidechain/common/utils.hpp>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/archive/iterators/base64_from_binary.hpp>
|
||||
#include <boost/archive/iterators/binary_from_base64.hpp>
|
||||
#include <boost/archive/iterators/transform_width.hpp>
|
||||
|
|
@ -47,4 +48,17 @@ std::string object_id_to_string(graphene::chain::object_id_type id) {
|
|||
return object_id;
|
||||
}
|
||||
|
||||
graphene::chain::object_id_type string_to_object_id(const std::string &id) {
|
||||
std::vector<std::string> strs;
|
||||
boost::split(strs, id, boost::is_any_of("."));
|
||||
if (strs.size() != 3) {
|
||||
elog("Wrong object_id format: ${id}", ("id", id));
|
||||
return graphene::chain::object_id_type{};
|
||||
}
|
||||
|
||||
auto s = boost::lexical_cast<int>(strs.at(0));
|
||||
auto t = boost::lexical_cast<int>(strs.at(1));
|
||||
return graphene::chain::object_id_type{(uint8_t)s, (uint8_t)t, boost::lexical_cast<uint64_t>(strs.at(2))};
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
|
|||
|
|
@ -5,8 +5,51 @@
|
|||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace ethereum {
|
||||
|
||||
//! rlp_decoder
|
||||
//! base_decoder
|
||||
boost::multiprecision::uint256_t base_decoder::decode_uint256(const std::string &value) {
|
||||
boost::multiprecision::uint256_t result = 0;
|
||||
|
||||
boost::multiprecision::uint256_t power(1);
|
||||
uint8_t digit;
|
||||
int pos = value.size() - 1;
|
||||
while (pos >= 0) {
|
||||
digit = 0;
|
||||
if ('0' <= value[pos] && value[pos] <= '9') {
|
||||
digit = value[pos] - '0';
|
||||
} else if ('a' <= value[pos] && value[pos] <= 'z') {
|
||||
digit = value[pos] - 'a' + 10;
|
||||
}
|
||||
result += digit * power;
|
||||
pos--;
|
||||
power *= 16;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string base_decoder::decode_address(const std::string &value) {
|
||||
return value.substr(24, 40);
|
||||
}
|
||||
|
||||
//! deposit_erc20_decoder
|
||||
const std::string deposit_erc20_decoder::function_signature = "97feb926"; //! depositERC20(address,uint256)
|
||||
fc::optional<deposit_erc20_transaction> deposit_erc20_decoder::decode(const std::string &input) {
|
||||
const auto input_without_0x = remove_0x(input);
|
||||
if (function_signature != input_without_0x.substr(0, 8)) {
|
||||
return fc::optional<deposit_erc20_transaction>{};
|
||||
}
|
||||
if (input_without_0x.size() != 136) {
|
||||
return fc::optional<deposit_erc20_transaction>{};
|
||||
}
|
||||
|
||||
deposit_erc20_transaction erc_20;
|
||||
erc_20.token = add_0x(base_decoder::decode_address(input_without_0x.substr(8, 64)));
|
||||
erc_20.amount = base_decoder::decode_uint256(input_without_0x.substr(72, 64));
|
||||
|
||||
return erc_20;
|
||||
}
|
||||
|
||||
//! rlp_decoder
|
||||
namespace {
|
||||
const signed char p_util_hexdigit[256] =
|
||||
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include <boost/algorithm/hex.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <graphene/peerplays_sidechain/ethereum/utils.hpp>
|
||||
|
||||
|
|
@ -19,13 +18,17 @@ std::string base_encoder::encode_address(const std::string &value) {
|
|||
|
||||
std::string base_encoder::encode_string(const std::string &value) {
|
||||
std::string data = (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), value.size())).str();
|
||||
data += boost::algorithm::hex(value) + std::string((64 - value.size() * 2 % 64), '0');
|
||||
data += boost::algorithm::hex(value);
|
||||
if (value.size() % 32 != 0) {
|
||||
data += std::string((64 - value.size() * 2 % 64), '0');
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
//! update_owners_encoder
|
||||
std::string update_owners_encoder::encode(const std::vector<std::pair<std::string, uint16_t>> &owners_weights, const std::string &object_id) const {
|
||||
std::string data = "0x" + function_signature;
|
||||
const std::string update_owners_encoder::function_signature = "23ab6adf"; //! updateOwners((address,uint256)[],string)
|
||||
std::string update_owners_encoder::encode(const std::vector<std::pair<std::string, uint16_t>> &owners_weights, const std::string &object_id) {
|
||||
std::string data = add_0x(function_signature);
|
||||
data += base_encoder::encode_uint256(64);
|
||||
data += base_encoder::encode_uint256((owners_weights.size() * 2 + 3) * 32);
|
||||
data += base_encoder::encode_uint256(owners_weights.size());
|
||||
|
|
@ -39,8 +42,9 @@ std::string update_owners_encoder::encode(const std::vector<std::pair<std::strin
|
|||
}
|
||||
|
||||
//! withdrawal_encoder
|
||||
std::string withdrawal_encoder::encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id) const {
|
||||
std::string data = "0x" + function_signature;
|
||||
const std::string withdrawal_encoder::function_signature = "e088747b"; //! withdraw(address,uint256,string)
|
||||
std::string withdrawal_encoder::encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id) {
|
||||
std::string data = add_0x(function_signature);
|
||||
data += base_encoder::encode_address(to);
|
||||
data += base_encoder::encode_uint256(amount);
|
||||
data += base_encoder::encode_uint256(32 * 3);
|
||||
|
|
@ -49,6 +53,70 @@ std::string withdrawal_encoder::encode(const std::string &to, boost::multiprecis
|
|||
return data;
|
||||
}
|
||||
|
||||
//! withdrawal_erc20_encoder
|
||||
const std::string withdrawal_erc20_encoder::function_signature = "483c0467"; //! withdrawERC20(address,address,uint256,string)
|
||||
std::string withdrawal_erc20_encoder::encode(const std::string &token, const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id) {
|
||||
std::string data = add_0x(function_signature);
|
||||
data += base_encoder::encode_address(token);
|
||||
data += base_encoder::encode_address(to);
|
||||
data += base_encoder::encode_uint256(amount);
|
||||
data += base_encoder::encode_uint256(32 * 4);
|
||||
data += base_encoder::encode_string(object_id);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
//! signature_encoder
|
||||
const std::string update_owners_function_signature = "9d608673"; //! updateOwners((bytes,(uint8,bytes32,bytes32))[])
|
||||
const std::string withdrawal_function_signature = "daac6c81"; //! withdraw((bytes,(uint8,bytes32,bytes32))[])
|
||||
const std::string withdrawal_erc20_function_signature = "d2bf2866"; //! withdrawERC20((bytes,(uint8,bytes32,bytes32))[])
|
||||
signature_encoder::signature_encoder(const std::string &function_hash) :
|
||||
function_signature{function_hash} {
|
||||
}
|
||||
|
||||
std::string signature_encoder::get_function_signature_from_transaction(const std::string &transaction) {
|
||||
const std::string tr = remove_0x(transaction);
|
||||
if (tr.substr(0, 8) == update_owners_encoder::function_signature)
|
||||
return update_owners_function_signature;
|
||||
|
||||
if (tr.substr(0, 8) == withdrawal_encoder::function_signature)
|
||||
return withdrawal_function_signature;
|
||||
|
||||
if (tr.substr(0, 8) == withdrawal_erc20_encoder::function_signature)
|
||||
return withdrawal_erc20_function_signature;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string signature_encoder::encode(const std::vector<encoded_sign_transaction> &transactions) const {
|
||||
std::string data = add_0x(function_signature);
|
||||
data += base_encoder::encode_uint256(32);
|
||||
data += base_encoder::encode_uint256(transactions.size());
|
||||
size_t offset = (transactions.size()) * 32;
|
||||
for (const auto &transaction : transactions) {
|
||||
data += base_encoder::encode_uint256(offset);
|
||||
const auto transaction_data = remove_0x(transaction.data);
|
||||
offset += 5 * 32 + transaction_data.size() / 2;
|
||||
if (transaction_data.size() / 2 % 32 != 0) {
|
||||
offset += 32 - transaction_data.size() / 2 % 32;
|
||||
}
|
||||
}
|
||||
for (const auto &transaction : transactions) {
|
||||
data += base_encoder::encode_uint256(4 * 32);
|
||||
data += base_encoder::encode_address(transaction.sign.v);
|
||||
data += base_encoder::encode_address(transaction.sign.r);
|
||||
data += base_encoder::encode_address(transaction.sign.s);
|
||||
const auto transaction_data = remove_0x(transaction.data);
|
||||
data += base_encoder::encode_uint256(transaction_data.size() / 2);
|
||||
data += transaction_data;
|
||||
if (transaction_data.size() % 64 != 0) {
|
||||
data += std::string((64 - transaction_data.size() % 64), '0');
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
//! rlp_encoder
|
||||
std::string rlp_encoder::encode(const std::string &s) {
|
||||
return encode_rlp(hex2bytes(s));
|
||||
|
|
@ -69,8 +137,9 @@ std::string rlp_encoder::encode_length(int len, int offset) {
|
|||
|
||||
std::string rlp_encoder::hex2bytes(const std::string &s) {
|
||||
std::string dest;
|
||||
dest.resize(s.size() / 2);
|
||||
hex2bin(s.c_str(), &dest[0]);
|
||||
const auto s_final = s.size() % 2 == 0 ? s : "0" + s;
|
||||
dest.resize(s_final.size() / 2);
|
||||
hex2bin(s_final.c_str(), &dest[0]);
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,43 @@ const secp256k1_context *eth_context() {
|
|||
return ctx;
|
||||
}
|
||||
|
||||
//! transaction
|
||||
bytes keccak_hash(const std::string &data) {
|
||||
bytes hash;
|
||||
hash.resize(32);
|
||||
const auto transaction_string = boost::algorithm::unhex(remove_0x(data));
|
||||
keccak_256((const unsigned char *)transaction_string.data(), transaction_string.size(), (unsigned char *)hash.data());
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
signature sign_hash(const bytes &hash, const std::string &chain_id, const std::string &private_key) {
|
||||
const bytes priv_key = parse_hex(private_key);
|
||||
|
||||
int recid = 0;
|
||||
secp256k1_ecdsa_recoverable_signature sig;
|
||||
FC_ASSERT(secp256k1_ecdsa_sign_recoverable(eth_context(), &sig, (const unsigned char *)hash.data(), (const unsigned char *)priv_key.data(), NULL, NULL));
|
||||
fc::ecc::compact_signature result;
|
||||
FC_ASSERT(secp256k1_ecdsa_recoverable_signature_serialize_compact(eth_context(), (unsigned char *)result.begin() + 1, &recid, &sig));
|
||||
|
||||
unsigned int v = recid + from_hex<unsigned int>(chain_id) * 2 + 35;
|
||||
|
||||
bytes r;
|
||||
for (int i = 1; i < 33; i++)
|
||||
r.emplace_back((char)result.at(i));
|
||||
|
||||
bytes s;
|
||||
for (int i = 33; i < 65; i++)
|
||||
s.emplace_back((char)result.at(i));
|
||||
|
||||
signature eth_sig;
|
||||
eth_sig.v = to_hex(v);
|
||||
eth_sig.r = fc::to_hex((char *)&r[0], r.size());
|
||||
eth_sig.s = fc::to_hex((char *)&s[0], s.size());
|
||||
|
||||
return eth_sig;
|
||||
}
|
||||
|
||||
//! base_transaction
|
||||
|
||||
base_transaction::base_transaction(const std::string &raw_tx) {
|
||||
}
|
||||
|
|
@ -70,12 +106,7 @@ raw_transaction::raw_transaction(const std::string &raw_tx) :
|
|||
}
|
||||
|
||||
bytes raw_transaction::hash() const {
|
||||
bytes hash;
|
||||
hash.resize(32);
|
||||
const auto transaction_string = boost::algorithm::unhex(remove_0x(serialize()));
|
||||
keccak_256((const unsigned char *)transaction_string.data(), transaction_string.size(), (unsigned char *)hash.data());
|
||||
|
||||
return hash;
|
||||
return keccak_hash(serialize());
|
||||
}
|
||||
|
||||
signed_transaction raw_transaction::sign(const std::string &private_key) const {
|
||||
|
|
@ -88,27 +119,10 @@ signed_transaction raw_transaction::sign(const std::string &private_key) const {
|
|||
tr.value = value;
|
||||
tr.data = data;
|
||||
|
||||
const bytes priv_key = parse_hex(private_key);
|
||||
|
||||
int recid = 0;
|
||||
secp256k1_ecdsa_recoverable_signature sig;
|
||||
FC_ASSERT(secp256k1_ecdsa_sign_recoverable(eth_context(), &sig, (const unsigned char *)hash().data(), (const unsigned char *)priv_key.data(), NULL, NULL));
|
||||
fc::ecc::compact_signature result;
|
||||
FC_ASSERT(secp256k1_ecdsa_recoverable_signature_serialize_compact(eth_context(), (unsigned char *)result.begin() + 1, &recid, &sig));
|
||||
|
||||
bytes r;
|
||||
for (int i = 1; i < 33; i++)
|
||||
r.emplace_back((char)result.at(i));
|
||||
|
||||
unsigned int v = recid + from_hex<unsigned int>(chain_id) * 2 + 35;
|
||||
|
||||
bytes s;
|
||||
for (int i = 33; i < 65; i++)
|
||||
s.emplace_back((char)result.at(i));
|
||||
|
||||
tr.r = fc::to_hex((char *)&r[0], r.size());
|
||||
tr.v = to_hex(v);
|
||||
tr.s = fc::to_hex((char *)&s[0], s.size());
|
||||
const auto sig = sign_hash(hash(), chain_id, private_key);
|
||||
tr.v = sig.v;
|
||||
tr.r = sig.r;
|
||||
tr.s = sig.s;
|
||||
|
||||
return tr;
|
||||
}
|
||||
|
|
@ -196,8 +210,8 @@ std::string signed_transaction::serialize() const {
|
|||
rlp_encoder::encode(remove_0x(value)) +
|
||||
rlp_encoder::encode(remove_0x(data)) +
|
||||
rlp_encoder::encode(remove_0x(v)) +
|
||||
rlp_encoder::encode(remove_0x(r)) +
|
||||
rlp_encoder::encode(remove_0x(s));
|
||||
rlp_encoder::encode(remove_leading_00(remove_0x(r))) +
|
||||
rlp_encoder::encode(remove_leading_00(remove_0x(s)));
|
||||
|
||||
return add_0x(bytes2hex(rlp_encoder::encode_length(serialized.size(), 192) + serialized));
|
||||
}
|
||||
|
|
@ -220,9 +234,9 @@ void signed_transaction::deserialize(const std::string &raw_tx) {
|
|||
boost::algorithm::to_lower(data);
|
||||
v = add_0x(rlp_array.at(6));
|
||||
boost::algorithm::to_lower(v);
|
||||
r = add_0x(rlp_array.at(7));
|
||||
r = add_0x(add_leading_00(rlp_array.at(7)));
|
||||
boost::algorithm::to_lower(r);
|
||||
s = add_0x(rlp_array.at(8));
|
||||
s = add_0x(add_leading_00(rlp_array.at(8)));
|
||||
boost::algorithm::to_lower(s);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,36 @@
|
|||
#include <graphene/peerplays_sidechain/ethereum/types.hpp>
|
||||
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace ethereum {
|
||||
|
||||
signature::signature(const std::string &sign) {
|
||||
deserialize(sign);
|
||||
}
|
||||
|
||||
std::string signature::serialize() const {
|
||||
boost::property_tree::ptree pt;
|
||||
pt.put("v", v);
|
||||
pt.put("r", r);
|
||||
pt.put("s", s);
|
||||
|
||||
std::stringstream ss;
|
||||
boost::property_tree::json_parser::write_json(ss, pt);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void signature::deserialize(const std::string &raw_tx) {
|
||||
std::stringstream ss_tx(raw_tx);
|
||||
boost::property_tree::ptree tx_json;
|
||||
boost::property_tree::read_json(ss_tx, tx_json);
|
||||
|
||||
if (tx_json.count("v"))
|
||||
v = tx_json.get<std::string>("v");
|
||||
if (tx_json.count("r"))
|
||||
r = tx_json.get<std::string>("r");
|
||||
if (tx_json.count("s"))
|
||||
s = tx_json.get<std::string>("s");
|
||||
}
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::ethereum
|
||||
|
|
|
|||
|
|
@ -49,4 +49,24 @@ std::string remove_0x(const std::string &s) {
|
|||
return s;
|
||||
}
|
||||
|
||||
std::string add_leading_00(const std::string &s) {
|
||||
std::string result = s;
|
||||
|
||||
while (result.size() < 64) {
|
||||
result = "00" + result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string remove_leading_00(const std::string &s) {
|
||||
std::string result = s;
|
||||
|
||||
while (result.size() > 1 && result.substr(0, 2) == "00") {
|
||||
result = result.substr(2);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::ethereum
|
||||
|
|
|
|||
|
|
@ -8,6 +8,14 @@ using namespace graphene::chain;
|
|||
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
|
||||
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 {
|
||||
|
||||
|
|
@ -96,9 +104,6 @@ private:
|
|||
void create_address();
|
||||
|
||||
public:
|
||||
enum address_types { MAINNET_SCRIPT = 5,
|
||||
TESTNET_SCRIPT = 196 };
|
||||
|
||||
enum { OP_0 = 0x00,
|
||||
OP_EQUAL = 0x87,
|
||||
OP_HASH160 = 0xa9,
|
||||
|
|
@ -145,7 +150,7 @@ public:
|
|||
btc_weighted_multisig_address() = default;
|
||||
|
||||
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 {
|
||||
return redeem_script_;
|
||||
|
|
@ -190,7 +195,7 @@ class btc_one_or_weighted_multisig_address : public bitcoin_address {
|
|||
public:
|
||||
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,
|
||||
network network_type = network::regtest);
|
||||
network network_type = network::regtest, payment_type type = payment_type::P2SH_WSH);
|
||||
bytes get_redeem_script() const {
|
||||
return redeem_script_;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
typedef std::function<uint64_t()> get_fee_func_type;
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class estimate_fee_external {
|
||||
public:
|
||||
estimate_fee_external();
|
||||
~estimate_fee_external();
|
||||
std::vector<std::pair<std::string, uint64_t>> get_fee_external(uint16_t target_block);
|
||||
|
||||
private:
|
||||
std::string get_response(std::string url);
|
||||
// Here add your custom parser for external url. Take care of incremental name
|
||||
// and populate the list of url_parsers bellow paired with the function
|
||||
uint64_t parse_and_get_fee_1();
|
||||
uint64_t parse_and_get_fee_2();
|
||||
uint64_t parse_and_get_fee_3();
|
||||
|
||||
const std::map<std::string, get_fee_func_type> url_get_fee_parsers{
|
||||
{"https://www.bitgo.com/api/v2/btc/tx/fee", std::bind(&estimate_fee_external::parse_and_get_fee_1, this)},
|
||||
{"https://bitcoiner.live/api/fees/estimates/latest", std::bind(&estimate_fee_external::parse_and_get_fee_2, this)},
|
||||
{"https://api.blockchain.info/mempool/fees", std::bind(&estimate_fee_external::parse_and_get_fee_3, this)}};
|
||||
|
||||
std::string response;
|
||||
uint16_t target_block;
|
||||
CURL *curl{nullptr};
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -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
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <fc/crypto/elliptic.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/types.hpp>
|
||||
|
|
|
|||
|
|
@ -3,44 +3,52 @@
|
|||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <fc/thread/future.hpp>
|
||||
#include <fc/thread/thread.hpp>
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/beast/core.hpp>
|
||||
|
||||
#include <graphene/peerplays_sidechain/defs.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
struct rpc_reply {
|
||||
uint16_t status;
|
||||
std::string body;
|
||||
class rpc_connection;
|
||||
|
||||
struct rpc_credentials {
|
||||
std::string url;
|
||||
std::string user;
|
||||
std::string password;
|
||||
};
|
||||
|
||||
class rpc_client {
|
||||
public:
|
||||
rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls);
|
||||
const sidechain_type sidechain;
|
||||
|
||||
rpc_client(sidechain_type _sidechain, const std::vector<rpc_credentials> &_credentials, bool _debug_rpc_calls, bool _simulate_connection_reselection);
|
||||
~rpc_client();
|
||||
|
||||
protected:
|
||||
std::string retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx);
|
||||
std::string retrieve_value_from_reply(std::string reply_str, std::string value_path);
|
||||
bool debug_rpc_calls;
|
||||
bool simulate_connection_reselection;
|
||||
std::string send_post_request(std::string method, std::string params, bool show_log);
|
||||
|
||||
std::string url;
|
||||
std::string user;
|
||||
std::string password;
|
||||
bool debug_rpc_calls;
|
||||
static std::string send_post_request(rpc_connection &conn, std::string method, std::string params, bool show_log);
|
||||
|
||||
std::string protocol;
|
||||
std::string host;
|
||||
std::string port;
|
||||
std::string target;
|
||||
std::string authorization;
|
||||
|
||||
uint32_t request_id;
|
||||
static std::string retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx);
|
||||
static std::string retrieve_value_from_reply(std::string reply_str, std::string value_path);
|
||||
|
||||
private:
|
||||
rpc_reply send_post_request(std::string body, bool show_log);
|
||||
std::vector<rpc_connection *> connections;
|
||||
int n_active_conn;
|
||||
fc::future<void> connection_selection_task;
|
||||
std::mutex conn_mutex;
|
||||
|
||||
boost::beast::net::io_context ioc;
|
||||
boost::beast::net::ip::tcp::resolver resolver;
|
||||
boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp> results;
|
||||
rpc_connection &get_active_connection() const;
|
||||
|
||||
void select_connection();
|
||||
void schedule_connection_selection();
|
||||
virtual uint64_t ping(rpc_connection &conn) const = 0;
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
|
|||
|
|
@ -8,5 +8,6 @@ std::string base64_encode(const std::string &s);
|
|||
std::string base64_decode(const std::string &s);
|
||||
|
||||
std::string object_id_to_string(graphene::chain::object_id_type id);
|
||||
graphene::chain::object_id_type string_to_object_id(const std::string &id);
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
|
|||
|
|
@ -57,10 +57,16 @@ struct info_for_vin {
|
|||
bool resend = false;
|
||||
};
|
||||
|
||||
enum class sidechain_event_type {
|
||||
deposit,
|
||||
withdrawal
|
||||
};
|
||||
|
||||
struct sidechain_event_data {
|
||||
fc::time_point_sec timestamp;
|
||||
uint32_t block_num;
|
||||
sidechain_type sidechain;
|
||||
sidechain_event_type type;
|
||||
std::string sidechain_uid;
|
||||
std::string sidechain_transaction_id;
|
||||
std::string sidechain_from;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <fc/optional.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace ethereum {
|
||||
|
||||
class base_decoder {
|
||||
public:
|
||||
static boost::multiprecision::uint256_t decode_uint256(const std::string &value);
|
||||
static std::string decode_address(const std::string &value);
|
||||
};
|
||||
|
||||
struct deposit_erc20_transaction {
|
||||
std::string token;
|
||||
boost::multiprecision::uint256_t amount;
|
||||
};
|
||||
|
||||
class deposit_erc20_decoder {
|
||||
public:
|
||||
static const std::string function_signature;
|
||||
|
||||
static fc::optional<deposit_erc20_transaction> decode(const std::string &input);
|
||||
};
|
||||
|
||||
class rlp_decoder {
|
||||
private:
|
||||
enum RLP_constants {
|
||||
|
|
|
|||
|
|
@ -4,8 +4,15 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <graphene/peerplays_sidechain/ethereum/types.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace ethereum {
|
||||
|
||||
struct encoded_sign_transaction {
|
||||
std::string data;
|
||||
signature sign;
|
||||
};
|
||||
|
||||
class base_encoder {
|
||||
public:
|
||||
static std::string encode_uint256(boost::multiprecision::uint256_t value);
|
||||
|
|
@ -15,16 +22,34 @@ public:
|
|||
|
||||
class update_owners_encoder {
|
||||
public:
|
||||
const std::string function_signature = "23ab6adf"; //! updateOwners((address,uint256)[],string)
|
||||
static const std::string function_signature;
|
||||
|
||||
std::string encode(const std::vector<std::pair<std::string, uint16_t>> &owners_weights, const std::string &object_id) const;
|
||||
static std::string encode(const std::vector<std::pair<std::string, uint16_t>> &owners_weights, const std::string &object_id);
|
||||
};
|
||||
|
||||
class withdrawal_encoder {
|
||||
public:
|
||||
const std::string function_signature = "e088747b"; //! withdraw(address,uint256,string)
|
||||
static const std::string function_signature;
|
||||
|
||||
std::string encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id) const;
|
||||
static std::string encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id);
|
||||
};
|
||||
|
||||
class withdrawal_erc20_encoder {
|
||||
public:
|
||||
static const std::string function_signature;
|
||||
|
||||
static std::string encode(const std::string &token, const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id);
|
||||
};
|
||||
|
||||
class signature_encoder {
|
||||
public:
|
||||
const std::string function_signature;
|
||||
|
||||
signature_encoder(const std::string &function_hash);
|
||||
|
||||
static std::string get_function_signature_from_transaction(const std::string &transaction);
|
||||
|
||||
std::string encode(const std::vector<encoded_sign_transaction> &transactions) const;
|
||||
};
|
||||
|
||||
class rlp_encoder {
|
||||
|
|
@ -39,35 +64,4 @@ private:
|
|||
static void hex2bin(const char *src, char *target);
|
||||
};
|
||||
|
||||
/*class ethereum_function_call_encoder {
|
||||
public:
|
||||
enum operation_t {
|
||||
OPERATION_CALL,
|
||||
OPERATION_DELEGATE_CALL
|
||||
};
|
||||
|
||||
static constexpr const char *const default_prev_addr = "0000000000000000000000000000000000000001";
|
||||
|
||||
std::string encode_function_signature(const std::string &function_signature);
|
||||
std::string encode_address(const std::string &addr);
|
||||
std::string encode_uint256(const std::string &value);
|
||||
std::string encode_uint8(uint8_t value);
|
||||
std::string encode_bytes(const std::string &values);
|
||||
};
|
||||
|
||||
class safe_transaction_encoder {
|
||||
public:
|
||||
static constexpr const char *const default_safe_tx_gas = "0";
|
||||
static constexpr const char *const default_data_gas = "0";
|
||||
static constexpr const char *const default_gas_price = "0";
|
||||
static constexpr const char *const default_gas_token = "0000000000000000000000000000000000000000";
|
||||
static constexpr const char *const default_refund_receiver = "0000000000000000000000000000000000000000";
|
||||
|
||||
std::string create_safe_address(const std::vector<std::string> &owner_addresses, uint32_t threshold);
|
||||
std::string build_transaction(const std::string &safe_account_addr, const std::string &value, const std::string &data, uint8_t operation, const std::string &safeTxGas, const std::string &dataGas, const std::string &gasPrice, const std::string &gasToken, const std::string &refundReceiver);
|
||||
|
||||
private:
|
||||
ethereum_function_call_encoder m_ethereum_function_call_encoder;
|
||||
};*/
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::ethereum
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace ethereum {
|
||||
|
||||
bytes keccak_hash(const std::string &data);
|
||||
signature sign_hash(const bytes &hash, const std::string &chain_id, const std::string &private_key);
|
||||
|
||||
class base_transaction {
|
||||
public:
|
||||
base_transaction() = default;
|
||||
|
|
@ -75,89 +78,3 @@ public:
|
|||
};
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::ethereum
|
||||
|
||||
// Example 1
|
||||
//{
|
||||
// "blockHash": "0x64a6706ecaf5a97b7f3e047abb20ff223ce82c6994d80e68fdb1fdfb38d0209c",
|
||||
// "blockNumber": "0xe5827c",
|
||||
// "from": "0x8614c67e085f2334010f2a28e806c6f1cc176d12",
|
||||
// "gas": "0x38822",
|
||||
// "gasPrice": "0xce42cba69",
|
||||
// "maxFeePerGas": "0xddb4d8d16",
|
||||
// "maxPriorityFeePerGas": "0x3b9aca00",
|
||||
// "hash": "0xeac92ea09fa8eb3ca2fb0d156cceb38ae69d4345869d41e8e49d5ecbcbb622dc",
|
||||
// "input": "0x5ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde03121500000000000000000000000000000000000000000000000000000000",
|
||||
// "nonce": "0x32",
|
||||
// "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45",
|
||||
// "transactionIndex": "0xb6",
|
||||
// "value": "0x2514d9d7d7d8000",
|
||||
// "type": "0x2",
|
||||
// "accessList": [],
|
||||
// "chainId": "0x1",
|
||||
// "v": "0x1",
|
||||
// "r": "0x2f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28c",
|
||||
// "s": "0x782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14"
|
||||
//}
|
||||
//
|
||||
//"0xf9021332850ce42cba69830388229468b3465833fb72a70ecdf485e0e4c7bd8665fc458802514d9d7d7d8000b901a45ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde0312150000000000000000000000000000000000000000000000000000000001a02f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28ca0782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14"
|
||||
//
|
||||
//{
|
||||
// "nonce": 50,
|
||||
// "gasPrice": {
|
||||
// "_hex": "0x0ce42cba69"
|
||||
// },
|
||||
// "gasLimit": {
|
||||
// "_hex": "0x038822"
|
||||
// },
|
||||
// "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45",
|
||||
// "value": {
|
||||
// "_hex": "0x02514d9d7d7d8000"
|
||||
// },
|
||||
// "data": "0x5ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde03121500000000000000000000000000000000000000000000000000000000",
|
||||
// "v": 1,
|
||||
// "r": "0x2f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28c",
|
||||
// "s": "0x782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14"
|
||||
//}
|
||||
|
||||
// Example 2
|
||||
//{
|
||||
// "blockHash": "0xe2ae3afd86dc7343c7fb753441447a0a51bb19499325ad6e278256f0cd1b5894",
|
||||
// "blockNumber": "0xe58271",
|
||||
// "from": "0xb895ade6d337fbb8cb97f2ea7da43106c7f5cc26",
|
||||
// "gas": "0x5208",
|
||||
// "gasPrice": "0xe6f3b322e",
|
||||
// "maxFeePerGas": "0x1322455fd3",
|
||||
// "maxPriorityFeePerGas": "0x53724e00",
|
||||
// "hash": "0xed29b56e52ad2d452e25b8ec70c37f59d935cd6d0f8fe8e83b256f3ffdfd3fce",
|
||||
// "input": "0x",
|
||||
// "nonce": "0x37",
|
||||
// "to": "0x176386b6ffc469ac049f9ec1f6cc0efd1d09b373",
|
||||
// "transactionIndex": "0x8a",
|
||||
// "value": "0x4563918244f40000",
|
||||
// "type": "0x2",
|
||||
// "accessList": [],
|
||||
// "chainId": "0x1",
|
||||
// "v": "0x0",
|
||||
// "r": "0xdcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4c",
|
||||
// "s": "0x28c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a"
|
||||
//}
|
||||
//
|
||||
//"0xf86c37850e6f3b322e82520894176386b6ffc469ac049f9ec1f6cc0efd1d09b373884563918244f400008000a0dcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4ca028c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a"
|
||||
//
|
||||
//{
|
||||
// "nonce": 55,
|
||||
// "gasPrice": {
|
||||
// "_hex": "0x0e6f3b322e"
|
||||
// },
|
||||
// "gasLimit": {
|
||||
// "_hex": "0x5208"
|
||||
// },
|
||||
// "to": "0x176386b6ffc469ac049f9ec1f6cc0efd1d09b373",
|
||||
// "value": {
|
||||
// "_hex": "0x4563918244f40000"
|
||||
// },
|
||||
// "data": "0x",
|
||||
// "v": 0,
|
||||
// "r": "0xdcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4c",
|
||||
// "s": "0x28c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a"
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -9,4 +9,17 @@ typedef uint64_t network_id_type;
|
|||
|
||||
using bytes = std::vector<char>;
|
||||
|
||||
class signature {
|
||||
public:
|
||||
std::string v;
|
||||
std::string r;
|
||||
std::string s;
|
||||
|
||||
signature() = default;
|
||||
signature(const std::string &sign);
|
||||
|
||||
std::string serialize() const;
|
||||
void deserialize(const std::string &sign);
|
||||
};
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::ethereum
|
||||
|
|
|
|||
|
|
@ -14,12 +14,16 @@ std::string add_0x(const std::string &s);
|
|||
|
||||
std::string remove_0x(const std::string &s);
|
||||
|
||||
std::string add_leading_00(const std::string &s);
|
||||
|
||||
std::string remove_leading_00(const std::string &s);
|
||||
|
||||
template <typename T>
|
||||
std::string to_hex(const T &val, bool add_front_zero = true) {
|
||||
std::stringstream stream;
|
||||
stream << std::hex << val;
|
||||
std::string result(stream.str());
|
||||
if(add_front_zero) {
|
||||
if (add_front_zero) {
|
||||
if (result.size() % 2)
|
||||
result = "0" + result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,15 +22,15 @@ typedef fc::ecc::private_key private_key_type;
|
|||
typedef fc::sha256 chain_id_type;
|
||||
typedef std::string account_name_type;
|
||||
typedef fc::ripemd160 block_id_type;
|
||||
//typedef fc::ripemd160 checksum_type;
|
||||
// typedef fc::ripemd160 checksum_type;
|
||||
typedef fc::ripemd160 transaction_id_type;
|
||||
typedef fc::sha256 digest_type;
|
||||
typedef fc::ecc::compact_signature signature_type;
|
||||
typedef fc::safe<int64_t> share_type;
|
||||
//typedef safe<uint64_t> ushare_type;
|
||||
//typedef uint16_t weight_type;
|
||||
//typedef uint32_t contribution_id_type;
|
||||
//typedef fixed_string<32> custom_id_type;
|
||||
// typedef safe<uint64_t> ushare_type;
|
||||
// typedef uint16_t weight_type;
|
||||
// typedef uint32_t contribution_id_type;
|
||||
// typedef fixed_string<32> custom_id_type;
|
||||
|
||||
struct public_key_type {
|
||||
|
||||
|
|
|
|||
|
|
@ -16,14 +16,17 @@
|
|||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class sidechain_net_handler {
|
||||
protected:
|
||||
sidechain_net_handler(sidechain_type _sidechain, peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options);
|
||||
|
||||
public:
|
||||
sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options);
|
||||
virtual ~sidechain_net_handler();
|
||||
|
||||
sidechain_type get_sidechain();
|
||||
std::vector<std::string> get_sidechain_deposit_addresses();
|
||||
std::vector<std::string> get_sidechain_withdraw_addresses();
|
||||
std::string get_private_key(std::string public_key);
|
||||
sidechain_type get_sidechain() const;
|
||||
std::vector<std::string> get_sidechain_deposit_addresses() const;
|
||||
std::vector<std::string> get_sidechain_withdraw_addresses() const;
|
||||
std::vector<sidechain_transaction_object> get_sidechain_transaction_objects(sidechain_transaction_status status) const;
|
||||
std::string get_private_key(std::string public_key) const;
|
||||
|
||||
bool proposal_exists(int32_t operation_tag, const object_id_type &object_id, boost::optional<chain::operation &> proposal_op = boost::none);
|
||||
bool signer_expected(const sidechain_transaction_object &sto, son_id_type signer);
|
||||
|
|
@ -53,11 +56,12 @@ public:
|
|||
virtual optional<asset> estimate_withdrawal_transaction_fee() const = 0;
|
||||
|
||||
protected:
|
||||
const sidechain_type sidechain;
|
||||
peerplays_sidechain_plugin &plugin;
|
||||
graphene::chain::database &database;
|
||||
sidechain_type sidechain;
|
||||
|
||||
bool debug_rpc_calls;
|
||||
bool use_bitcoind_client;
|
||||
|
||||
std::map<std::string, std::string> private_keys;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/common/rpc_client.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <zmq_addon.hpp>
|
||||
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <fc/network/http/connection.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 {
|
||||
|
||||
|
|
@ -23,7 +25,27 @@ public:
|
|||
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:
|
||||
enum class multi_type {
|
||||
script,
|
||||
|
|
@ -41,14 +63,47 @@ public:
|
|||
std::string label;
|
||||
};
|
||||
|
||||
public:
|
||||
bitcoin_rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls);
|
||||
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;
|
||||
};
|
||||
|
||||
std::string createwallet(const std::string &wallet_name);
|
||||
uint64_t estimatesmartfee(uint16_t conf_target = 128);
|
||||
std::string getblock(const std::string &block_hash, int32_t verbosity = 2);
|
||||
std::string getrawtransaction(const std::string &txid, const bool verbose = false);
|
||||
std::string getnetworkinfo();
|
||||
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:
|
||||
bitcoin_rpc_client(const std::vector<rpc_credentials> &_credentials, bool _debug_rpc_calls, bool _simulate_connection_reselection);
|
||||
|
||||
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();
|
||||
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);
|
||||
|
|
@ -58,37 +113,88 @@ public:
|
|||
std::string walletlock();
|
||||
bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60);
|
||||
|
||||
virtual uint64_t ping(rpc_connection &conn) const override;
|
||||
|
||||
private:
|
||||
std::string ip;
|
||||
uint32_t rpc_port;
|
||||
std::string user;
|
||||
std::string password;
|
||||
std::string wallet_name;
|
||||
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:
|
||||
zmq_listener(std::string _ip, uint32_t _zmq);
|
||||
virtual ~zmq_listener();
|
||||
virtual ~zmq_listener_base(){};
|
||||
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();
|
||||
boost::signals2::signal<void(const std::string &)> event_received;
|
||||
|
||||
private:
|
||||
void handle_zmq();
|
||||
std::vector<zmq::message_t> receive_multipart();
|
||||
|
||||
std::string ip;
|
||||
uint32_t zmq_port;
|
||||
|
||||
zmq::context_t ctx;
|
||||
zmq::socket_t socket;
|
||||
};
|
||||
|
||||
std::atomic_bool stopped;
|
||||
std::thread thr;
|
||||
class zmq_listener_libbitcoin : public zmq_listener_base {
|
||||
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 +215,16 @@ public:
|
|||
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
|
||||
|
||||
private:
|
||||
std::string ip;
|
||||
uint32_t zmq_port;
|
||||
uint32_t rpc_port;
|
||||
std::string rpc_user;
|
||||
std::string rpc_password;
|
||||
std::vector<rpc_credentials> _rpc_credentials;
|
||||
std::string libbitcoin_server_ip;
|
||||
uint32_t libbitcoin_block_zmq_port;
|
||||
uint32_t libbitcoin_trx_zmq_port;
|
||||
uint32_t bitcoin_node_zmq_port;
|
||||
std::string wallet_name;
|
||||
std::string wallet_password;
|
||||
|
||||
std::unique_ptr<bitcoin_rpc_client> bitcoin_client;
|
||||
std::unique_ptr<zmq_listener> listener;
|
||||
std::unique_ptr<bitcoin_client_base> bitcoin_client;
|
||||
std::unique_ptr<zmq_listener_base> listener;
|
||||
|
||||
fc::future<void> on_changed_objects_task;
|
||||
|
||||
|
|
@ -128,7 +234,7 @@ private:
|
|||
std::mutex event_handler_mutex;
|
||||
typedef std::lock_guard<decltype(event_handler_mutex)> scoped_lock;
|
||||
|
||||
std::string create_primary_wallet_address(const std::vector<son_info> &son_pubkeys);
|
||||
std::string create_primary_wallet_address(const std::vector<son_sidechain_info> &son_pubkeys);
|
||||
|
||||
std::string create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address);
|
||||
std::string create_deposit_transaction(const son_wallet_deposit_object &swdo);
|
||||
|
|
@ -138,9 +244,9 @@ private:
|
|||
std::string sign_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::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_cb(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include <boost/bimap.hpp>
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
#include <graphene/peerplays_sidechain/common/rpc_client.hpp>
|
||||
|
|
@ -13,7 +14,7 @@ namespace graphene { namespace peerplays_sidechain {
|
|||
|
||||
class ethereum_rpc_client : public rpc_client {
|
||||
public:
|
||||
ethereum_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls);
|
||||
ethereum_rpc_client(const std::vector<rpc_credentials> &credentials, bool debug_rpc_calls, bool simulate_connection_reselection);
|
||||
|
||||
std::string eth_blockNumber();
|
||||
std::string eth_get_block_by_number(std::string block_number, bool full_block);
|
||||
|
|
@ -34,6 +35,9 @@ public:
|
|||
std::string eth_send_transaction(const std::string ¶ms);
|
||||
std::string eth_send_raw_transaction(const std::string ¶ms);
|
||||
std::string eth_get_transaction_receipt(const std::string ¶ms);
|
||||
std::string eth_get_transaction_by_hash(const std::string ¶ms);
|
||||
|
||||
virtual uint64_t ping(rpc_connection &conn) const override;
|
||||
};
|
||||
|
||||
class sidechain_net_handler_ethereum : public sidechain_net_handler {
|
||||
|
|
@ -52,17 +56,17 @@ public:
|
|||
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
|
||||
|
||||
private:
|
||||
std::string rpc_url;
|
||||
std::string rpc_user;
|
||||
std::string rpc_password;
|
||||
std::vector<rpc_credentials> _rpc_credentials;
|
||||
std::string wallet_contract_address;
|
||||
using bimap_type = boost::bimap<std::string, std::string>;
|
||||
bimap_type erc20_addresses;
|
||||
|
||||
ethereum_rpc_client *rpc_client;
|
||||
|
||||
ethereum::chain_id_type chain_id;
|
||||
ethereum::network_id_type network_id;
|
||||
|
||||
std::string create_primary_wallet_transaction(const std::vector<son_info> &son_pubkeys, const std::string &object_id);
|
||||
std::string create_primary_wallet_transaction(const std::vector<son_sidechain_info> &son_pubkeys, const std::string &object_id);
|
||||
std::string create_deposit_transaction(const son_wallet_deposit_object &swdo);
|
||||
std::string create_withdrawal_transaction(const son_wallet_withdraw_object &swwo);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace graphene { namespace peerplays_sidechain {
|
|||
|
||||
class hive_rpc_client : public rpc_client {
|
||||
public:
|
||||
hive_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls);
|
||||
hive_rpc_client(const std::vector<rpc_credentials> &credentials, bool debug_rpc_calls, bool simulate_connection_reselection);
|
||||
|
||||
std::string account_history_api_get_transaction(std::string transaction_id);
|
||||
std::string block_api_get_block(uint32_t block_number);
|
||||
|
|
@ -30,6 +30,8 @@ public:
|
|||
std::string get_head_block_time();
|
||||
std::string get_is_test_net();
|
||||
std::string get_last_irreversible_block_num();
|
||||
|
||||
virtual uint64_t ping(rpc_connection &conn) const override;
|
||||
};
|
||||
|
||||
class sidechain_net_handler_hive : public sidechain_net_handler {
|
||||
|
|
@ -48,9 +50,8 @@ public:
|
|||
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
|
||||
|
||||
private:
|
||||
std::string rpc_url;
|
||||
std::string rpc_user;
|
||||
std::string rpc_password;
|
||||
std::vector<rpc_credentials> _rpc_credentials;
|
||||
|
||||
std::string wallet_account_name;
|
||||
|
||||
hive_rpc_client *rpc_client;
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ private:
|
|||
uint16_t retries_threshold = 150;
|
||||
|
||||
bool first_block_skipped;
|
||||
bool son_processing_enabled;
|
||||
void on_applied_block(const signed_block &b);
|
||||
};
|
||||
|
||||
|
|
@ -116,26 +117,27 @@ peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidec
|
|||
sidechain_enabled_peerplays(false),
|
||||
current_son_id([] {
|
||||
std::map<sidechain_type, son_id_type> current_son_id;
|
||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||
for (const auto &active_sidechain_type : all_sidechain_types) {
|
||||
current_son_id.emplace(active_sidechain_type, son_id_type(std::numeric_limits<uint32_t>().max()));
|
||||
}
|
||||
return current_son_id;
|
||||
}()),
|
||||
sidechain_enabled([] {
|
||||
std::map<sidechain_type, bool> sidechain_enabled;
|
||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||
for (const auto &active_sidechain_type : all_sidechain_types) {
|
||||
sidechain_enabled.emplace(active_sidechain_type, false);
|
||||
}
|
||||
return sidechain_enabled;
|
||||
}()),
|
||||
net_handlers([] {
|
||||
std::map<sidechain_type, std::unique_ptr<sidechain_net_handler>> net_handlers;
|
||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||
for (const auto &active_sidechain_type : all_sidechain_types) {
|
||||
net_handlers.emplace(active_sidechain_type, nullptr);
|
||||
}
|
||||
return net_handlers;
|
||||
}()),
|
||||
first_block_skipped(false) {
|
||||
first_block_skipped(false),
|
||||
son_processing_enabled(false) {
|
||||
}
|
||||
|
||||
peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() {
|
||||
|
|
@ -143,18 +145,18 @@ peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() {
|
|||
if (_heartbeat_task.valid())
|
||||
_heartbeat_task.cancel_and_wait(__FUNCTION__);
|
||||
} catch (fc::canceled_exception &) {
|
||||
//Expected exception. Move along.
|
||||
// Expected exception. Move along.
|
||||
} catch (fc::exception &e) {
|
||||
edump((e.to_detail_string()));
|
||||
}
|
||||
|
||||
try {
|
||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||
for (const auto &active_sidechain_type : all_sidechain_types) {
|
||||
if (_son_processing_task.count(active_sidechain_type) != 0 && _son_processing_task.at(active_sidechain_type).valid())
|
||||
_son_processing_task.at(active_sidechain_type).wait();
|
||||
}
|
||||
} catch (fc::canceled_exception &) {
|
||||
//Expected exception. Move along.
|
||||
// Expected exception. Move along.
|
||||
} catch (fc::exception &e) {
|
||||
edump((e.to_detail_string()));
|
||||
}
|
||||
|
|
@ -175,9 +177,14 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options(
|
|||
cli.add_options()("sidechain-retry-threshold", bpo::value<uint16_t>()->default_value(150), "Sidechain retry throttling threshold");
|
||||
|
||||
cli.add_options()("debug-rpc-calls", bpo::value<bool>()->default_value(false), "Outputs RPC calls to console");
|
||||
cli.add_options()("simulate-rpc-connection-reselection", bpo::value<bool>()->default_value(false), "Simulate RPC connection reselection by altering their response times by a random value");
|
||||
|
||||
cli.add_options()("bitcoin-sidechain-enabled", bpo::value<bool>()->default_value(false), "Bitcoin sidechain handler enabled");
|
||||
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<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR("127.0.0.1"), "IP address of Bitcoin node");
|
||||
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-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-user", bpo::value<string>()->default_value("1"), "Bitcoin RPC user");
|
||||
|
|
@ -188,15 +195,17 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options(
|
|||
"Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)");
|
||||
|
||||
cli.add_options()("ethereum-sidechain-enabled", bpo::value<bool>()->default_value(false), "Ethereum sidechain handler enabled");
|
||||
cli.add_options()("ethereum-node-rpc-url", bpo::value<string>()->default_value("127.0.0.1:8545"), "Ethereum node RPC URL [http[s]://]host[:port]");
|
||||
cli.add_options()("ethereum-node-rpc-url", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR("127.0.0.1:8545"), "Ethereum node RPC URL [http[s]://]host[:port]");
|
||||
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-wallet-contract-address", bpo::value<string>(), "Ethereum wallet contract address");
|
||||
cli.add_options()("ethereum-erc-20-address", bpo::value<vector<string>>()->composing()->multitoken(),
|
||||
"Tuple of [ERC-20 symbol, ERC-20 address] (may specify multiple times)");
|
||||
cli.add_options()("ethereum-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", "9bedac2bd8fe2a6f6528e066c67fc8ac0622e96828d40c0e820d83c5bd2b0589")),
|
||||
"Tuple of [Ethereum public key, Ethereum private key] (may specify multiple times)");
|
||||
|
||||
cli.add_options()("hive-sidechain-enabled", bpo::value<bool>()->default_value(false), "Hive sidechain handler enabled");
|
||||
cli.add_options()("hive-node-rpc-url", bpo::value<string>()->default_value("127.0.0.1:28090"), "Hive node RPC URL [http[s]://]host[:port]");
|
||||
cli.add_options()("hive-node-rpc-url", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR("127.0.0.1:28090"), "Hive node RPC URL [http[s]://]host[:port]");
|
||||
cli.add_options()("hive-node-rpc-user", bpo::value<string>(), "Hive node RPC user");
|
||||
cli.add_options()("hive-node-rpc-password", bpo::value<string>(), "Hive node RPC password");
|
||||
cli.add_options()("hive-wallet-account-name", bpo::value<string>(), "Hive wallet account name");
|
||||
|
|
@ -247,12 +256,14 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt
|
|||
}
|
||||
|
||||
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") &&
|
||||
options.count("bitcoin-node-rpc-user") && options.count("bitcoin-node-rpc-password") &&
|
||||
options.count("bitcoin-wallet-name") && options.count("bitcoin-wallet-password") &&
|
||||
options.count("bitcoin-private-key");
|
||||
if (sidechain_enabled_bitcoin && !config_ready_bitcoin) {
|
||||
|
||||
config_ready_bitcoin = (((options.count("libbitcoin-server-ip") && options.count("libbitcoin-server-zmq-port")) ||
|
||||
(options.count("bitcoin-node-ip") && options.count("bitcoin-node-zmq-port") &&
|
||||
options.count("bitcoin-node-rpc-port") && options.count("bitcoin-node-rpc-user") &&
|
||||
options.count("bitcoin-node-rpc-password") && options.count("bitcoin-wallet-name") &&
|
||||
options.count("bitcoin-wallet-password"))) &&
|
||||
options.count("bitcoin-private-key"));
|
||||
if (!config_ready_bitcoin) {
|
||||
wlog("Haven't set up Bitcoin sidechain parameters");
|
||||
}
|
||||
|
||||
|
|
@ -283,6 +294,9 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt
|
|||
if (sidechain_enabled_peerplays && !config_ready_peerplays) {
|
||||
wlog("Haven't set up Peerplays sidechain parameters");
|
||||
}
|
||||
|
||||
if (options.at("simulate-rpc-connection-reselection").as<bool>())
|
||||
ilog("### RPC connection reselection will be simulated");
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::plugin_startup() {
|
||||
|
|
@ -360,7 +374,7 @@ bool peerplays_sidechain_plugin_impl::is_active_son(sidechain_type sidechain, so
|
|||
set<son_id_type> active_son_ids;
|
||||
std::transform(gpo.active_sons.at(sidechain).cbegin(), gpo.active_sons.at(sidechain).cend(),
|
||||
std::inserter(active_son_ids, active_son_ids.end()),
|
||||
[](const son_info &swi) {
|
||||
[](const son_sidechain_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
|
|
@ -398,14 +412,14 @@ bool peerplays_sidechain_plugin_impl::is_son_down_op_valid(const chain::operatio
|
|||
const chain::global_property_object &gpo = d.get_global_properties();
|
||||
const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties();
|
||||
const auto &idx = d.get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
son_report_down_operation down_op = op.get<son_report_down_operation>();
|
||||
auto son_obj = idx.find(down_op.son_id);
|
||||
const son_report_down_operation down_op = op.get<son_report_down_operation>();
|
||||
const auto son_obj = idx.find(down_op.son_id);
|
||||
if (son_obj == idx.end()) {
|
||||
return false;
|
||||
}
|
||||
auto stats = son_obj->statistics(d);
|
||||
fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval;
|
||||
int64_t down_threshold = gpo.parameters.son_down_time();
|
||||
const auto stats = son_obj->statistics(d);
|
||||
const fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval;
|
||||
const int64_t down_threshold = gpo.parameters.son_down_time();
|
||||
|
||||
bool status_son_down_op_valid = true;
|
||||
for (const auto &status : son_obj->statuses) {
|
||||
|
|
@ -413,10 +427,12 @@ bool peerplays_sidechain_plugin_impl::is_son_down_op_valid(const chain::operatio
|
|||
status_son_down_op_valid = false;
|
||||
}
|
||||
if (status_son_down_op_valid) {
|
||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||
fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(active_sidechain_type) > last_maintenance_time) ? stats.last_active_timestamp.at(active_sidechain_type) : last_maintenance_time);
|
||||
if (((fc::time_point::now() - last_active_ts) <= fc::seconds(down_threshold))) {
|
||||
status_son_down_op_valid = false;
|
||||
for (const auto &active_sidechain_type : active_sidechain_types(d.head_block_time())) {
|
||||
if (stats.last_active_timestamp.contains(active_sidechain_type)) {
|
||||
const fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(active_sidechain_type) > last_maintenance_time) ? stats.last_active_timestamp.at(active_sidechain_type) : last_maintenance_time);
|
||||
if (((fc::time_point::now() - last_active_ts) <= fc::seconds(down_threshold))) {
|
||||
status_son_down_op_valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -464,7 +480,7 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() {
|
|||
|
||||
//! Check that son is active (at least for one sidechain_type)
|
||||
bool is_son_active = false;
|
||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||
for (const auto &active_sidechain_type : active_sidechain_types(d.head_block_time())) {
|
||||
if (sidechain_enabled.at(active_sidechain_type)) {
|
||||
if (is_active_son(active_sidechain_type, son_id))
|
||||
is_son_active = true;
|
||||
|
|
@ -502,7 +518,7 @@ void peerplays_sidechain_plugin_impl::schedule_son_processing() {
|
|||
|
||||
const auto next_wakeup = now + std::chrono::microseconds(time_to_next_son_processing);
|
||||
|
||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||
for (const auto &active_sidechain_type : active_sidechain_types(plugin.database().head_block_time())) {
|
||||
if (_son_processing_task.count(active_sidechain_type) != 0 && _son_processing_task.at(active_sidechain_type).wait_for(std::chrono::seconds{0}) != std::future_status::ready) {
|
||||
wlog("Son doesn't process in time for sidechain: ${active_sidechain_type}", ("active_sidechain_type", active_sidechain_type));
|
||||
_son_processing_task.at(active_sidechain_type).wait();
|
||||
|
|
@ -524,11 +540,11 @@ void peerplays_sidechain_plugin_impl::son_processing(sidechain_type sidechain) {
|
|||
return;
|
||||
}
|
||||
|
||||
//fc::time_point now_fine = fc::time_point::now();
|
||||
//fc::time_point_sec now = now_fine + fc::microseconds(500000);
|
||||
//if (plugin.database().get_slot_time(1) < now) {
|
||||
// return; // Not synced
|
||||
//}
|
||||
// fc::time_point now_fine = fc::time_point::now();
|
||||
// fc::time_point_sec now = now_fine + fc::microseconds(500000);
|
||||
// if (plugin.database().get_slot_time(1) < now) {
|
||||
// return; // Not synced
|
||||
// }
|
||||
|
||||
const fc::time_point now_fine = fc::time_point::now();
|
||||
const fc::time_point_sec now = now_fine - fc::milliseconds(3000);
|
||||
|
|
@ -619,7 +635,7 @@ bool peerplays_sidechain_plugin_impl::can_son_participate(sidechain_type sidecha
|
|||
|
||||
std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin_impl::get_son_listener_log() {
|
||||
std::map<sidechain_type, std::vector<std::string>> result;
|
||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||
for (const auto &active_sidechain_type : active_sidechain_types(plugin.database().head_block_time())) {
|
||||
if (net_handlers.at(active_sidechain_type)) {
|
||||
result.emplace(active_sidechain_type, net_handlers.at(active_sidechain_type)->get_son_listener_log());
|
||||
}
|
||||
|
|
@ -628,6 +644,11 @@ std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin_im
|
|||
}
|
||||
|
||||
optional<asset> peerplays_sidechain_plugin_impl::estimate_withdrawal_transaction_fee(sidechain_type sidechain) {
|
||||
if (net_handlers.count(sidechain) == 0) {
|
||||
wlog("No net handler for sidechain: ${sidechain}", ("sidechain", sidechain));
|
||||
return optional<asset>();
|
||||
}
|
||||
|
||||
if (!net_handlers.at(sidechain)) {
|
||||
wlog("Net handler is null for sidechain: ${sidechain}", ("sidechain", sidechain));
|
||||
return optional<asset>();
|
||||
|
|
@ -705,7 +726,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type s
|
|||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = get_current_son_object(sidechain).son_account;
|
||||
proposal_op.proposed_ops.emplace_back(op_wrapper(son_down_op));
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
const uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(d.head_block_time().sec_since_epoch() + lifetime);
|
||||
return proposal_op;
|
||||
};
|
||||
|
|
@ -714,19 +735,27 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type s
|
|||
const chain::global_property_object &gpo = d.get_global_properties();
|
||||
const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties();
|
||||
const auto &idx = d.get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
std::set<son_id_type> sons_being_reported_down = d.get_sons_being_reported_down();
|
||||
chain::son_id_type my_son_id = get_current_son_id(sidechain);
|
||||
const std::set<son_id_type> sons_being_reported_down = d.get_sons_being_reported_down();
|
||||
const chain::son_id_type my_son_id = get_current_son_id(sidechain);
|
||||
|
||||
//! Fixme - check this part of the code
|
||||
for (auto son_inf : gpo.active_sons.at(sidechain)) {
|
||||
if (my_son_id == son_inf.son_id || (sons_being_reported_down.find(son_inf.son_id) != sons_being_reported_down.end())) {
|
||||
continue;
|
||||
}
|
||||
auto son_obj = idx.find(son_inf.son_id);
|
||||
auto stats = son_obj->statistics(d);
|
||||
fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval;
|
||||
fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(sidechain) > last_maintenance_time) ? stats.last_active_timestamp.at(sidechain) : last_maintenance_time);
|
||||
int64_t down_threshold = gpo.parameters.son_down_time();
|
||||
|
||||
const auto son_obj = idx.find(son_inf.son_id);
|
||||
const auto stats = son_obj->statistics(d);
|
||||
const fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval;
|
||||
const fc::time_point_sec last_active_ts = [&stats, &sidechain, &last_maintenance_time] {
|
||||
fc::time_point_sec last_active_ts;
|
||||
if (stats.last_active_timestamp.contains(sidechain)) {
|
||||
last_active_ts = (stats.last_active_timestamp.at(sidechain) > last_maintenance_time) ? stats.last_active_timestamp.at(sidechain) : last_maintenance_time;
|
||||
} else
|
||||
last_active_ts = last_maintenance_time;
|
||||
return last_active_ts;
|
||||
}();
|
||||
const int64_t down_threshold = gpo.parameters.son_down_time();
|
||||
|
||||
bool status_son_down_valid = true;
|
||||
for (const auto &status : son_obj->statuses) {
|
||||
|
|
@ -756,7 +785,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type s
|
|||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::create_son_deregister_proposals(sidechain_type sidechain) {
|
||||
const std::lock_guard<std::mutex> lck{access_son_down_prop_mutex};
|
||||
const std::lock_guard<std::mutex> lck{access_son_deregister_prop_mutex};
|
||||
chain::database &d = plugin.database();
|
||||
std::set<son_id_type> sons_to_be_dereg = d.get_sons_to_be_deregistered();
|
||||
chain::son_id_type my_son_id = get_current_son_id(sidechain);
|
||||
|
|
@ -844,7 +873,16 @@ void peerplays_sidechain_plugin_impl::settle_sidechain_transactions(sidechain_ty
|
|||
|
||||
void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) {
|
||||
if (first_block_skipped) {
|
||||
schedule_son_processing();
|
||||
if (son_processing_enabled) {
|
||||
schedule_son_processing();
|
||||
} else {
|
||||
const fc::time_point now_fine = fc::time_point::now();
|
||||
const fc::time_point_sec now = now_fine + fc::microseconds(500000);
|
||||
if (plugin.database().get_slot_time(1) >= now) {
|
||||
son_processing_enabled = true;
|
||||
schedule_son_processing();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
first_block_skipped = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) :
|
||||
sidechain_net_handler::sidechain_net_handler(sidechain_type _sidechain, peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) :
|
||||
sidechain(_sidechain),
|
||||
plugin(_plugin),
|
||||
database(_plugin.database()) {
|
||||
|
||||
|
|
@ -21,11 +22,11 @@ sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin &_plugin
|
|||
sidechain_net_handler::~sidechain_net_handler() {
|
||||
}
|
||||
|
||||
sidechain_type sidechain_net_handler::get_sidechain() {
|
||||
sidechain_type sidechain_net_handler::get_sidechain() const {
|
||||
return sidechain;
|
||||
}
|
||||
|
||||
std::vector<std::string> sidechain_net_handler::get_sidechain_deposit_addresses() {
|
||||
std::vector<std::string> sidechain_net_handler::get_sidechain_deposit_addresses() const {
|
||||
std::vector<std::string> result;
|
||||
|
||||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>();
|
||||
|
|
@ -38,7 +39,7 @@ std::vector<std::string> sidechain_net_handler::get_sidechain_deposit_addresses(
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> sidechain_net_handler::get_sidechain_withdraw_addresses() {
|
||||
std::vector<std::string> sidechain_net_handler::get_sidechain_withdraw_addresses() const {
|
||||
std::vector<std::string> result;
|
||||
|
||||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>();
|
||||
|
|
@ -51,7 +52,20 @@ std::vector<std::string> sidechain_net_handler::get_sidechain_withdraw_addresses
|
|||
return result;
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler::get_private_key(std::string public_key) {
|
||||
std::vector<sidechain_transaction_object> sidechain_net_handler::get_sidechain_transaction_objects(sidechain_transaction_status status) const {
|
||||
std::vector<sidechain_transaction_object> result;
|
||||
|
||||
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_status>();
|
||||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, status));
|
||||
std::for_each(idx_range.first, idx_range.second,
|
||||
[&result](const sidechain_transaction_object &sto) {
|
||||
result.push_back(sto);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler::get_private_key(std::string public_key) const {
|
||||
auto private_key_itr = private_keys.find(public_key);
|
||||
if (private_key_itr != private_keys.end()) {
|
||||
return private_key_itr->second;
|
||||
|
|
@ -170,29 +184,32 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
|
|||
|
||||
bool enable_peerplays_asset_deposits = false;
|
||||
#ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS
|
||||
//enable_peerplays_asset_deposits = (sed.sidechain == sidechain_type::peerplays) &&
|
||||
// (sed.sidechain_currency.compare("BTC") != 0) &&
|
||||
// (sed.sidechain_currency.compare("ETH") != 0) &&
|
||||
// (sed.sidechain_currency.compare("HBD") != 0) &&
|
||||
// (sed.sidechain_currency.compare("HIVE") != 0);
|
||||
// enable_peerplays_asset_deposits = (sed.sidechain == sidechain_type::peerplays) &&
|
||||
// (sed.sidechain_currency.compare("BTC") != 0) &&
|
||||
// (sed.sidechain_currency.compare("ETH") != 0) &&
|
||||
// (sed.sidechain_currency.compare("HBD") != 0) &&
|
||||
// (sed.sidechain_currency.compare("HIVE") != 0);
|
||||
#endif
|
||||
|
||||
bool deposit_condition = (sed.peerplays_to == gpo.parameters.son_account()) &&
|
||||
(((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency.compare("BTC") == 0)) ||
|
||||
((sed.sidechain == sidechain_type::ethereum) && (sed.sidechain_currency.compare("ETH") == 0)) ||
|
||||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HBD") == 0)) ||
|
||||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) ||
|
||||
enable_peerplays_asset_deposits);
|
||||
const bool deposit_condition = (sed.peerplays_to == gpo.parameters.son_account()) &&
|
||||
(sed.sidechain == sidechain) &&
|
||||
(sed.type == sidechain_event_type::deposit) &&
|
||||
(((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency.compare("BTC") == 0)) ||
|
||||
((sed.sidechain == sidechain_type::ethereum) && (!sed.sidechain_currency.empty())) ||
|
||||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HBD") == 0)) ||
|
||||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) ||
|
||||
enable_peerplays_asset_deposits);
|
||||
|
||||
bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain == sidechain) && //! Fixme -> sidechain_type::peerplays
|
||||
((sed.sidechain_currency == object_id_to_string(gpo.parameters.btc_asset())) ||
|
||||
(sed.sidechain_currency == object_id_to_string(gpo.parameters.eth_asset())) ||
|
||||
(sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset())) ||
|
||||
(sed.sidechain_currency == object_id_to_string(gpo.parameters.hive_asset())));
|
||||
const bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) &&
|
||||
(sed.sidechain == sidechain) &&
|
||||
(sed.type == sidechain_event_type::withdrawal) &&
|
||||
(((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency == object_id_to_string(gpo.parameters.btc_asset()))) ||
|
||||
((sed.sidechain == sidechain_type::ethereum) && (!sed.sidechain_currency.empty())) ||
|
||||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset()))) ||
|
||||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency == object_id_to_string(gpo.parameters.hive_asset()))));
|
||||
|
||||
// Deposit request
|
||||
if (deposit_condition) {
|
||||
|
||||
for (son_id_type son_id : plugin.get_sons()) {
|
||||
if (plugin.is_active_son(sidechain, son_id)) {
|
||||
|
||||
|
|
@ -256,7 +273,17 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
|
|||
withdraw_currency_price = database.get<asset_object>(database.get_global_properties().parameters.hive_asset()).options.core_exchange_rate;
|
||||
}
|
||||
if (withdraw_currency.empty()) {
|
||||
return;
|
||||
//! This is ERC-20 withdrawal
|
||||
const auto asset_object_id = string_to_object_id(sed.sidechain_currency);
|
||||
const auto &assets_by_id = database.get_index_type<asset_index>().indices().get<by_id>();
|
||||
const auto asset_itr = assets_by_id.find(asset_object_id);
|
||||
if (asset_itr == assets_by_id.end()) {
|
||||
wlog("Could not find asset: ${asset_object_id}", ("asset_object_id", asset_object_id));
|
||||
return;
|
||||
}
|
||||
|
||||
withdraw_currency = asset_itr->symbol;
|
||||
withdraw_currency_price = asset_itr->options.core_exchange_rate;
|
||||
}
|
||||
|
||||
for (son_id_type son_id : plugin.get_sons()) {
|
||||
|
|
@ -424,7 +451,7 @@ void sidechain_net_handler::process_deposits() {
|
|||
if (swdo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(sidechain, chain::operation::tag<chain::son_wallet_deposit_process_operation>::value, swdo.id)) {
|
||||
return;
|
||||
}
|
||||
//Ignore the deposits which are not valid anymore, considered refunds.
|
||||
// Ignore the deposits which are not valid anymore, considered refunds.
|
||||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_address_and_expires>();
|
||||
const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, swdo.sidechain_from, time_point_sec::maximum()));
|
||||
if (addr_itr == sidechain_addresses_idx.end()) {
|
||||
|
|
@ -473,10 +500,9 @@ void sidechain_net_handler::process_withdrawals() {
|
|||
}
|
||||
|
||||
void sidechain_net_handler::process_sidechain_transactions() {
|
||||
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_status>();
|
||||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::valid));
|
||||
const auto stos = get_sidechain_transaction_objects(sidechain_transaction_status::valid);
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) {
|
||||
std::for_each(stos.cbegin(), stos.cend(), [&](const sidechain_transaction_object &sto) {
|
||||
if ((sto.id == object_id_type(0, 0, 0)) || !signer_expected(sto, plugin.get_current_son_id(sidechain))) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -520,10 +546,9 @@ void sidechain_net_handler::process_sidechain_transactions() {
|
|||
}
|
||||
|
||||
void sidechain_net_handler::send_sidechain_transactions() {
|
||||
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_status>();
|
||||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::complete));
|
||||
const auto stos = get_sidechain_transaction_objects(sidechain_transaction_status::complete);
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) {
|
||||
std::for_each(stos.cbegin(), stos.cend(), [&](const sidechain_transaction_object &sto) {
|
||||
if (sto.id == object_id_type(0, 0, 0)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -555,10 +580,9 @@ void sidechain_net_handler::send_sidechain_transactions() {
|
|||
}
|
||||
|
||||
void sidechain_net_handler::settle_sidechain_transactions() {
|
||||
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_status>();
|
||||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::sent));
|
||||
const auto stos = get_sidechain_transaction_objects(sidechain_transaction_status::sent);
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) {
|
||||
std::for_each(stos.cbegin(), stos.cend(), [&](const sidechain_transaction_object &sto) {
|
||||
if (sto.id == object_id_type(0, 0, 0)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -653,9 +677,10 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) {
|
|||
continue;
|
||||
}
|
||||
|
||||
bool is_tracked_asset =
|
||||
const bool is_tracked_asset =
|
||||
((sidechain == sidechain_type::bitcoin) && (transfer_op.amount.asset_id == gpo.parameters.btc_asset())) ||
|
||||
((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id == gpo.parameters.eth_asset())) ||
|
||||
((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id != gpo.parameters.btc_asset()) && (transfer_op.amount.asset_id != gpo.parameters.hbd_asset()) && (transfer_op.amount.asset_id != gpo.parameters.hive_asset()) && (transfer_op.amount.asset_id != asset_id_type())) ||
|
||||
((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hbd_asset())) ||
|
||||
((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hive_asset()));
|
||||
|
||||
|
|
@ -681,7 +706,8 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) {
|
|||
sidechain_event_data sed;
|
||||
sed.timestamp = database.head_block_time();
|
||||
sed.block_num = database.head_block_num();
|
||||
sed.sidechain = sidechain; //! Fixme -> sidechain_type::peerplays
|
||||
sed.sidechain = sidechain;
|
||||
sed.type = sidechain_event_type::withdrawal;
|
||||
sed.sidechain_uid = sidechain_uid;
|
||||
sed.sidechain_transaction_id = trx.id().str();
|
||||
sed.sidechain_from = sidechain_from;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -14,8 +14,9 @@
|
|||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/chain/son_sidechain_info.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/ethereum/decoders.hpp>
|
||||
#include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
|
||||
#include <graphene/peerplays_sidechain/ethereum/transaction.hpp>
|
||||
#include <graphene/peerplays_sidechain/ethereum/utils.hpp>
|
||||
|
|
@ -24,8 +25,8 @@
|
|||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
ethereum_rpc_client::ethereum_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls) :
|
||||
rpc_client(url, user_name, password, debug_rpc_calls) {
|
||||
ethereum_rpc_client::ethereum_rpc_client(const std::vector<rpc_credentials> &credentials, bool debug_rpc_calls, bool simulate_connection_reselection) :
|
||||
rpc_client(sidechain_type::ethereum, credentials, debug_rpc_calls, simulate_connection_reselection) {
|
||||
}
|
||||
|
||||
std::string ethereum_rpc_client::eth_blockNumber() {
|
||||
|
|
@ -76,7 +77,7 @@ std::string ethereum_rpc_client::get_network_id() {
|
|||
}
|
||||
|
||||
std::string ethereum_rpc_client::get_nonce(const std::string &address) {
|
||||
const std::string reply_str = eth_get_transaction_count("[\"" + address + "\", \"latest\"]");
|
||||
const std::string reply_str = eth_get_transaction_count("[\"" + address + "\", \"pending\"]");
|
||||
const auto nonce_string = retrieve_value_from_reply(reply_str, "");
|
||||
if (!nonce_string.empty()) {
|
||||
const auto nonce_val = ethereum::from_hex<boost::multiprecision::uint256_t>(nonce_string);
|
||||
|
|
@ -121,20 +122,33 @@ std::string ethereum_rpc_client::eth_get_transaction_receipt(const std::string &
|
|||
return send_post_request("eth_getTransactionReceipt", "[\"" + params + "\"]", debug_rpc_calls);
|
||||
}
|
||||
|
||||
std::string ethereum_rpc_client::eth_get_transaction_by_hash(const std::string ¶ms) {
|
||||
return send_post_request("eth_getTransactionByHash", "[\"" + params + "\"]", debug_rpc_calls);
|
||||
}
|
||||
|
||||
uint64_t ethereum_rpc_client::ping(rpc_connection &conn) const {
|
||||
std::string reply = send_post_request(conn, "eth_blockNumber", "", debug_rpc_calls);
|
||||
if (!reply.empty())
|
||||
return ethereum::from_hex<uint64_t>(retrieve_value_from_reply(reply, ""));
|
||||
return std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) :
|
||||
sidechain_net_handler(_plugin, options) {
|
||||
sidechain = sidechain_type::ethereum;
|
||||
sidechain_net_handler(sidechain_type::ethereum, _plugin, options) {
|
||||
|
||||
if (options.count("debug-rpc-calls")) {
|
||||
debug_rpc_calls = options.at("debug-rpc-calls").as<bool>();
|
||||
}
|
||||
bool simulate_connection_reselection = options.at("simulate-rpc-connection-reselection").as<bool>();
|
||||
|
||||
rpc_url = options.at("ethereum-node-rpc-url").as<std::string>();
|
||||
std::vector<std::string> rpc_urls = options.at("ethereum-node-rpc-url").as<std::vector<std::string>>();
|
||||
std::string rpc_user;
|
||||
if (options.count("ethereum-node-rpc-user")) {
|
||||
rpc_user = options.at("ethereum-node-rpc-user").as<std::string>();
|
||||
} else {
|
||||
rpc_user = "";
|
||||
}
|
||||
std::string rpc_password;
|
||||
if (options.count("ethereum-node-rpc-password")) {
|
||||
rpc_password = options.at("ethereum-node-rpc-password").as<std::string>();
|
||||
} else {
|
||||
|
|
@ -143,6 +157,21 @@ sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidecha
|
|||
|
||||
wallet_contract_address = options.at("ethereum-wallet-contract-address").as<std::string>();
|
||||
|
||||
if (options.count("ethereum-erc-20-address")) {
|
||||
const std::vector<std::string> symbol_addresses = options["ethereum-erc-20-address"].as<std::vector<std::string>>();
|
||||
for (const std::string &itr : symbol_addresses) {
|
||||
auto itr_pair = graphene::app::dejsonify<std::pair<std::string, std::string>>(itr, 5);
|
||||
ilog("ERC-20 symbol: ${symbol}, address: ${address}", ("symbol", itr_pair.first)("address", itr_pair.second));
|
||||
if (!itr_pair.first.length() || !itr_pair.second.length()) {
|
||||
FC_THROW("Invalid symbol address pair.");
|
||||
}
|
||||
|
||||
auto address = itr_pair.second;
|
||||
std::transform(address.begin(), address.end(), address.begin(), ::tolower);
|
||||
erc20_addresses.insert(bimap_type::value_type{itr_pair.first, address});
|
||||
}
|
||||
}
|
||||
|
||||
if (options.count("ethereum-private-key")) {
|
||||
const std::vector<std::string> pub_priv_keys = options["ethereum-private-key"].as<std::vector<std::string>>();
|
||||
for (const std::string &itr_key_pair : pub_priv_keys) {
|
||||
|
|
@ -155,18 +184,27 @@ sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidecha
|
|||
}
|
||||
}
|
||||
|
||||
rpc_client = new ethereum_rpc_client(rpc_url, rpc_user, rpc_password, debug_rpc_calls);
|
||||
for (size_t i = 0; i < rpc_urls.size(); i++) {
|
||||
rpc_credentials creds;
|
||||
creds.url = rpc_urls[i];
|
||||
creds.user = rpc_user;
|
||||
creds.password = rpc_password;
|
||||
_rpc_credentials.push_back(creds);
|
||||
}
|
||||
FC_ASSERT(!_rpc_credentials.empty());
|
||||
|
||||
rpc_client = new ethereum_rpc_client(_rpc_credentials, debug_rpc_calls, simulate_connection_reselection);
|
||||
|
||||
const std::string chain_id_str = rpc_client->get_chain_id();
|
||||
if (chain_id_str.empty()) {
|
||||
elog("No Ethereum node running at ${url}", ("url", rpc_url));
|
||||
elog("No Ethereum node running at ${url}", ("url", _rpc_credentials[0].url));
|
||||
FC_ASSERT(false);
|
||||
}
|
||||
chain_id = std::stoll(chain_id_str);
|
||||
|
||||
const std::string network_id_str = rpc_client->get_network_id();
|
||||
if (network_id_str.empty()) {
|
||||
elog("No Ethereum node running at ${url}", ("url", rpc_url));
|
||||
elog("No Ethereum node running at ${url}", ("url", _rpc_credentials[0].url));
|
||||
FC_ASSERT(false);
|
||||
}
|
||||
network_id = std::stoll(network_id_str);
|
||||
|
|
@ -185,6 +223,7 @@ sidechain_net_handler_ethereum::~sidechain_net_handler_ethereum() {
|
|||
}
|
||||
|
||||
bool sidechain_net_handler_ethereum::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)));
|
||||
|
||||
bool should_approve = false;
|
||||
|
|
@ -213,13 +252,16 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po)
|
|||
case chain::operation::tag<chain::son_wallet_update_operation>::value: {
|
||||
bool address_ok = false;
|
||||
bool transaction_ok = false;
|
||||
son_wallet_id_type swo_id = op_obj_idx_0.get<son_wallet_update_operation>().son_wallet_id;
|
||||
const son_wallet_id_type swo_id = op_obj_idx_0.get<son_wallet_update_operation>().son_wallet_id;
|
||||
const auto ast = active_sidechain_types(database.head_block_time());
|
||||
const auto id = (swo_id.instance.value - std::distance(ast.begin(), ast.find(sidechain))) / ast.size();
|
||||
const son_wallet_id_type op_id{id};
|
||||
const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
const auto swo = idx.find(swo_id);
|
||||
const auto swo = idx.find(op_id);
|
||||
if (swo != idx.end()) {
|
||||
|
||||
auto active_sons = gpo.active_sons.at(sidechain);
|
||||
vector<son_info> wallet_sons = swo->sons.at(sidechain);
|
||||
const auto active_sons = gpo.active_sons.at(sidechain);
|
||||
const vector<son_sidechain_info> wallet_sons = swo->sons.at(sidechain);
|
||||
|
||||
bool son_sets_equal = (active_sons.size() == wallet_sons.size());
|
||||
|
||||
|
|
@ -234,8 +276,10 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po)
|
|||
}
|
||||
|
||||
if (po.proposed_transaction.operations.size() >= 2) {
|
||||
object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id;
|
||||
std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction;
|
||||
const object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id;
|
||||
const auto id = (object_id.instance() - std::distance(ast.begin(), ast.find(sidechain))) / ast.size();
|
||||
const object_id_type obj_id{object_id.space(), object_id.type(), id};
|
||||
const std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction;
|
||||
|
||||
const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_object_id>();
|
||||
const auto st = st_idx.find(object_id);
|
||||
|
|
@ -243,9 +287,9 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po)
|
|||
|
||||
std::string tx_str = "";
|
||||
|
||||
if (object_id.is<son_wallet_id_type>()) {
|
||||
if (obj_id.is<son_wallet_id_type>()) {
|
||||
const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
const auto swo = idx.find(object_id);
|
||||
const auto swo = idx.find(obj_id);
|
||||
if (swo != idx.end()) {
|
||||
tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), object_id.operator std::string());
|
||||
}
|
||||
|
|
@ -265,64 +309,65 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po)
|
|||
|
||||
case chain::operation::tag<chain::son_wallet_deposit_process_operation>::value: {
|
||||
bool process_ok = false;
|
||||
son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get<son_wallet_deposit_process_operation>().son_wallet_deposit_id;
|
||||
const son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get<son_wallet_deposit_process_operation>().son_wallet_deposit_id;
|
||||
const auto &idx = database.get_index_type<son_wallet_deposit_index>().indices().get<by_id>();
|
||||
const auto swdo = idx.find(swdo_id);
|
||||
if (swdo != idx.end()) {
|
||||
|
||||
//std::string swdo_txid = swdo->sidechain_transaction_id;
|
||||
//std::string swdo_sidechain_from = swdo->sidechain_from;
|
||||
//std::string swdo_sidechain_currency = swdo->sidechain_currency;
|
||||
//uint64_t swdo_sidechain_amount = swdo->sidechain_amount.value;
|
||||
//uint64_t swdo_op_idx = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-")));
|
||||
//
|
||||
//std::string tx_str = rpc_client->account_history_api_get_transaction(swdo_txid);
|
||||
//if (tx_str != "") {
|
||||
//
|
||||
// std::stringstream ss_tx(tx_str);
|
||||
// boost::property_tree::ptree tx;
|
||||
// boost::property_tree::read_json(ss_tx, tx);
|
||||
//
|
||||
// uint64_t op_idx = -1;
|
||||
// for (const auto &ops : tx.get_child("result.operations")) {
|
||||
// const auto &op = ops.second;
|
||||
// op_idx = op_idx + 1;
|
||||
// if (op_idx == swdo_op_idx) {
|
||||
// std::string operation_type = op.get<std::string>("type");
|
||||
//
|
||||
// if (operation_type == "transfer_operation") {
|
||||
// const auto &op_value = op.get_child("value");
|
||||
//
|
||||
// std::string sidechain_from = op_value.get<std::string>("from");
|
||||
//
|
||||
// const auto &amount_child = op_value.get_child("amount");
|
||||
//
|
||||
// uint64_t amount = amount_child.get<uint64_t>("amount");
|
||||
// std::string nai = amount_child.get<std::string>("nai");
|
||||
// std::string sidechain_currency = "";
|
||||
// if ((nai == "@@000000013" /*?? HBD*/) || (nai == "@@000000013" /*TBD*/)) {
|
||||
// sidechain_currency = "HBD";
|
||||
// }
|
||||
// if ((nai == "@@000000021") /*?? HIVE*/ || (nai == "@@000000021" /*TESTS*/)) {
|
||||
// sidechain_currency = "HIVE";
|
||||
// }
|
||||
//
|
||||
// std::string memo = op_value.get<std::string>("memo");
|
||||
// boost::trim(memo);
|
||||
// if (!memo.empty()) {
|
||||
// sidechain_from = memo;
|
||||
// }
|
||||
//
|
||||
// process_ok = (swdo_sidechain_from == sidechain_from) &&
|
||||
// (swdo_sidechain_currency == sidechain_currency) &&
|
||||
// (swdo_sidechain_amount == amount);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
const std::string swdo_txid = swdo->sidechain_transaction_id;
|
||||
const std::string swdo_sidechain_from = swdo->sidechain_from;
|
||||
const std::string swdo_sidechain_currency = swdo->sidechain_currency;
|
||||
const uint64_t swdo_sidechain_amount = swdo->sidechain_amount.value;
|
||||
|
||||
process_ok = true;
|
||||
const std::string tx_str = rpc_client->eth_get_transaction_by_hash(swdo_txid);
|
||||
if (tx_str != "") {
|
||||
|
||||
std::stringstream ss_tx(tx_str);
|
||||
boost::property_tree::ptree tx;
|
||||
boost::property_tree::read_json(ss_tx, tx);
|
||||
|
||||
if (tx.get<std::string>("result") != "null") {
|
||||
|
||||
const std::string sidechain_from = tx.get<std::string>("result.from");
|
||||
const std::string sidechain_to = tx.get<std::string>("result.to");
|
||||
|
||||
std::string cmp_sidechain_to = sidechain_to;
|
||||
std::transform(cmp_sidechain_to.begin(), cmp_sidechain_to.end(), cmp_sidechain_to.begin(), ::toupper);
|
||||
std::string cmp_wallet_contract_address = wallet_contract_address;
|
||||
std::transform(cmp_wallet_contract_address.begin(), cmp_wallet_contract_address.end(), cmp_wallet_contract_address.begin(), ::toupper);
|
||||
|
||||
//! Check whether it is ERC-20 token deposit
|
||||
std::string symbol;
|
||||
boost::multiprecision::uint256_t amount;
|
||||
bool error_in_deposit = false;
|
||||
const auto deposit_erc_20 = ethereum::deposit_erc20_decoder::decode(tx.get<std::string>("result.input"));
|
||||
if (deposit_erc_20.valid()) {
|
||||
std::string cmp_token = deposit_erc_20->token;
|
||||
std::transform(cmp_token.begin(), cmp_token.end(), cmp_token.begin(), ::tolower);
|
||||
|
||||
const auto it = erc20_addresses.right.find(cmp_token);
|
||||
if (it == erc20_addresses.right.end()) {
|
||||
wlog("No erc-20 token with address: ${address}", ("address", cmp_token));
|
||||
error_in_deposit = true;
|
||||
}
|
||||
symbol = it->second;
|
||||
amount = deposit_erc_20->amount;
|
||||
} else {
|
||||
symbol = "ETH";
|
||||
const std::string value_s = tx.get<std::string>("result.value");
|
||||
amount = boost::multiprecision::uint256_t{value_s};
|
||||
amount = amount / 100000;
|
||||
amount = amount / 100000;
|
||||
}
|
||||
|
||||
process_ok = (!error_in_deposit) &&
|
||||
(swdo_sidechain_from == sidechain_from) &&
|
||||
(cmp_sidechain_to == cmp_wallet_contract_address) &&
|
||||
(swdo_sidechain_currency == symbol) &&
|
||||
(swdo_sidechain_amount == fc::safe<uint64_t>{amount}.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
should_approve = process_ok;
|
||||
break;
|
||||
|
|
@ -331,23 +376,23 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po)
|
|||
case chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value: {
|
||||
bool process_ok = false;
|
||||
bool transaction_ok = false;
|
||||
son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get<son_wallet_withdraw_process_operation>().son_wallet_withdraw_id;
|
||||
const son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get<son_wallet_withdraw_process_operation>().son_wallet_withdraw_id;
|
||||
const auto &idx = database.get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
|
||||
const auto swwo = idx.find(swwo_id);
|
||||
if (swwo != idx.end()) {
|
||||
uint32_t swwo_block_num = swwo->block_num;
|
||||
std::string swwo_peerplays_transaction_id = swwo->peerplays_transaction_id;
|
||||
uint32_t swwo_op_idx = std::stoll(swwo->peerplays_uid.substr(swwo->peerplays_uid.find_last_of("-") + 1));
|
||||
const uint32_t swwo_block_num = swwo->block_num;
|
||||
const std::string swwo_peerplays_transaction_id = swwo->peerplays_transaction_id;
|
||||
const uint32_t swwo_op_idx = std::stoll(swwo->peerplays_uid.substr(swwo->peerplays_uid.find_last_of("-") + 1));
|
||||
|
||||
const auto &block = database.fetch_block_by_number(swwo_block_num);
|
||||
|
||||
for (const auto &tx : block->transactions) {
|
||||
if (tx.id().str() == swwo_peerplays_transaction_id) {
|
||||
operation op = tx.operations[swwo_op_idx];
|
||||
transfer_operation t_op = op.get<transfer_operation>();
|
||||
const operation op = tx.operations[swwo_op_idx];
|
||||
const transfer_operation t_op = op.get<transfer_operation>();
|
||||
|
||||
price asset_price = database.get<asset_object>(t_op.amount.asset_id).options.core_exchange_rate;
|
||||
asset peerplays_asset = asset(t_op.amount.amount * asset_price.base.amount / asset_price.quote.amount);
|
||||
const price asset_price = database.get<asset_object>(t_op.amount.asset_id).options.core_exchange_rate;
|
||||
const asset peerplays_asset = asset(t_op.amount.amount * asset_price.base.amount / asset_price.quote.amount);
|
||||
|
||||
process_ok = (t_op.to == gpo.parameters.son_account()) &&
|
||||
(swwo->peerplays_from == t_op.from) &&
|
||||
|
|
@ -356,8 +401,8 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po)
|
|||
}
|
||||
}
|
||||
|
||||
object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id;
|
||||
std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction;
|
||||
const object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id;
|
||||
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 = st_idx.find(object_id);
|
||||
|
|
@ -384,9 +429,9 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po)
|
|||
|
||||
case chain::operation::tag<chain::sidechain_transaction_sign_operation>::value: {
|
||||
should_approve = true;
|
||||
son_id_type signer = op_obj_idx_0.get<sidechain_transaction_sign_operation>().signer;
|
||||
std::string signature = op_obj_idx_0.get<sidechain_transaction_sign_operation>().signature;
|
||||
sidechain_transaction_id_type sidechain_transaction_id = op_obj_idx_0.get<sidechain_transaction_sign_operation>().sidechain_transaction_id;
|
||||
const son_id_type signer = op_obj_idx_0.get<sidechain_transaction_sign_operation>().signer;
|
||||
const std::string signature = op_obj_idx_0.get<sidechain_transaction_sign_operation>().signature;
|
||||
const sidechain_transaction_id_type sidechain_transaction_id = op_obj_idx_0.get<sidechain_transaction_sign_operation>().sidechain_transaction_id;
|
||||
const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_id>();
|
||||
const auto sto = st_idx.find(sidechain_transaction_id);
|
||||
if (sto == st_idx.end()) {
|
||||
|
|
@ -431,29 +476,33 @@ void sidechain_net_handler_ethereum::process_primary_wallet() {
|
|||
if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) ||
|
||||
(active_sw->addresses.at(sidechain).empty())) {
|
||||
|
||||
if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id)) {
|
||||
const auto ast = active_sidechain_types(database.head_block_time());
|
||||
const auto id = active_sw->id.instance() * ast.size() + std::distance(ast.begin(), ast.find(sidechain));
|
||||
const object_id_type op_id{active_sw->id.space(), active_sw->id.type(), id};
|
||||
|
||||
if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, op_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!plugin.can_son_participate(sidechain, chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id)) {
|
||||
if (!plugin.can_son_participate(sidechain, chain::operation::tag<chain::son_wallet_update_operation>::value, op_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account;
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
const uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
|
||||
|
||||
son_wallet_update_operation swu_op;
|
||||
swu_op.payer = gpo.parameters.son_account();
|
||||
swu_op.son_wallet_id = active_sw->id;
|
||||
swu_op.son_wallet_id = op_id;
|
||||
swu_op.sidechain = sidechain;
|
||||
swu_op.address = wallet_contract_address;
|
||||
proposal_op.proposed_ops.emplace_back(swu_op);
|
||||
|
||||
const auto signers = [this, &prev_sw, &active_sw, &swi] {
|
||||
std::vector<son_info> signers;
|
||||
std::vector<son_sidechain_info> signers;
|
||||
//! Check if we don't have any previous set of active SONs use the current one
|
||||
if (prev_sw != swi.rend()) {
|
||||
if (!prev_sw->sons.at(sidechain).empty())
|
||||
|
|
@ -467,14 +516,21 @@ void sidechain_net_handler_ethereum::process_primary_wallet() {
|
|||
return signers;
|
||||
}();
|
||||
|
||||
std::string tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), active_sw->id.operator std::string());
|
||||
std::string tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), op_id.operator std::string());
|
||||
if (!tx_str.empty()) {
|
||||
sidechain_transaction_create_operation stc_op;
|
||||
stc_op.payer = gpo.parameters.son_account();
|
||||
stc_op.object_id = active_sw->id;
|
||||
stc_op.object_id = op_id;
|
||||
stc_op.sidechain = sidechain;
|
||||
stc_op.transaction = tx_str;
|
||||
stc_op.signers = signers;
|
||||
for (const auto &signer : signers) {
|
||||
son_info si;
|
||||
si.son_id = signer.son_id;
|
||||
si.weight = signer.weight;
|
||||
si.signing_key = signer.signing_key;
|
||||
si.sidechain_public_keys[sidechain] = signer.public_key;
|
||||
stc_op.signers.emplace_back(std::move(si));
|
||||
}
|
||||
proposal_op.proposed_ops.emplace_back(stc_op);
|
||||
}
|
||||
|
||||
|
|
@ -484,7 +540,7 @@ void sidechain_net_handler_ethereum::process_primary_wallet() {
|
|||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
plugin.log_son_proposal_retry(sidechain, chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id);
|
||||
plugin.log_son_proposal_retry(sidechain, chain::operation::tag<chain::son_wallet_update_operation>::value, op_id);
|
||||
} catch (fc::exception &e) {
|
||||
elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what()));
|
||||
return;
|
||||
|
|
@ -497,14 +553,26 @@ void sidechain_net_handler_ethereum::process_sidechain_addresses() {
|
|||
}
|
||||
|
||||
bool sidechain_net_handler_ethereum::process_deposit(const son_wallet_deposit_object &swdo) {
|
||||
|
||||
if (proposal_exists(chain::operation::tag<chain::son_wallet_deposit_process_operation>::value, swdo.id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
|
||||
price asset_price = database.get<asset_object>(database.get_global_properties().parameters.eth_asset()).options.core_exchange_rate;
|
||||
asset asset_to_issue = asset(swdo.peerplays_asset.amount * asset_price.quote.amount / asset_price.base.amount, database.get_global_properties().parameters.eth_asset());
|
||||
const auto &assets_by_symbol = database.get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
const auto asset_itr = assets_by_symbol.find(swdo.sidechain_currency);
|
||||
if (asset_itr == assets_by_symbol.end()) {
|
||||
wlog("Could not find asset: ${symbol}", ("symbol", swdo.sidechain_currency));
|
||||
return false;
|
||||
}
|
||||
|
||||
const price asset_price = asset_itr->options.core_exchange_rate;
|
||||
const asset asset_to_issue = asset(swdo.peerplays_asset.amount * asset_price.quote.amount / asset_price.base.amount, asset_itr->get_id());
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account;
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
const uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
|
||||
|
||||
son_wallet_deposit_process_operation swdp_op;
|
||||
|
|
@ -547,7 +615,7 @@ bool sidechain_net_handler_ethereum::process_withdrawal(const son_wallet_withdra
|
|||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account;
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
const uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
|
||||
|
||||
son_wallet_withdraw_process_operation swwp_op;
|
||||
|
|
@ -560,7 +628,14 @@ bool sidechain_net_handler_ethereum::process_withdrawal(const son_wallet_withdra
|
|||
stc_op.object_id = swwo.id;
|
||||
stc_op.sidechain = sidechain;
|
||||
stc_op.transaction = tx_str;
|
||||
stc_op.signers = gpo.active_sons.at(sidechain);
|
||||
for (const auto &signer : gpo.active_sons.at(sidechain)) {
|
||||
son_info si;
|
||||
si.son_id = signer.son_id;
|
||||
si.weight = signer.weight;
|
||||
si.signing_key = signer.signing_key;
|
||||
si.sidechain_public_keys[sidechain] = signer.public_key;
|
||||
stc_op.signers.emplace_back(std::move(si));
|
||||
}
|
||||
proposal_op.proposed_ops.emplace_back(stc_op);
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op);
|
||||
|
|
@ -585,33 +660,70 @@ std::string sidechain_net_handler_ethereum::process_sidechain_transaction(const
|
|||
std::string sidechain_net_handler_ethereum::send_sidechain_transaction(const sidechain_transaction_object &sto) {
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::ptree pt_array;
|
||||
|
||||
std::vector<ethereum::encoded_sign_transaction> transactions;
|
||||
for (const auto &signature : sto.signatures) {
|
||||
const auto &transaction = signature.second;
|
||||
|
||||
//! Check if we have this signed transaction, if not, don't send it
|
||||
if (transaction.empty())
|
||||
if (signature.second.empty())
|
||||
continue;
|
||||
|
||||
ethereum::encoded_sign_transaction transaction{sto.transaction, ethereum::signature{signature.second}};
|
||||
transactions.emplace_back(transaction);
|
||||
}
|
||||
|
||||
const auto ¤t_son = plugin.get_current_son_object(sidechain);
|
||||
FC_ASSERT(current_son.sidechain_public_keys.contains(sidechain), "No public keys for current son: ${account_id}", ("account_id", current_son.son_account));
|
||||
const auto &public_key = current_son.sidechain_public_keys.at(sidechain);
|
||||
|
||||
const auto function_signature = ethereum::signature_encoder::get_function_signature_from_transaction(sto.transaction);
|
||||
if (function_signature.empty()) {
|
||||
elog("Function signature is empty for transaction id ${id}, transaction ${transaction}", ("id", sto.id)("transaction", sto.transaction));
|
||||
return std::string{}; //! Return empty string, as we have error in sending
|
||||
}
|
||||
|
||||
const ethereum::signature_encoder encoder{function_signature};
|
||||
#ifdef SEND_RAW_TRANSACTION
|
||||
const std::string sidechain_transaction = rpc_client->eth_send_raw_transaction(transaction);
|
||||
const auto data = encoder.encode(transactions);
|
||||
const std::string params = "[{\"from\":\"" + ethereum::add_0x(public_key) + "\", \"to\":\"" + wallet_contract_address + "\", \"data\":\"" + data + "\"}]";
|
||||
|
||||
ethereum::raw_transaction raw_tr;
|
||||
raw_tr.nonce = rpc_client->get_nonce(ethereum::add_0x(public_key));
|
||||
raw_tr.gas_price = rpc_client->get_gas_price();
|
||||
raw_tr.gas_limit = rpc_client->get_estimate_gas(params);
|
||||
if (raw_tr.gas_limit.empty())
|
||||
raw_tr.gas_limit = rpc_client->get_gas_limit();
|
||||
raw_tr.to = wallet_contract_address;
|
||||
raw_tr.value = "";
|
||||
raw_tr.data = data;
|
||||
raw_tr.chain_id = ethereum::add_0x(ethereum::to_hex(chain_id));
|
||||
|
||||
const auto sign_tr = raw_tr.sign(get_private_key(public_key));
|
||||
const std::string sidechain_transaction = rpc_client->eth_send_raw_transaction(sign_tr.serialize());
|
||||
#else
|
||||
const std::string sidechain_transaction = rpc_client->eth_send_transaction(transaction);
|
||||
ethereum::transaction raw_tr;
|
||||
raw_tr.data = encoder.encode(transactions);
|
||||
raw_tr.to = wallet_contract_address;
|
||||
raw_tr.from = ethereum::add_0x(public_key);
|
||||
|
||||
const auto sign_tr = raw_tr.sign(get_private_key(public_key));
|
||||
const std::string sidechain_transaction = rpc_client->eth_send_transaction(sign_tr.serialize());
|
||||
#endif
|
||||
|
||||
std::stringstream ss_tx(sidechain_transaction);
|
||||
boost::property_tree::ptree tx_json;
|
||||
boost::property_tree::read_json(ss_tx, tx_json);
|
||||
if (tx_json.count("result") && !tx_json.count("error")) {
|
||||
boost::property_tree::ptree node;
|
||||
node.put("transaction", transaction);
|
||||
node.put("transaction_receipt", tx_json.get<std::string>("result"));
|
||||
pt_array.push_back(std::make_pair("", node));
|
||||
} else {
|
||||
//! Fixme
|
||||
//! How should we proceed with error in eth_send_transaction
|
||||
elog("Error in eth_send_transaction for transaction ${id}, transaction ${transaction}", ("id", sto.id)("transaction", transaction));
|
||||
return std::string{}; //! Return empty string, as we have error in sending
|
||||
}
|
||||
std::stringstream ss_tx(sidechain_transaction);
|
||||
boost::property_tree::ptree tx_json;
|
||||
boost::property_tree::read_json(ss_tx, tx_json);
|
||||
if (tx_json.count("result") && !tx_json.count("error")) {
|
||||
boost::property_tree::ptree node;
|
||||
node.put("transaction", sto.transaction);
|
||||
node.put("sidechain_transaction", sidechain_transaction);
|
||||
node.put("transaction_receipt", tx_json.get<std::string>("result"));
|
||||
pt_array.push_back(std::make_pair("", node));
|
||||
} else {
|
||||
//! Fixme
|
||||
//! How should we proceed with error in eth_send_transaction
|
||||
elog("Error in eth send transaction for transaction id ${id}, transaction ${transaction}, sidechain_transaction ${sidechain_transaction}", ("id", sto.id)("transaction", sto.transaction)("sidechain_transaction", sidechain_transaction));
|
||||
return std::string{}; //! Return empty string, as we have error in sending
|
||||
}
|
||||
pt.add_child("result_array", pt_array);
|
||||
|
||||
|
|
@ -645,8 +757,8 @@ bool sidechain_net_handler_ethereum::settle_sidechain_transaction(const sidechai
|
|||
if ("0x1" == json_receipt.get<std::string>("result.status")) {
|
||||
count += 1;
|
||||
//! Fixme - compare data somehow?
|
||||
//if( sto.transaction == entry_receipt.second.get<std::string>("data") ) {
|
||||
//}
|
||||
// if( sto.transaction == entry_receipt.second.get<std::string>("data") ) {
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -654,8 +766,24 @@ bool sidechain_net_handler_ethereum::settle_sidechain_transaction(const sidechai
|
|||
if (count != json.get_child("result_array").size()) {
|
||||
wlog("Not all receipts received for transaction ${id}", ("id", sto.id));
|
||||
return false;
|
||||
} else
|
||||
} else {
|
||||
if (sto.object_id.is<son_wallet_id_type>()) {
|
||||
settle_amount = asset(0, database.get_global_properties().parameters.eth_asset());
|
||||
}
|
||||
|
||||
if (sto.object_id.is<son_wallet_withdraw_id_type>()) {
|
||||
auto swwo = database.get<son_wallet_withdraw_object>(sto.object_id);
|
||||
const auto &assets_by_symbol = database.get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
const auto asset_itr = assets_by_symbol.find(swwo.withdraw_currency);
|
||||
if (asset_itr == assets_by_symbol.end()) {
|
||||
wlog("Could not find asset: ${symbol}", ("symbol", swwo.withdraw_currency));
|
||||
return false;
|
||||
}
|
||||
settle_amount = asset(swwo.withdraw_amount, asset_itr->get_id());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -680,42 +808,41 @@ optional<asset> sidechain_net_handler_ethereum::estimate_withdrawal_transaction_
|
|||
return optional<asset>{};
|
||||
}
|
||||
|
||||
const auto &assets_by_symbol = database.get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
auto asset_itr = assets_by_symbol.find("ETH");
|
||||
if (asset_itr == assets_by_symbol.end()) {
|
||||
wlog("Could not find asset matching ETH");
|
||||
return optional<asset>{};
|
||||
}
|
||||
|
||||
const auto &public_key = son->sidechain_public_keys.at(sidechain);
|
||||
const ethereum::withdrawal_encoder encoder;
|
||||
const auto data = encoder.encode(public_key, 1 * 10000000000, son_wallet_withdraw_id_type{0}.operator object_id_type().operator std::string());
|
||||
const auto data = ethereum::withdrawal_encoder::encode(public_key, boost::multiprecision::uint256_t{1} * boost::multiprecision::uint256_t{10000000000}, "0");
|
||||
const std::string params = "[{\"from\":\"" + ethereum::add_0x(public_key) + "\", \"to\":\"" + wallet_contract_address + "\", \"data\":\"" + data + "\"}]";
|
||||
|
||||
const auto estimate_gas = ethereum::from_hex<int64_t>(rpc_client->get_estimate_gas(params));
|
||||
const auto gas_price = ethereum::from_hex<int64_t>(rpc_client->get_gas_price());
|
||||
const auto eth_gas_fee = double(estimate_gas * gas_price) / double{1000000000000000000};
|
||||
return asset_itr->amount_from_string(std::to_string(eth_gas_fee));
|
||||
|
||||
const auto asset = database.get<asset_object>(database.get_global_properties().parameters.eth_asset());
|
||||
return asset.amount_from_string(std::to_string(eth_gas_fee));
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(const std::vector<son_info> &son_pubkeys, const std::string &object_id) {
|
||||
std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(const std::vector<son_sidechain_info> &son_pubkeys, const std::string &object_id) {
|
||||
std::vector<std::pair<std::string, uint16_t>> owners_weights;
|
||||
for (auto &son : son_pubkeys) {
|
||||
const std::string pub_key_str = son.public_key;
|
||||
owners_weights.emplace_back(std::make_pair(pub_key_str, son.weight));
|
||||
}
|
||||
|
||||
const ethereum::update_owners_encoder encoder;
|
||||
return encoder.encode(owners_weights, object_id);
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_ethereum::create_deposit_transaction(const son_wallet_deposit_object &swdo) {
|
||||
return "Deposit-Transaction";
|
||||
return ethereum::update_owners_encoder::encode(owners_weights, object_id);
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_ethereum::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) {
|
||||
const ethereum::withdrawal_encoder encoder;
|
||||
return encoder.encode(swwo.withdraw_address.substr(2), swwo.withdraw_amount.value * 10000000000, swwo.id.operator std::string());
|
||||
if (swwo.withdraw_currency == "ETH") {
|
||||
return ethereum::withdrawal_encoder::encode(ethereum::remove_0x(swwo.withdraw_address), boost::multiprecision::uint256_t{swwo.withdraw_amount.value} * boost::multiprecision::uint256_t{10000000000}, swwo.id.operator std::string());
|
||||
} else {
|
||||
const auto it = erc20_addresses.left.find(swwo.withdraw_currency);
|
||||
if (it == erc20_addresses.left.end()) {
|
||||
elog("No erc-20 token: ${symbol}", ("symbol", swwo.withdraw_currency));
|
||||
return "";
|
||||
}
|
||||
return ethereum::withdrawal_erc20_encoder::encode(ethereum::remove_0x(it->second), ethereum::remove_0x(swwo.withdraw_address), boost::multiprecision::uint256_t{swwo.withdraw_amount.value}, swwo.id.operator std::string());
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_ethereum::sign_transaction(const sidechain_transaction_object &sto) {
|
||||
|
|
@ -724,32 +851,18 @@ std::string sidechain_net_handler_ethereum::sign_transaction(const sidechain_tra
|
|||
|
||||
const auto &public_key = current_son.sidechain_public_keys.at(sidechain);
|
||||
|
||||
#ifdef SEND_RAW_TRANSACTION
|
||||
ethereum::raw_transaction raw_tr;
|
||||
raw_tr.nonce = rpc_client->get_nonce(ethereum::add_0x(public_key));
|
||||
raw_tr.gas_price = rpc_client->get_gas_price();
|
||||
raw_tr.gas_limit = rpc_client->get_gas_limit();
|
||||
raw_tr.to = wallet_contract_address;
|
||||
raw_tr.value = "";
|
||||
raw_tr.data = sto.transaction;
|
||||
raw_tr.chain_id = ethereum::add_0x(ethereum::to_hex(chain_id));
|
||||
//! We need to change v value according to chain_id
|
||||
auto signature = ethereum::sign_hash(ethereum::keccak_hash(sto.transaction), ethereum::add_0x(ethereum::to_hex(chain_id)), get_private_key(public_key));
|
||||
signature.v = ethereum::to_hex(ethereum::from_hex<unsigned int>(signature.v) - 2 * chain_id - 35 + 27);
|
||||
|
||||
const auto sign_tr = raw_tr.sign(get_private_key(public_key));
|
||||
return sign_tr.serialize();
|
||||
#else
|
||||
ethereum::transaction sign_transaction;
|
||||
sign_transaction.data = sto.transaction;
|
||||
sign_transaction.to = wallet_contract_address;
|
||||
sign_transaction.from = "0x" + public_key;
|
||||
return sign_transaction.sign(get_private_key(public_key)).serialize();
|
||||
#endif
|
||||
return signature.serialize();
|
||||
}
|
||||
|
||||
void sidechain_net_handler_ethereum::schedule_ethereum_listener() {
|
||||
fc::time_point now = fc::time_point::now();
|
||||
int64_t time_to_next = 5000;
|
||||
const fc::time_point now = fc::time_point::now();
|
||||
const int64_t time_to_next = 5000;
|
||||
|
||||
fc::time_point next_wakeup(now + fc::milliseconds(time_to_next));
|
||||
const fc::time_point next_wakeup(now + fc::milliseconds(time_to_next));
|
||||
|
||||
_listener_task = fc::schedule([this] {
|
||||
ethereum_listener_loop();
|
||||
|
|
@ -761,10 +874,9 @@ void sidechain_net_handler_ethereum::ethereum_listener_loop() {
|
|||
schedule_ethereum_listener();
|
||||
|
||||
const auto reply = rpc_client->eth_blockNumber();
|
||||
//std::string reply = rpc_client->eth_get_logs(wallet_contract_address);
|
||||
|
||||
if (!reply.empty()) {
|
||||
uint64_t head_block_number = ethereum::from_hex<uint64_t>(reply);
|
||||
const uint64_t head_block_number = ethereum::from_hex<uint64_t>(reply);
|
||||
|
||||
if (head_block_number != last_block_received) {
|
||||
//! Check that current block number is greater than last one
|
||||
|
|
@ -799,11 +911,12 @@ void sidechain_net_handler_ethereum::handle_event(const std::string &block_numbe
|
|||
|
||||
size_t tx_idx = -1;
|
||||
for (const auto &tx_child : block_json.get_child("result.transactions")) {
|
||||
boost::property_tree::ptree tx = tx_child.second;
|
||||
const boost::property_tree::ptree tx = tx_child.second;
|
||||
tx_idx = tx_idx + 1;
|
||||
|
||||
const std::string to = tx.get<std::string>("to");
|
||||
std::string from = tx.get<std::string>("from");
|
||||
std::string to = tx.get<std::string>("to");
|
||||
std::transform(from.begin(), from.end(), from.begin(), ::tolower);
|
||||
|
||||
std::string cmp_to = to;
|
||||
std::transform(cmp_to.begin(), cmp_to.end(), cmp_to.begin(), ::toupper);
|
||||
|
|
@ -812,10 +925,35 @@ void sidechain_net_handler_ethereum::handle_event(const std::string &block_numbe
|
|||
|
||||
if (cmp_to == cmp_wallet_contract_address) {
|
||||
|
||||
std::string value_s = tx.get<std::string>("value");
|
||||
boost::multiprecision::uint256_t amount(value_s);
|
||||
amount = amount / 100000;
|
||||
amount = amount / 100000;
|
||||
//! Check whether it is ERC-20 token deposit
|
||||
std::string symbol;
|
||||
boost::multiprecision::uint256_t amount;
|
||||
const auto deposit_erc_20 = ethereum::deposit_erc20_decoder::decode(tx.get<std::string>("input"));
|
||||
if (deposit_erc_20.valid()) {
|
||||
std::string cmp_token = deposit_erc_20->token;
|
||||
std::transform(cmp_token.begin(), cmp_token.end(), cmp_token.begin(), ::tolower);
|
||||
|
||||
const auto it = erc20_addresses.right.find(cmp_token);
|
||||
if (it == erc20_addresses.right.end()) {
|
||||
wlog("No erc-20 token with address: ${address}", ("address", cmp_token));
|
||||
continue;
|
||||
}
|
||||
symbol = it->second;
|
||||
amount = deposit_erc_20->amount;
|
||||
} else {
|
||||
symbol = "ETH";
|
||||
const std::string value_s = tx.get<std::string>("value");
|
||||
amount = boost::multiprecision::uint256_t{value_s};
|
||||
amount = amount / 100000;
|
||||
amount = amount / 100000;
|
||||
}
|
||||
|
||||
const auto &assets_by_symbol = database.get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
const auto asset_itr = assets_by_symbol.find(symbol);
|
||||
if (asset_itr == assets_by_symbol.end()) {
|
||||
wlog("Could not find asset: ${symbol}", ("symbol", symbol));
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_address_and_expires>();
|
||||
const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, from, time_point_sec::maximum()));
|
||||
|
|
@ -826,22 +964,22 @@ void sidechain_net_handler_ethereum::handle_event(const std::string &block_numbe
|
|||
std::stringstream ss;
|
||||
ss << "ethereum"
|
||||
<< "-" << tx.get<std::string>("hash") << "-" << tx_idx;
|
||||
std::string sidechain_uid = ss.str();
|
||||
|
||||
sidechain_event_data sed;
|
||||
sed.timestamp = database.head_block_time();
|
||||
sed.block_num = database.head_block_num();
|
||||
sed.sidechain = sidechain;
|
||||
sed.sidechain_uid = sidechain_uid;
|
||||
sed.type = sidechain_event_type::deposit;
|
||||
sed.sidechain_uid = ss.str();
|
||||
sed.sidechain_transaction_id = tx.get<std::string>("hash");
|
||||
sed.sidechain_from = from;
|
||||
sed.sidechain_to = to;
|
||||
sed.sidechain_currency = "ETH";
|
||||
sed.sidechain_currency = symbol;
|
||||
sed.sidechain_amount = amount;
|
||||
sed.peerplays_from = addr_itr->sidechain_address_account;
|
||||
sed.peerplays_to = database.get_global_properties().parameters.son_account();
|
||||
price eth_price = database.get<asset_object>(database.get_global_properties().parameters.eth_asset()).options.core_exchange_rate;
|
||||
sed.peerplays_asset = asset(sed.sidechain_amount * eth_price.base.amount / eth_price.quote.amount);
|
||||
const price price = asset_itr->options.core_exchange_rate;
|
||||
sed.peerplays_asset = asset(sed.sidechain_amount * price.base.amount / price.quote.amount);
|
||||
|
||||
add_to_son_listener_log("TRX : " + sed.sidechain_transaction_id);
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/chain/son_sidechain_info.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/common/utils.hpp>
|
||||
#include <graphene/peerplays_sidechain/hive/asset.hpp>
|
||||
|
|
@ -30,8 +30,8 @@
|
|||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
hive_rpc_client::hive_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls) :
|
||||
rpc_client(url, user_name, password, debug_rpc_calls) {
|
||||
hive_rpc_client::hive_rpc_client(const std::vector<rpc_credentials> &credentials, bool debug_rpc_calls, bool simulate_connection_reselection) :
|
||||
rpc_client(sidechain_type::hive, credentials, debug_rpc_calls, simulate_connection_reselection) {
|
||||
}
|
||||
|
||||
std::string hive_rpc_client::account_history_api_get_transaction(std::string transaction_id) {
|
||||
|
|
@ -112,20 +112,34 @@ std::string hive_rpc_client::get_last_irreversible_block_num() {
|
|||
return retrieve_value_from_reply(reply_str, "last_irreversible_block_num");
|
||||
}
|
||||
|
||||
uint64_t hive_rpc_client::ping(rpc_connection &conn) const {
|
||||
const std::string reply = send_post_request(conn, "database_api.get_dynamic_global_properties", "", debug_rpc_calls);
|
||||
if (!reply.empty()) {
|
||||
std::stringstream ss(reply);
|
||||
boost::property_tree::ptree json;
|
||||
boost::property_tree::read_json(ss, json);
|
||||
if (json.count("result"))
|
||||
return json.get<uint64_t>("result.head_block_number");
|
||||
}
|
||||
return std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
sidechain_net_handler_hive::sidechain_net_handler_hive(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) :
|
||||
sidechain_net_handler(_plugin, options) {
|
||||
sidechain = sidechain_type::hive;
|
||||
sidechain_net_handler(sidechain_type::hive, _plugin, options) {
|
||||
|
||||
if (options.count("debug-rpc-calls")) {
|
||||
debug_rpc_calls = options.at("debug-rpc-calls").as<bool>();
|
||||
}
|
||||
bool simulate_connection_reselection = options.at("simulate-rpc-connection-reselection").as<bool>();
|
||||
|
||||
rpc_url = options.at("hive-node-rpc-url").as<std::string>();
|
||||
std::vector<std::string> rpc_urls = options.at("hive-node-rpc-url").as<std::vector<std::string>>();
|
||||
std::string rpc_user;
|
||||
if (options.count("hive-rpc-user")) {
|
||||
rpc_user = options.at("hive-rpc-user").as<std::string>();
|
||||
} else {
|
||||
rpc_user = "";
|
||||
}
|
||||
std::string rpc_password;
|
||||
if (options.count("hive-rpc-password")) {
|
||||
rpc_password = options.at("hive-rpc-password").as<std::string>();
|
||||
} else {
|
||||
|
|
@ -146,11 +160,20 @@ sidechain_net_handler_hive::sidechain_net_handler_hive(peerplays_sidechain_plugi
|
|||
}
|
||||
}
|
||||
|
||||
rpc_client = new hive_rpc_client(rpc_url, rpc_user, rpc_password, debug_rpc_calls);
|
||||
for (size_t i = 0; i < rpc_urls.size(); i++) {
|
||||
rpc_credentials creds;
|
||||
creds.url = rpc_urls[i];
|
||||
creds.user = rpc_user;
|
||||
creds.password = rpc_password;
|
||||
_rpc_credentials.push_back(creds);
|
||||
}
|
||||
FC_ASSERT(!_rpc_credentials.empty());
|
||||
|
||||
rpc_client = new hive_rpc_client(_rpc_credentials, debug_rpc_calls, simulate_connection_reselection);
|
||||
|
||||
const std::string chain_id_str = rpc_client->get_chain_id();
|
||||
if (chain_id_str.empty()) {
|
||||
elog("No Hive node running at ${url}", ("url", rpc_url));
|
||||
elog("No Hive node running at ${url}", ("url", _rpc_credentials[0].url));
|
||||
FC_ASSERT(false);
|
||||
}
|
||||
chain_id = chain_id_type(chain_id_str);
|
||||
|
|
@ -180,7 +203,8 @@ sidechain_net_handler_hive::~sidechain_net_handler_hive() {
|
|||
}
|
||||
|
||||
bool sidechain_net_handler_hive::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;
|
||||
|
||||
|
|
@ -209,12 +233,15 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) {
|
|||
bool address_ok = false;
|
||||
bool transaction_ok = false;
|
||||
son_wallet_id_type swo_id = op_obj_idx_0.get<son_wallet_update_operation>().son_wallet_id;
|
||||
const auto ast = active_sidechain_types(database.head_block_time());
|
||||
const auto id = (swo_id.instance.value - std::distance(ast.begin(), ast.find(sidechain))) / ast.size();
|
||||
const son_wallet_id_type op_id{id};
|
||||
const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
const auto swo = idx.find(swo_id);
|
||||
const auto swo = idx.find(op_id);
|
||||
if (swo != idx.end()) {
|
||||
|
||||
auto active_sons = gpo.active_sons.at(sidechain);
|
||||
vector<son_info> wallet_sons = swo->sons.at(sidechain);
|
||||
const auto &active_sons = gpo.active_sons.at(sidechain);
|
||||
const auto &wallet_sons = swo->sons.at(sidechain);
|
||||
|
||||
bool son_sets_equal = (active_sons.size() == wallet_sons.size());
|
||||
|
||||
|
|
@ -229,7 +256,9 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) {
|
|||
}
|
||||
|
||||
if (po.proposed_transaction.operations.size() >= 2) {
|
||||
object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id;
|
||||
const object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id;
|
||||
const auto id = (object_id.instance() - std::distance(ast.begin(), ast.find(sidechain))) / ast.size();
|
||||
const object_id_type obj_id{object_id.space(), object_id.type(), id};
|
||||
std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction;
|
||||
|
||||
const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_object_id>();
|
||||
|
|
@ -238,9 +267,9 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) {
|
|||
|
||||
std::string tx_str = "";
|
||||
|
||||
if (object_id.is<son_wallet_id_type>()) {
|
||||
if (obj_id.is<son_wallet_id_type>()) {
|
||||
const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
const auto swo = idx.find(object_id);
|
||||
const auto swo = idx.find(obj_id);
|
||||
if (swo != idx.end()) {
|
||||
|
||||
std::stringstream ss_trx(boost::algorithm::unhex(op_tx_str));
|
||||
|
|
@ -256,13 +285,14 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) {
|
|||
|
||||
const std::string memo_key = rpc_client->get_account_memo_key(wallet_account_name);
|
||||
|
||||
hive::authority active;
|
||||
active.weight_threshold = total_weight * 2 / 3 + 1;
|
||||
active.account_auths = account_auths;
|
||||
hive::authority a;
|
||||
a.weight_threshold = total_weight * 2 / 3 + 1;
|
||||
a.account_auths = account_auths;
|
||||
|
||||
hive::account_update_operation auo;
|
||||
auo.account = wallet_account_name;
|
||||
auo.active = active;
|
||||
auo.owner = a;
|
||||
auo.active = a;
|
||||
auo.memo_key = op_trx.operations[0].get<hive::account_update_operation>().memo_key;
|
||||
|
||||
hive::signed_transaction htrx;
|
||||
|
|
@ -485,7 +515,15 @@ void sidechain_net_handler_hive::process_primary_wallet() {
|
|||
if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) ||
|
||||
(active_sw->addresses.at(sidechain).empty())) {
|
||||
|
||||
if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id)) {
|
||||
const auto ast = active_sidechain_types(database.head_block_time());
|
||||
const auto id = active_sw->id.instance() * ast.size() + std::distance(ast.begin(), ast.find(sidechain));
|
||||
const object_id_type op_id{active_sw->id.space(), active_sw->id.type(), id};
|
||||
|
||||
if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, op_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!plugin.can_son_participate(sidechain, chain::operation::tag<chain::son_wallet_update_operation>::value, op_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -505,13 +543,14 @@ void sidechain_net_handler_hive::process_primary_wallet() {
|
|||
return;
|
||||
}
|
||||
|
||||
hive::authority active;
|
||||
active.weight_threshold = total_weight * 2 / 3 + 1;
|
||||
active.account_auths = account_auths;
|
||||
hive::authority a;
|
||||
a.weight_threshold = total_weight * 2 / 3 + 1;
|
||||
a.account_auths = account_auths;
|
||||
|
||||
hive::account_update_operation auo;
|
||||
auo.account = wallet_account_name;
|
||||
auo.active = active;
|
||||
auo.owner = a;
|
||||
auo.active = a;
|
||||
auo.memo_key = hive::public_key_type(memo_key);
|
||||
|
||||
const std::string block_id_str = rpc_client->get_head_block_id();
|
||||
|
|
@ -540,19 +579,40 @@ void sidechain_net_handler_hive::process_primary_wallet() {
|
|||
|
||||
son_wallet_update_operation swu_op;
|
||||
swu_op.payer = gpo.parameters.son_account();
|
||||
swu_op.son_wallet_id = active_sw->id;
|
||||
swu_op.son_wallet_id = op_id;
|
||||
swu_op.sidechain = sidechain;
|
||||
swu_op.address = wallet_account_name;
|
||||
|
||||
proposal_op.proposed_ops.emplace_back(swu_op);
|
||||
|
||||
const auto signers = [this, &prev_sw, &active_sw, &swi] {
|
||||
std::vector<son_sidechain_info> signers;
|
||||
//! Check if we don't have any previous set of active SONs use the current one
|
||||
if (prev_sw != swi.rend()) {
|
||||
if (!prev_sw->sons.at(sidechain).empty())
|
||||
signers = prev_sw->sons.at(sidechain);
|
||||
else
|
||||
signers = active_sw->sons.at(sidechain);
|
||||
} else {
|
||||
signers = active_sw->sons.at(sidechain);
|
||||
}
|
||||
|
||||
return signers;
|
||||
}();
|
||||
|
||||
sidechain_transaction_create_operation stc_op;
|
||||
stc_op.payer = gpo.parameters.son_account();
|
||||
stc_op.object_id = active_sw->id;
|
||||
stc_op.object_id = op_id;
|
||||
stc_op.sidechain = sidechain;
|
||||
stc_op.transaction = tx_str;
|
||||
stc_op.signers = gpo.active_sons.at(sidechain);
|
||||
|
||||
for (const auto &signer : signers) {
|
||||
son_info si;
|
||||
si.son_id = signer.son_id;
|
||||
si.weight = signer.weight;
|
||||
si.signing_key = signer.signing_key;
|
||||
si.sidechain_public_keys[sidechain] = signer.public_key;
|
||||
stc_op.signers.emplace_back(std::move(si));
|
||||
}
|
||||
proposal_op.proposed_ops.emplace_back(stc_op);
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op);
|
||||
|
|
@ -607,6 +667,11 @@ void sidechain_net_handler_hive::process_sidechain_addresses() {
|
|||
}
|
||||
|
||||
bool sidechain_net_handler_hive::process_deposit(const son_wallet_deposit_object &swdo) {
|
||||
|
||||
if (proposal_exists(chain::operation::tag<chain::son_wallet_deposit_process_operation>::value, swdo.id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
|
||||
price asset_price;
|
||||
|
|
@ -653,6 +718,11 @@ bool sidechain_net_handler_hive::process_deposit(const son_wallet_deposit_object
|
|||
}
|
||||
|
||||
bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_object &swwo) {
|
||||
|
||||
if (proposal_exists(chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value, swwo.id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
|
||||
//=====
|
||||
|
|
@ -708,7 +778,14 @@ bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_ob
|
|||
stc_op.object_id = swwo.id;
|
||||
stc_op.sidechain = sidechain;
|
||||
stc_op.transaction = tx_str;
|
||||
stc_op.signers = gpo.active_sons.at(sidechain);
|
||||
for (const auto &signer : gpo.active_sons.at(sidechain)) {
|
||||
son_info si;
|
||||
si.son_id = signer.son_id;
|
||||
si.weight = signer.weight;
|
||||
si.signing_key = signer.signing_key;
|
||||
si.sidechain_public_keys[sidechain] = signer.public_key;
|
||||
stc_op.signers.emplace_back(std::move(si));
|
||||
}
|
||||
proposal_op.proposed_ops.emplace_back(stc_op);
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op);
|
||||
|
|
@ -781,14 +858,14 @@ bool sidechain_net_handler_hive::settle_sidechain_transaction(const sidechain_tr
|
|||
boost::property_tree::ptree tx_json;
|
||||
boost::property_tree::read_json(ss_tx, tx_json);
|
||||
|
||||
//const chain::global_property_object &gpo = database.get_global_properties();
|
||||
// const chain::global_property_object &gpo = database.get_global_properties();
|
||||
|
||||
std::string tx_txid = tx_json.get<std::string>("result.transaction_id");
|
||||
uint32_t tx_block_num = tx_json.get<uint32_t>("result.block_num");
|
||||
const uint32_t last_irreversible_block = std::stoul(rpc_client->get_last_irreversible_block_num());
|
||||
|
||||
//std::string tx_address = addr.get_address();
|
||||
//int64_t tx_amount = -1;
|
||||
// std::string tx_address = addr.get_address();
|
||||
// int64_t tx_amount = -1;
|
||||
|
||||
if (tx_block_num <= last_irreversible_block) {
|
||||
if (sto.object_id.is<son_wallet_withdraw_id_type>()) {
|
||||
|
|
@ -841,15 +918,15 @@ void sidechain_net_handler_hive::hive_listener_loop() {
|
|||
}
|
||||
}
|
||||
|
||||
//std::string reply = rpc_client->get_last_irreversible_block_num();
|
||||
//if (!reply.empty()) {
|
||||
// uint64_t last_irreversible_block = std::stoul(reply);
|
||||
// if (last_irreversible_block != last_block_received) {
|
||||
// std::string event_data = std::to_string(last_irreversible_block);
|
||||
// handle_event(event_data);
|
||||
// last_block_received = last_irreversible_block;
|
||||
// }
|
||||
//}
|
||||
// std::string reply = rpc_client->get_last_irreversible_block_num();
|
||||
// if (!reply.empty()) {
|
||||
// uint64_t last_irreversible_block = std::stoul(reply);
|
||||
// if (last_irreversible_block != last_block_received) {
|
||||
// std::string event_data = std::to_string(last_irreversible_block);
|
||||
// handle_event(event_data);
|
||||
// last_block_received = last_irreversible_block;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void sidechain_net_handler_hive::handle_event(const std::string &event_data) {
|
||||
|
|
@ -883,7 +960,7 @@ void sidechain_net_handler_hive::handle_event(const std::string &event_data) {
|
|||
const auto &amount_child = op_value.get_child("amount");
|
||||
|
||||
uint64_t amount = amount_child.get<uint64_t>("amount");
|
||||
//uint64_t precision = amount_child.get<uint64_t>("precision");
|
||||
// uint64_t precision = amount_child.get<uint64_t>("precision");
|
||||
std::string nai = amount_child.get<std::string>("nai");
|
||||
std::string sidechain_currency = "";
|
||||
price sidechain_currency_price = {};
|
||||
|
|
@ -933,6 +1010,7 @@ void sidechain_net_handler_hive::handle_event(const std::string &event_data) {
|
|||
sed.timestamp = database.head_block_time();
|
||||
sed.block_num = database.head_block_num();
|
||||
sed.sidechain = sidechain;
|
||||
sed.type = sidechain_event_type::deposit;
|
||||
sed.sidechain_uid = sidechain_uid;
|
||||
sed.sidechain_transaction_id = transaction_id;
|
||||
sed.sidechain_from = from;
|
||||
|
|
|
|||
|
|
@ -23,17 +23,16 @@
|
|||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
sidechain_net_handler_peerplays::sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) :
|
||||
sidechain_net_handler(_plugin, options) {
|
||||
sidechain = sidechain_type::peerplays;
|
||||
//const auto &assets_by_symbol = database.get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
//const auto get_asset_id = [&assets_by_symbol](const string &symbol) {
|
||||
// auto asset_itr = assets_by_symbol.find(symbol);
|
||||
// FC_ASSERT(asset_itr != assets_by_symbol.end(), "Unable to find asset '${sym}'", ("sym", symbol));
|
||||
// return asset_itr->get_id();
|
||||
//};
|
||||
//tracked_assets.push_back(get_asset_id("PBTC"));
|
||||
//tracked_assets.push_back(get_asset_id("PETH"));
|
||||
//tracked_assets.push_back(get_asset_id("PEOS"));
|
||||
sidechain_net_handler(sidechain_type::peerplays, _plugin, options) {
|
||||
// const auto &assets_by_symbol = database.get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
// const auto get_asset_id = [&assets_by_symbol](const string &symbol) {
|
||||
// auto asset_itr = assets_by_symbol.find(symbol);
|
||||
// FC_ASSERT(asset_itr != assets_by_symbol.end(), "Unable to find asset '${sym}'", ("sym", symbol));
|
||||
// return asset_itr->get_id();
|
||||
// };
|
||||
// tracked_assets.push_back(get_asset_id("PBTC"));
|
||||
// tracked_assets.push_back(get_asset_id("PETH"));
|
||||
// tracked_assets.push_back(get_asset_id("PEOS"));
|
||||
|
||||
if (options.count("peerplays-private-key")) {
|
||||
const std::vector<std::string> pub_priv_keys = options["peerplays-private-key"].as<std::vector<std::string>>();
|
||||
|
|
@ -197,7 +196,14 @@ bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_o
|
|||
stc_op.object_id = swdo.id;
|
||||
stc_op.sidechain = sidechain;
|
||||
stc_op.transaction = tx_str;
|
||||
stc_op.signers = gpo.active_sons.at(sidechain);
|
||||
for (const auto &signer : gpo.active_sons.at(sidechain)) {
|
||||
son_info si;
|
||||
si.son_id = signer.son_id;
|
||||
si.weight = signer.weight;
|
||||
si.signing_key = signer.signing_key;
|
||||
si.sidechain_public_keys[sidechain] = signer.public_key;
|
||||
stc_op.signers.emplace_back(std::move(si));
|
||||
}
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account;
|
||||
|
|
@ -278,8 +284,8 @@ bool sidechain_net_handler_peerplays::settle_sidechain_transaction(const sidecha
|
|||
}
|
||||
|
||||
if (sto.object_id.is<son_wallet_deposit_id_type>()) {
|
||||
//auto swdo = database.get<son_wallet_deposit_object>(sto.object_id);
|
||||
//settle_amount = asset(swdo.sidechain_amount, swdo.sidechain_currency);
|
||||
// auto swdo = database.get<son_wallet_deposit_object>(sto.object_id);
|
||||
// settle_amount = asset(swdo.sidechain_amount, swdo.sidechain_currency);
|
||||
}
|
||||
|
||||
if (sto.object_id.is<son_wallet_withdraw_id_type>()) {
|
||||
|
|
|
|||
|
|
@ -191,17 +191,6 @@ struct worker_vote_delta
|
|||
flat_set<worker_id_type> vote_abstain;
|
||||
};
|
||||
|
||||
struct signed_block_with_info : public signed_block
|
||||
{
|
||||
signed_block_with_info();
|
||||
signed_block_with_info( const signed_block& block );
|
||||
signed_block_with_info( const signed_block_with_info& block ) = default;
|
||||
|
||||
block_id_type block_id;
|
||||
public_key_type signing_key;
|
||||
vector< transaction_id_type > transaction_ids;
|
||||
};
|
||||
|
||||
struct vesting_balance_object_with_info : public vesting_balance_object
|
||||
{
|
||||
vesting_balance_object_with_info();
|
||||
|
|
@ -276,7 +265,12 @@ class wallet_api
|
|||
* @param num height of the block to retrieve
|
||||
* @returns info about the block, or null if not found
|
||||
*/
|
||||
optional<signed_block_with_info> get_block( uint32_t num );
|
||||
optional<signed_block> get_block( uint32_t num );
|
||||
/** Returns info about a specified block, with some extra info.
|
||||
* @param num height of the block to retrieve
|
||||
* @returns info about the block, or null if not found
|
||||
*/
|
||||
optional<signed_block_with_info> get_block2( uint32_t num );
|
||||
/** Get signed blocks
|
||||
* @param block_num_from The lowest block number
|
||||
* @param block_num_to The highest block number
|
||||
|
|
@ -980,13 +974,13 @@ class wallet_api
|
|||
*
|
||||
* @return true if the label was set, otherwise false
|
||||
*/
|
||||
bool set_key_label( public_key_type, string label );
|
||||
bool set_key_label( public_key_type key, string label );
|
||||
|
||||
/** Get label of a public key.
|
||||
* @param key a public key
|
||||
* @return the label if already set by \c set_key_label(), or an empty string if not set
|
||||
*/
|
||||
string get_key_label( public_key_type )const;
|
||||
string get_key_label( public_key_type key )const;
|
||||
|
||||
/* Get the public key associated with a given label.
|
||||
* @param label a label
|
||||
|
|
@ -1565,14 +1559,14 @@ class wallet_api
|
|||
* @brief Get list of active sons
|
||||
* @return List of active SONs
|
||||
*/
|
||||
flat_map<sidechain_type, vector<son_info>> get_active_sons();
|
||||
flat_map<sidechain_type, vector<son_sidechain_info>> get_active_sons();
|
||||
|
||||
/**
|
||||
* @brief Get list of active sons
|
||||
* @param sidechain Sidechain type [bitcoin|ethereum|hive]
|
||||
* @return List of active SONs
|
||||
*/
|
||||
vector<son_info> get_active_sons_by_sidechain(sidechain_type sidechain);
|
||||
vector<son_sidechain_info> get_active_sons_by_sidechain(sidechain_type sidechain);
|
||||
|
||||
/**
|
||||
* @brief Get SON network status
|
||||
|
|
@ -2571,9 +2565,29 @@ class wallet_api
|
|||
|
||||
/**
|
||||
* @brief Returns all tokens
|
||||
* @param limit the maximum number of NFT objects to return (max: 100)
|
||||
* @param lower_id ID of the first NFT object to include in the list.
|
||||
* @return Returns vector of NFT objects, empty vector if none
|
||||
*/
|
||||
vector<nft_object> nft_get_all_tokens() const;
|
||||
vector<nft_object> nft_get_all_tokens(uint32_t limit, optional<nft_id_type> lower_id) const;
|
||||
|
||||
/**
|
||||
* @brief Returns all tokens owned by owner
|
||||
* @param owner NFT owner account ID
|
||||
* @param limit the maximum number of NFT objects to return (max: 100)
|
||||
* @param lower_id ID of the first NFT object to include in the list.
|
||||
* @return Returns vector of NFT objects, empty vector if none
|
||||
*/
|
||||
vector<nft_object> nft_get_tokens_by_owner(account_id_type owner, uint32_t limit, optional<nft_id_type> lower_id) const;
|
||||
|
||||
/**
|
||||
* @brief Returns all NFT metadata objects owned by owner
|
||||
* @param owner NFT owner account ID
|
||||
* @param limit the maximum number of NFT metadata objects to return (max: 100)
|
||||
* @param lower_id ID of the first NFT metadata object to include in the list.
|
||||
* @return Returns vector of NFT metadata objects, empty vector if none
|
||||
*/
|
||||
vector<nft_metadata_object> nft_get_metadata_by_owner(account_id_type owner, uint32_t limit, optional<nft_metadata_id_type> lower_id) const;
|
||||
signed_transaction nft_lottery_buy_ticket( nft_metadata_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy, bool broadcast );
|
||||
|
||||
signed_transaction create_offer(set<nft_id_type> item_ids,
|
||||
|
|
@ -2742,9 +2756,6 @@ FC_REFLECT( graphene::wallet::worker_vote_delta,
|
|||
(vote_abstain)
|
||||
)
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::wallet::signed_block_with_info, (graphene::chain::signed_block),
|
||||
(block_id)(signing_key)(transaction_ids) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::wallet::vesting_balance_object_with_info, (graphene::chain::vesting_balance_object),
|
||||
(allowed_withdraw)(allowed_withdraw_time) )
|
||||
|
||||
|
|
@ -2862,6 +2873,7 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(get_account)
|
||||
(get_account_id)
|
||||
(get_block)
|
||||
(get_block2)
|
||||
(get_blocks)
|
||||
(get_account_count)
|
||||
(get_account_history)
|
||||
|
|
@ -2943,6 +2955,8 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(nft_get_approved)
|
||||
(nft_is_approved_for_all)
|
||||
(nft_get_all_tokens)
|
||||
(nft_get_tokens_by_owner)
|
||||
(nft_get_metadata_by_owner)
|
||||
(nft_lottery_buy_ticket)
|
||||
(create_offer)
|
||||
(create_bid)
|
||||
|
|
|
|||
|
|
@ -2230,12 +2230,12 @@ public:
|
|||
return sign_transaction( tx, broadcast );
|
||||
} FC_CAPTURE_AND_RETHROW( (owner_account) ) }
|
||||
|
||||
flat_map<sidechain_type, vector<son_info>> get_active_sons()
|
||||
flat_map<sidechain_type, vector<son_sidechain_info>> get_active_sons()
|
||||
{ try {
|
||||
return _remote_db->get_active_sons();
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
vector<son_info> get_active_sons_by_sidechain(sidechain_type sidechain)
|
||||
vector<son_sidechain_info> get_active_sons_by_sidechain(sidechain_type sidechain)
|
||||
{ try {
|
||||
return _remote_db->get_active_sons_by_sidechain(sidechain);
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
|
@ -2773,21 +2773,34 @@ public:
|
|||
FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son));
|
||||
FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type");
|
||||
|
||||
bool update_vote_time = false;
|
||||
if (approve)
|
||||
{
|
||||
auto insert_result = voting_account_object.options.votes.insert(son_obj->get_sidechain_vote_id(sidechain));
|
||||
if (!insert_result.second)
|
||||
FC_THROW("Account ${account} has already voted for son ${son} for sidechain ${sidechain}", ("account", voting_account)("son", son)("sidechain", sidechain));
|
||||
FC_ASSERT(son_obj->get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", *son_obj));
|
||||
account_id_type stake_account = get_account_id(voting_account);
|
||||
const auto gpos_info = _remote_db->get_gpos_info(stake_account);
|
||||
const auto vesting_subperiod = _remote_db->get_global_properties().parameters.gpos_subperiod();
|
||||
const auto gpos_start_time = fc::time_point_sec(_remote_db->get_global_properties().parameters.gpos_period_start());
|
||||
const auto subperiod_start_time = gpos_start_time.sec_since_epoch() + (gpos_info.current_subperiod - 1) * vesting_subperiod;
|
||||
|
||||
auto insert_result = voting_account_object.options.votes.insert(*son_obj->get_sidechain_vote_id(sidechain));
|
||||
if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time))
|
||||
FC_THROW("Account ${account} was already voting for son ${son} in the current GPOS sub-period", ("account", voting_account)("son", son));
|
||||
else
|
||||
update_vote_time = true; //Allow user to vote in each sub-period(Update voting time, which is reference in calculating VF)
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->get_sidechain_vote_id(sidechain));
|
||||
FC_ASSERT(son_obj->get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", *son_obj));
|
||||
unsigned votes_removed = voting_account_object.options.votes.erase(*son_obj->get_sidechain_vote_id(sidechain));
|
||||
if (!votes_removed)
|
||||
FC_THROW("Account ${account} has already unvoted for son ${son} for sidechain ${sidechain}", ("account", voting_account)("son", son)("sidechain", sidechain));
|
||||
}
|
||||
|
||||
account_update_operation account_update_op;
|
||||
account_update_op.account = voting_account_object.id;
|
||||
account_update_op.new_options = voting_account_object.options;
|
||||
account_update_op.extensions.value.update_last_voting_time = update_vote_time;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( account_update_op );
|
||||
|
|
@ -2819,7 +2832,8 @@ public:
|
|||
FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son));
|
||||
FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type");
|
||||
|
||||
auto insert_result = voting_account_object.options.votes.insert(son_obj->get_sidechain_vote_id(sidechain));
|
||||
FC_ASSERT(son_obj->get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", *son_obj));
|
||||
auto insert_result = voting_account_object.options.votes.insert(*son_obj->get_sidechain_vote_id(sidechain));
|
||||
if (!insert_result.second)
|
||||
FC_THROW("Account ${account} was already voting for SON ${son}", ("account", voting_account)("son", son));
|
||||
}
|
||||
|
|
@ -2830,7 +2844,8 @@ public:
|
|||
FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son));
|
||||
FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type");
|
||||
|
||||
unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->get_sidechain_vote_id(sidechain));
|
||||
FC_ASSERT(son_obj->get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", *son_obj));
|
||||
unsigned votes_removed = voting_account_object.options.votes.erase(*son_obj->get_sidechain_vote_id(sidechain));
|
||||
if (!votes_removed)
|
||||
FC_THROW("Account ${account} is already not voting for SON ${son}", ("account", voting_account)("son", son));
|
||||
}
|
||||
|
|
@ -3349,7 +3364,7 @@ public:
|
|||
|
||||
if(transaction_fee) {
|
||||
if (*transaction_fee >= xfer_op.amount) {
|
||||
FC_THROW("Transaction fee: ${sidechain_fee}, would be grater than transfer amount ${amount}",
|
||||
FC_THROW("Transaction fee: ${sidechain_fee}, is greater than or equal to the transferred amount ${amount}",
|
||||
("sidechain_fee", get_asset(transaction_fee->asset_id).amount_to_pretty_string(transaction_fee->amount))("amount", get_asset(xfer_op.amount.asset_id).amount_to_pretty_string(xfer_op.amount.amount)));
|
||||
}
|
||||
}
|
||||
|
|
@ -4549,11 +4564,16 @@ bool wallet_api::copy_wallet_file(string destination_filename)
|
|||
return my->copy_wallet_file(destination_filename);
|
||||
}
|
||||
|
||||
optional<signed_block_with_info> wallet_api::get_block(uint32_t num)
|
||||
optional<signed_block> wallet_api::get_block(uint32_t num)
|
||||
{
|
||||
return my->_remote_db->get_block(num);
|
||||
}
|
||||
|
||||
optional<signed_block_with_info> wallet_api::get_block2(uint32_t num)
|
||||
{
|
||||
return my->_remote_db->get_block2(num);
|
||||
}
|
||||
|
||||
vector<optional<signed_block>> wallet_api::get_blocks(uint32_t block_num_from, uint32_t block_num_to) const
|
||||
{
|
||||
return my->_remote_db->get_blocks(block_num_from, block_num_to);
|
||||
|
|
@ -5289,12 +5309,12 @@ map<string, son_id_type> wallet_api::list_sons(const string& lowerbound, uint32_
|
|||
return my->_remote_db->lookup_son_accounts(lowerbound, limit);
|
||||
}
|
||||
|
||||
flat_map<sidechain_type, vector<son_info>> wallet_api::get_active_sons()
|
||||
flat_map<sidechain_type, vector<son_sidechain_info>> wallet_api::get_active_sons()
|
||||
{
|
||||
return my->get_active_sons();
|
||||
}
|
||||
|
||||
vector<son_info> wallet_api::get_active_sons_by_sidechain(sidechain_type sidechain)
|
||||
vector<son_sidechain_info> wallet_api::get_active_sons_by_sidechain(sidechain_type sidechain)
|
||||
{
|
||||
return my->get_active_sons_by_sidechain(sidechain);
|
||||
}
|
||||
|
|
@ -7027,9 +7047,28 @@ bool wallet_api::nft_is_approved_for_all(string owner_account_id_or_name, string
|
|||
return my->_remote_db->nft_is_approved_for_all(owner_account.id, operator_account.id);
|
||||
}
|
||||
|
||||
vector<nft_object> wallet_api::nft_get_all_tokens() const
|
||||
vector<nft_object> wallet_api::nft_get_all_tokens(uint32_t limit, optional<nft_id_type> lower_id) const
|
||||
{
|
||||
return my->_remote_db->nft_get_all_tokens();
|
||||
nft_id_type lb_id;
|
||||
if(lower_id)
|
||||
lb_id = *lower_id;
|
||||
return my->_remote_db->nft_get_all_tokens(lb_id, limit);
|
||||
}
|
||||
|
||||
vector<nft_object> wallet_api::nft_get_tokens_by_owner(account_id_type owner, uint32_t limit, optional<nft_id_type> lower_id) const
|
||||
{
|
||||
nft_id_type lb_id;
|
||||
if(lower_id)
|
||||
lb_id = *lower_id;
|
||||
return my->_remote_db->nft_get_tokens_by_owner(owner, lb_id, limit);
|
||||
}
|
||||
|
||||
vector<nft_metadata_object> wallet_api::nft_get_metadata_by_owner(account_id_type owner, uint32_t limit, optional<nft_metadata_id_type> lower_id) const
|
||||
{
|
||||
nft_metadata_id_type lb_id;
|
||||
if(lower_id)
|
||||
lb_id = *lower_id;
|
||||
return my->_remote_db->nft_get_metadata_by_owner(owner, lb_id, limit);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::nft_lottery_buy_ticket( nft_metadata_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy, bool broadcast )
|
||||
|
|
@ -7270,10 +7309,6 @@ vector<account_role_object> wallet_api::get_account_roles_by_owner(string owner_
|
|||
account_object owner_account = my->get_account(owner_account_id_or_name);
|
||||
return my->_remote_db->get_account_roles_by_owner(owner_account.id);
|
||||
}
|
||||
// default ctor necessary for FC_REFLECT
|
||||
signed_block_with_info::signed_block_with_info()
|
||||
{
|
||||
}
|
||||
|
||||
order_book wallet_api::get_order_book( const string& base, const string& quote, unsigned limit )
|
||||
{
|
||||
|
|
@ -7340,17 +7375,6 @@ std::string wallet_api::eth_estimate_withdrawal_transaction_fee() const
|
|||
return my->eth_estimate_withdrawal_transaction_fee();
|
||||
}
|
||||
|
||||
// default ctor necessary for FC_REFLECT
|
||||
signed_block_with_info::signed_block_with_info( const signed_block& block )
|
||||
: signed_block( block )
|
||||
{
|
||||
block_id = id();
|
||||
signing_key = signee();
|
||||
transaction_ids.reserve( transactions.size() );
|
||||
for( const processed_transaction& tx : transactions )
|
||||
transaction_ids.push_back( tx.id() );
|
||||
}
|
||||
|
||||
vesting_balance_object_with_info::vesting_balance_object_with_info()
|
||||
: vesting_balance_object()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ public:
|
|||
fixture_(fixture)
|
||||
{
|
||||
fixture_.init_nathan();
|
||||
fixture_.generate_blocks(HARDFORK_SON3_TIME);
|
||||
fixture_.generate_block();
|
||||
fixture_.generate_blocks(HARDFORK_SON_FOR_ETHEREUM_TIME);
|
||||
fixture_.generate_maintenance_block();
|
||||
}
|
||||
|
||||
void create_son(const std::string& account_name, const std::string& son_url,
|
||||
|
|
@ -143,9 +143,12 @@ BOOST_AUTO_TEST_CASE( create_sons )
|
|||
BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1");
|
||||
BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::hive], "hive account 1");
|
||||
BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 1");
|
||||
BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 22);
|
||||
BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 23);
|
||||
BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 24);
|
||||
BOOST_REQUIRE(son1_obj.get_sidechain_vote_id(sidechain_type::bitcoin));
|
||||
BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::bitcoin)->instance(), 22);
|
||||
BOOST_REQUIRE(son1_obj.get_sidechain_vote_id(sidechain_type::hive));
|
||||
BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::hive)->instance(), 23);
|
||||
BOOST_REQUIRE(son1_obj.get_sidechain_vote_id(sidechain_type::ethereum));
|
||||
BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::ethereum)->instance(), 24);
|
||||
|
||||
auto son2_obj = con.wallet_api_ptr->get_son("son2account");
|
||||
BOOST_CHECK(son2_obj.son_account == con.wallet_api_ptr->get_account_id("son2account"));
|
||||
|
|
@ -153,9 +156,12 @@ BOOST_AUTO_TEST_CASE( create_sons )
|
|||
BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 2");
|
||||
BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::hive], "hive account 2");
|
||||
BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 2");
|
||||
BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 25);
|
||||
BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 26);
|
||||
BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 27);
|
||||
BOOST_REQUIRE(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin));
|
||||
BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin)->instance(), 25);
|
||||
BOOST_REQUIRE(son2_obj.get_sidechain_vote_id(sidechain_type::hive));
|
||||
BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::hive)->instance(), 26);
|
||||
BOOST_REQUIRE(son2_obj.get_sidechain_vote_id(sidechain_type::ethereum));
|
||||
BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::ethereum)->instance(), 27);
|
||||
|
||||
} catch( fc::exception& e ) {
|
||||
BOOST_TEST_MESSAGE("SON cli wallet tests exception");
|
||||
|
|
@ -190,9 +196,12 @@ BOOST_AUTO_TEST_CASE( cli_update_son )
|
|||
BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1");
|
||||
BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::hive], "hive account 1");
|
||||
BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 1");
|
||||
BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 22);
|
||||
BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::hive).instance(), 23);
|
||||
BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 24);
|
||||
BOOST_REQUIRE(son_data.get_sidechain_vote_id(sidechain_type::bitcoin));
|
||||
BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::bitcoin)->instance(), 22);
|
||||
BOOST_REQUIRE(son_data.get_sidechain_vote_id(sidechain_type::hive));
|
||||
BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::hive)->instance(), 23);
|
||||
BOOST_REQUIRE(son_data.get_sidechain_vote_id(sidechain_type::ethereum));
|
||||
BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::ethereum)->instance(), 24);
|
||||
|
||||
// update SON
|
||||
sidechain_public_keys.clear();
|
||||
|
|
@ -731,6 +740,19 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test )
|
|||
sidechain_type::ethereum, 0, true);
|
||||
BOOST_CHECK(generate_maintenance_block());
|
||||
|
||||
// Vote for less SONs than num_son (2 votes, but num_son is 3)
|
||||
accepted.clear();
|
||||
rejected.clear();
|
||||
accepted.push_back("son1account");
|
||||
accepted.push_back("son2account");
|
||||
BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected,
|
||||
sidechain_type::bitcoin, 3, true), fc::exception);
|
||||
BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected,
|
||||
sidechain_type::hive, 3, true), fc::exception);
|
||||
BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected,
|
||||
sidechain_type::ethereum, 3, true), fc::exception);
|
||||
generate_block();
|
||||
|
||||
// Verify the votes
|
||||
son1_obj = con.wallet_api_ptr->get_son("son1account");
|
||||
son1_end_votes = son1_obj.total_votes;
|
||||
|
|
@ -1400,7 +1422,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture )
|
|||
|
||||
// Check Network Status Before sending Heartbeats
|
||||
BOOST_CHECK(generate_maintenance_block());
|
||||
for(sidechain_type sidechain:active_sidechain_types)
|
||||
for(sidechain_type sidechain : all_sidechain_types)
|
||||
{
|
||||
auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain);
|
||||
for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter)
|
||||
|
|
@ -1448,7 +1470,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture )
|
|||
generate_blocks(50);
|
||||
|
||||
BOOST_TEST_MESSAGE("Checking Network Status");
|
||||
for(sidechain_type sidechain:active_sidechain_types)
|
||||
for(sidechain_type sidechain : all_sidechain_types)
|
||||
{
|
||||
auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain);
|
||||
for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter)
|
||||
|
|
@ -1479,7 +1501,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture )
|
|||
con.wallet_api_ptr->sign_transaction(trx2, true);
|
||||
|
||||
generate_blocks(50);
|
||||
for(sidechain_type sidechain:active_sidechain_types)
|
||||
for(sidechain_type sidechain : all_sidechain_types)
|
||||
{
|
||||
auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain);
|
||||
for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter)
|
||||
|
|
@ -1507,7 +1529,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture )
|
|||
|
||||
generate_blocks(db->head_block_time() + gpo.parameters.son_heartbeat_frequency(), false);
|
||||
BOOST_TEST_MESSAGE("Checking Network Status");
|
||||
for(sidechain_type sidechain:active_sidechain_types)
|
||||
for(sidechain_type sidechain : all_sidechain_types)
|
||||
{
|
||||
auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain);
|
||||
for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter)
|
||||
|
|
@ -1535,7 +1557,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture )
|
|||
|
||||
generate_blocks(db->head_block_time() + gpo.parameters.son_heartbeat_frequency() + gpo.parameters.son_down_time(), false);;
|
||||
BOOST_TEST_MESSAGE("Checking Network Status");
|
||||
for(sidechain_type sidechain:active_sidechain_types)
|
||||
for(sidechain_type sidechain : all_sidechain_types)
|
||||
{
|
||||
auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain);
|
||||
for(map<son_id_type, string>::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
|
||||
#include <graphene/peerplays_sidechain/ethereum/decoders.hpp>
|
||||
#include <graphene/peerplays_sidechain/ethereum/transaction.hpp>
|
||||
|
||||
using namespace graphene::peerplays_sidechain::ethereum;
|
||||
|
|
@ -9,12 +10,43 @@ using namespace graphene::peerplays_sidechain::ethereum;
|
|||
BOOST_AUTO_TEST_SUITE(ethereum_transaction_tests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(withdrawal_encoder_test) {
|
||||
const withdrawal_encoder encoder;
|
||||
const auto tx = encoder.encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.0");
|
||||
BOOST_CHECK_EQUAL(tx, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E300000000000000000000000000000000000000000000000000000");
|
||||
const auto tx = withdrawal_encoder::encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.39.0");
|
||||
BOOST_CHECK_EQUAL(tx, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000");
|
||||
|
||||
const auto tx1 = encoder.encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.1");
|
||||
BOOST_CHECK_EQUAL(tx1, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000");
|
||||
const auto tx1 = withdrawal_encoder::encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.39.1");
|
||||
BOOST_CHECK_EQUAL(tx1, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33392E310000000000000000000000000000000000000000000000000000");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(withdrawal_signature_encoder_test) {
|
||||
encoded_sign_transaction transaction;
|
||||
transaction.data = "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000";
|
||||
transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" );
|
||||
|
||||
const auto function_signature = signature_encoder::get_function_signature_from_transaction(transaction.data);
|
||||
BOOST_REQUIRE_EQUAL(function_signature.empty(), false);
|
||||
const signature_encoder encoder{function_signature};
|
||||
const auto tx = encoder.encode({transaction});
|
||||
BOOST_CHECK_EQUAL(tx, "0xdaac6c810000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006689c3a93d7059430d19ff952900dfada310c0dcced9ed046c335f886091c7e50c1a01016a488777b41a1815ca01a7d809ed47c36dcb0d5f86a43b079ce0d04afe00000000000000000000000000000000000000000000000000000000000000a4e088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33392E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(withdrawal_erc20_encoder_test) {
|
||||
const auto tx = withdrawal_erc20_encoder::encode("cc806da9df9d634b5dac0aa36dca1e7780e42C60", "5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10, "1.39.0");
|
||||
BOOST_CHECK_EQUAL(tx, "0x483c0467000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42C600000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc12000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000");
|
||||
|
||||
const auto tx1 = withdrawal_erc20_encoder::encode("cc806da9df9d634b5dac0aa36dca1e7780e42C60", "5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10, "1.39.1");
|
||||
BOOST_CHECK_EQUAL(tx1, "0x483c0467000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42C600000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc12000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000006312E33392E310000000000000000000000000000000000000000000000000000");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(withdrawal_erc20_signature_encoder_test) {
|
||||
encoded_sign_transaction transaction;
|
||||
transaction.data = "0x483c0467000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42C600000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc12000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000";
|
||||
transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" );
|
||||
|
||||
const auto function_signature = signature_encoder::get_function_signature_from_transaction(transaction.data);
|
||||
BOOST_REQUIRE_EQUAL(function_signature.empty(), false);
|
||||
const signature_encoder encoder{function_signature};
|
||||
const auto tx = encoder.encode({transaction});
|
||||
BOOST_CHECK_EQUAL(tx, "0xd2bf286600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000066f51f7732435016936f0e21aa3c290023ea96ddbc369a957aca28a865cb5004a46675855fccd4bd5a283e1ff61aa60ca9b8b63664e770689e5cfc1a0c6bbdc79a00000000000000000000000000000000000000000000000000000000000000c4483c0467000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42C600000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc12000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000006312E33392E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(update_owners_encoder_test) {
|
||||
|
|
@ -22,13 +54,36 @@ BOOST_AUTO_TEST_CASE(update_owners_encoder_test) {
|
|||
owners_weights.emplace_back("5FbBb31BE52608D2F52247E8400B7fCaA9E0bC12", 1);
|
||||
owners_weights.emplace_back("76ce31bd03f601c3fc13732def921c5bac282676", 1);
|
||||
|
||||
const update_owners_encoder encoder;
|
||||
const auto tx = encoder.encode(owners_weights, "1.35.0");
|
||||
BOOST_CHECK_EQUAL(tx, "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000");
|
||||
const auto tx = update_owners_encoder::encode(owners_weights, "1.39.0");
|
||||
BOOST_CHECK_EQUAL(tx, "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000");
|
||||
|
||||
owners_weights.emplace_back("09ee460834498a4ee361beb819470061b7381b49", 1);
|
||||
const auto tx1 = encoder.encode(owners_weights, "1.36.1");
|
||||
BOOST_CHECK_EQUAL(tx1, "0x23ab6adf0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac282676000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009ee460834498a4ee361beb819470061b7381b4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000");
|
||||
const auto tx1 = update_owners_encoder::encode(owners_weights, "1.39.1");
|
||||
BOOST_CHECK_EQUAL(tx1, "0x23ab6adf0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac282676000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009ee460834498a4ee361beb819470061b7381b4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33392E310000000000000000000000000000000000000000000000000000");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(update_owners_signature_encoder_test) {
|
||||
encoded_sign_transaction transaction;
|
||||
transaction.data = "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000";
|
||||
transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" );
|
||||
|
||||
const auto function_signature = signature_encoder::get_function_signature_from_transaction(transaction.data);
|
||||
BOOST_REQUIRE_EQUAL(function_signature.empty(), false);
|
||||
const signature_encoder encoder{function_signature};
|
||||
const auto tx = encoder.encode({transaction});
|
||||
BOOST_CHECK_EQUAL(tx, "0x9d608673000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000667121da3c6ab5054b1a77cac477f24e7ce7bfcb5e3a857cfcaf48e67fc8f003ac38dfa8821525383608a68c9f215f9a2a232e192ae80079cd2f31b0e01caa6e1d000000000000000000000000000000000000000000000000000000000000012423ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33392E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(deposit_erc20_decoder_test) {
|
||||
const auto erc_20_1 = deposit_erc20_decoder::decode("0x97feb926000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42c600000000000000000000000000000000000000000000000000000000000000064");
|
||||
BOOST_REQUIRE_EQUAL(erc_20_1.valid(), true);
|
||||
BOOST_CHECK_EQUAL(erc_20_1->token, "0xcc806da9df9d634b5dac0aa36dca1e7780e42c60");
|
||||
BOOST_CHECK_EQUAL(erc_20_1->amount, 100);
|
||||
|
||||
const auto erc_20_2 = deposit_erc20_decoder::decode("0x97feb926000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42c600000000000000000000000000000000000000000000000006400000000000000");
|
||||
BOOST_REQUIRE_EQUAL(erc_20_2.valid(), true);
|
||||
BOOST_CHECK_EQUAL(erc_20_2->token, "0xcc806da9df9d634b5dac0aa36dca1e7780e42c60");
|
||||
BOOST_CHECK_EQUAL(erc_20_2->amount, 7205759403792793600);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(raw_transaction_serialization_test) {
|
||||
|
|
|
|||
|
|
@ -1038,7 +1038,7 @@ BOOST_FIXTURE_TEST_CASE( hardfork_son2_time, database_fixture )
|
|||
generate_block(); // get the maintenance skip slots out of the way*/
|
||||
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), 7);
|
||||
|
||||
generate_blocks(HARDFORK_SON3_TIME);
|
||||
generate_blocks(HARDFORK_SON_FOR_ETHEREUM_TIME);
|
||||
// after this hardfork maximum son account should not reset the value
|
||||
// on 7 after maintenance interval anymore. It must be HARDFORK_SON2_TIME
|
||||
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), GRAPHENE_DEFAULT_MAX_SONS);
|
||||
|
|
|
|||
|
|
@ -193,6 +193,34 @@ BOOST_AUTO_TEST_CASE(tickets_purchase_fail_test)
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(tickets_purchase_overflow)
|
||||
{
|
||||
try
|
||||
{
|
||||
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||
INVOKE(create_lottery_nft_md_test);
|
||||
auto &test_nft_md_obj = test_nft_md_id(db);
|
||||
|
||||
nft_lottery_token_purchase_operation tpo;
|
||||
tpo.fee = asset();
|
||||
tpo.buyer = account_id_type();
|
||||
tpo.lottery_id = test_nft_md_obj.id;
|
||||
tpo.tickets_to_buy = 9223372036854775800; // Large number so that the overall amount overflows
|
||||
trx.operations.push_back(tpo);
|
||||
BOOST_REQUIRE_THROW(PUSH_TX(db, trx, ~0), fc::overflow_exception);
|
||||
trx.operations.clear();
|
||||
|
||||
tpo.tickets_to_buy = -2; // Negative value should also be rejected
|
||||
trx.operations.push_back(tpo);
|
||||
BOOST_REQUIRE_THROW(PUSH_TX(db, trx, ~0), fc::exception);
|
||||
}
|
||||
catch (fc::exception &e)
|
||||
{
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(lottery_end_by_stage_test)
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -4,76 +4,204 @@
|
|||
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/nft_object.hpp>
|
||||
#include <graphene/app/database_api.hpp>
|
||||
|
||||
|
||||
using namespace graphene::chain;
|
||||
using namespace graphene::chain::test;
|
||||
|
||||
|
||||
class nft_test_helper
|
||||
{
|
||||
database_fixture& fixture_;
|
||||
|
||||
public:
|
||||
nft_test_helper(database_fixture& fixture):
|
||||
fixture_(fixture)
|
||||
{
|
||||
fixture_.generate_blocks(HARDFORK_NFT_TIME);
|
||||
fixture_.generate_block();
|
||||
fixture_.generate_block();
|
||||
set_expiration(fixture_.db, fixture_.trx);
|
||||
}
|
||||
|
||||
nft_metadata_object create_metadata(const std::string& name, const std::string& symbol, const std::string& uri, const account_id_type& owner, const fc::ecc::private_key &priv_key)
|
||||
{
|
||||
const auto& idx_by_id = fixture_.db.get_index_type<nft_metadata_index>().indices().get<by_id>();
|
||||
size_t obj_count0 = idx_by_id.size();
|
||||
|
||||
fixture_.generate_block();
|
||||
|
||||
signed_transaction trx;
|
||||
set_expiration(fixture_.db, trx);
|
||||
|
||||
nft_metadata_create_operation op;
|
||||
op.owner = owner;
|
||||
op.symbol = symbol;
|
||||
op.base_uri = uri;
|
||||
op.name = name;
|
||||
op.is_transferable = true;
|
||||
BOOST_CHECK_NO_THROW(op.validate());
|
||||
trx.operations.push_back(op);
|
||||
fixture_.sign(trx, priv_key);
|
||||
PUSH_TX(fixture_.db, trx, ~0);
|
||||
fixture_.generate_block();
|
||||
|
||||
BOOST_REQUIRE( idx_by_id.size() == obj_count0 + 1 ); // one more metadata created
|
||||
|
||||
const auto& idx_by_name = fixture_.db.get_index_type<nft_metadata_index>().indices().get<by_name>();
|
||||
auto obj = idx_by_name.find(name);
|
||||
BOOST_CHECK( obj->owner == owner );
|
||||
BOOST_CHECK( obj->name == name );
|
||||
BOOST_CHECK( obj->symbol == symbol );
|
||||
BOOST_CHECK( obj->base_uri == uri );
|
||||
return *obj;
|
||||
}
|
||||
|
||||
|
||||
nft_object mint(const nft_metadata_id_type& metadata, const account_id_type& owner, const account_id_type& payer,
|
||||
const fc::optional<account_id_type>& approved, const std::vector<account_id_type>& approved_operators,
|
||||
const fc::ecc::private_key &priv_key)
|
||||
{
|
||||
const auto& idx_by_id = fixture_.db.get_index_type<nft_index>().indices().get<by_id>();
|
||||
size_t obj_count0 = idx_by_id.size();
|
||||
|
||||
fixture_.generate_block();
|
||||
|
||||
signed_transaction trx;
|
||||
set_expiration(fixture_.db, trx);
|
||||
|
||||
nft_mint_operation op;
|
||||
op.nft_metadata_id = metadata;
|
||||
op.payer = payer;
|
||||
op.owner = owner;
|
||||
if (approved)
|
||||
op.approved = *approved;
|
||||
op.approved_operators = approved_operators;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
fixture_.sign(trx, priv_key);
|
||||
PUSH_TX(fixture_.db, trx, ~0);
|
||||
|
||||
fixture_.generate_block();
|
||||
|
||||
BOOST_REQUIRE(idx_by_id.size() == obj_count0 + 1); // one more created
|
||||
|
||||
auto obj = idx_by_id.rbegin();
|
||||
|
||||
BOOST_REQUIRE(obj != idx_by_id.rend());
|
||||
BOOST_CHECK(obj->owner == owner);
|
||||
BOOST_CHECK(obj->approved_operators.size() == approved_operators.size());
|
||||
BOOST_CHECK(obj->approved_operators == approved_operators);
|
||||
|
||||
return *obj;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( nft_tests, database_fixture )
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( nft_metadata_name_validation_test ) {
|
||||
BOOST_TEST_MESSAGE("nft_metadata_name_validation_test");
|
||||
ACTORS((mdowner));
|
||||
nft_metadata_create_operation op;
|
||||
op.owner = mdowner_id;
|
||||
op.symbol = "NFT";
|
||||
op.base_uri = "http://nft.example.com";
|
||||
op.name = "123";
|
||||
op.is_transferable = true;
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = "";
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = "1ab";
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = ".abc";
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = "abc.";
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = "ABC";
|
||||
BOOST_CHECK_NO_THROW(op.validate());
|
||||
op.name = "abcdefghijklmnopq";
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = "ab";
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = "***";
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = "a12";
|
||||
BOOST_CHECK_NO_THROW(op.validate());
|
||||
op.name = "a1b";
|
||||
BOOST_CHECK_NO_THROW(op.validate());
|
||||
op.name = "abc";
|
||||
BOOST_CHECK_NO_THROW(op.validate());
|
||||
op.name = "abc123defg12345";
|
||||
BOOST_CHECK_NO_THROW(op.validate());
|
||||
op.name = "NFT Test";
|
||||
BOOST_CHECK_NO_THROW(op.validate());
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( nft_metadata_create_test ) {
|
||||
|
||||
BOOST_TEST_MESSAGE("nft_metadata_create_test");
|
||||
generate_blocks(HARDFORK_NFT_TIME);
|
||||
generate_block();
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
nft_test_helper nfth(*this);
|
||||
ACTORS((mdowner));
|
||||
nfth.create_metadata("NFT Test", "NFT", "http://nft.example.com", mdowner_id, mdowner_private_key);
|
||||
}
|
||||
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
BOOST_AUTO_TEST_CASE( nft_metadata_listing_test ) {
|
||||
|
||||
BOOST_TEST_MESSAGE("nft_metadata_listing_test");
|
||||
|
||||
nft_test_helper nfth(*this);
|
||||
|
||||
ACTORS((mdowner1));
|
||||
ACTORS((mdowner2));
|
||||
|
||||
// prepare metadata set
|
||||
for (int i=0; i < 200; i++)
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send nft_metadata_create_operation");
|
||||
|
||||
nft_metadata_create_operation op;
|
||||
op.owner = mdowner_id;
|
||||
op.symbol = "NFT";
|
||||
op.base_uri = "http://nft.example.com";
|
||||
op.name = "123";
|
||||
op.is_transferable = true;
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = "";
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = "1ab";
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = ".abc";
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = "abc.";
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = "ABC";
|
||||
BOOST_CHECK_NO_THROW(op.validate());
|
||||
op.name = "abcdefghijklmnopq";
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = "ab";
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = "***";
|
||||
BOOST_CHECK_THROW(op.validate(), fc::exception);
|
||||
op.name = "a12";
|
||||
BOOST_CHECK_NO_THROW(op.validate());
|
||||
op.name = "a1b";
|
||||
BOOST_CHECK_NO_THROW(op.validate());
|
||||
op.name = "abc";
|
||||
BOOST_CHECK_NO_THROW(op.validate());
|
||||
op.name = "abc123defg12345";
|
||||
BOOST_CHECK_NO_THROW(op.validate());
|
||||
op.name = "NFT Test";
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, mdowner_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
string sfx = fc::to_pretty_string(i);
|
||||
nft_metadata_object md = nfth.create_metadata("NFT Test " + sfx, "NFT" + sfx, "http://nft.example.com", mdowner1_id, mdowner1_private_key);
|
||||
BOOST_REQUIRE(md.id == nft_metadata_id_type(i));
|
||||
}
|
||||
for (int i=200; i < 250; i++)
|
||||
{
|
||||
string sfx = fc::to_pretty_string(i);
|
||||
nft_metadata_object md = nfth.create_metadata("NFT Test " + sfx, "NFT" + sfx, "http://nft.example.com", mdowner2_id, mdowner2_private_key);
|
||||
BOOST_REQUIRE(md.id == nft_metadata_id_type(i));
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Check nft_metadata_create_operation results");
|
||||
graphene::app::database_api db_api(db);
|
||||
vector<nft_metadata_object> listed;
|
||||
|
||||
const auto& idx = db.get_index_type<nft_metadata_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.begin();
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_CHECK( obj->owner == mdowner_id );
|
||||
BOOST_CHECK( obj->name == "NFT Test" );
|
||||
BOOST_CHECK( obj->symbol == "NFT" );
|
||||
BOOST_CHECK( obj->base_uri == "http://nft.example.com" );
|
||||
// first 100 returned
|
||||
listed = db_api.nft_get_metadata_by_owner(mdowner1_id, nft_metadata_id_type(0), 100);
|
||||
BOOST_REQUIRE(listed.size() == 100);
|
||||
BOOST_REQUIRE(listed[ 0].id == nft_metadata_id_type( 0));
|
||||
BOOST_REQUIRE(listed[99].id == nft_metadata_id_type(99));
|
||||
|
||||
// 100 starting from 50
|
||||
listed = db_api.nft_get_metadata_by_owner(mdowner1_id, nft_metadata_id_type(50), 100);
|
||||
BOOST_REQUIRE(listed.size() == 100);
|
||||
BOOST_REQUIRE(listed[ 0].id == nft_metadata_id_type( 50));
|
||||
BOOST_REQUIRE(listed[99].id == nft_metadata_id_type(149));
|
||||
|
||||
// the last 5 must be returned
|
||||
listed = db_api.nft_get_metadata_by_owner(mdowner1_id, nft_metadata_id_type(195), 10);
|
||||
BOOST_REQUIRE(listed.size() == 5);
|
||||
BOOST_REQUIRE(listed[0].id == nft_metadata_id_type(195));
|
||||
BOOST_REQUIRE(listed[4].id == nft_metadata_id_type(199));
|
||||
|
||||
// too much requested at once
|
||||
BOOST_CHECK_THROW(db_api.nft_get_metadata_by_owner(mdowner1_id, nft_metadata_id_type(0), 101), fc::exception);
|
||||
|
||||
// the last 40 must be returned
|
||||
listed = db_api.nft_get_metadata_by_owner(mdowner2_id, nft_metadata_id_type(210), 100);
|
||||
BOOST_REQUIRE(listed.size() == 40);
|
||||
BOOST_REQUIRE(listed[ 0].id == nft_metadata_id_type(210));
|
||||
BOOST_REQUIRE(listed[39].id == nft_metadata_id_type(249));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -120,49 +248,112 @@ BOOST_AUTO_TEST_CASE( nft_mint_test ) {
|
|||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
INVOKE(nft_metadata_create_test);
|
||||
nft_test_helper nfth(*this);
|
||||
|
||||
ACTORS((mdowner));
|
||||
ACTORS((alice));
|
||||
ACTORS((bob));
|
||||
ACTORS((operator1));
|
||||
ACTORS((operator2));
|
||||
|
||||
GET_ACTOR(mdowner);
|
||||
nft_metadata_object md = nfth.create_metadata("NFT Test", "NFT", "http://nft.example.com", mdowner_id, mdowner_private_key);
|
||||
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
nfth.mint(md.id, alice_id, mdowner_id, alice_id, {operator1_id, operator2_id}, alice_private_key);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( nft_object_listing_test ) {
|
||||
|
||||
BOOST_TEST_MESSAGE("nft_object_listing_test");
|
||||
|
||||
nft_test_helper nfth(*this);
|
||||
|
||||
ACTORS((mdowner1));
|
||||
ACTORS((mdowner2));
|
||||
ACTORS((alice));
|
||||
ACTORS((bob));
|
||||
|
||||
nft_metadata_object md1 = nfth.create_metadata("NFT Test 1", "NFT1", "http://nft.example.com", mdowner1_id, mdowner1_private_key);
|
||||
nft_metadata_object md2 = nfth.create_metadata("NFT Test 2", "NFT2", "http://nft.example.com", mdowner2_id, mdowner2_private_key);
|
||||
|
||||
// create NFT objects: 200 owned by alice and 200 by bob
|
||||
for (int i=0; i < 200; i++)
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send nft_mint_operation");
|
||||
|
||||
const auto& idx = db.get_index_type<nft_metadata_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto nft_md_obj = idx.begin();
|
||||
|
||||
nft_mint_operation op;
|
||||
op.payer = mdowner_id;
|
||||
op.nft_metadata_id = nft_md_obj->id;
|
||||
op.owner = alice_id;
|
||||
op.approved = alice_id;
|
||||
op.approved_operators.push_back(operator1_id);
|
||||
op.approved_operators.push_back(operator2_id);
|
||||
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
nft_object nft = nfth.mint(md1.id, alice_id, mdowner1_id, alice_id, {}, alice_private_key);
|
||||
BOOST_REQUIRE(nft.id == nft_id_type(i));
|
||||
}
|
||||
for (int i=200; i < 250; i++)
|
||||
{
|
||||
nft_object nft = nfth.mint(md1.id, bob_id, mdowner1_id, bob_id, {}, bob_private_key);
|
||||
BOOST_REQUIRE(nft.id == nft_id_type(i));
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Check nft_mint_operation results");
|
||||
graphene::app::database_api db_api(db);
|
||||
vector<nft_object> listed;
|
||||
|
||||
const auto& idx = db.get_index_type<nft_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE( idx.size() == 1 );
|
||||
auto obj = idx.begin();
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_CHECK( obj->owner == alice_id );
|
||||
BOOST_CHECK( obj->approved_operators.size() == 2 );
|
||||
BOOST_CHECK( obj->approved_operators.at(0) == operator1_id );
|
||||
BOOST_CHECK( obj->approved_operators.at(1) == operator2_id );
|
||||
//
|
||||
// listing all tokens:
|
||||
//
|
||||
// first 100 returned, all alice's
|
||||
listed = db_api.nft_get_all_tokens(nft_id_type(0), 100);
|
||||
BOOST_REQUIRE(listed.size() == 100);
|
||||
BOOST_REQUIRE(listed[ 0].id == nft_id_type( 0));
|
||||
BOOST_REQUIRE(listed[99].id == nft_id_type(99));
|
||||
BOOST_REQUIRE(all_of(listed.begin(), listed.end(), [alice_id](const nft_object &obj){ return obj.owner == alice_id; }));
|
||||
|
||||
// 100 starting from 50, all alice's
|
||||
listed = db_api.nft_get_all_tokens(nft_id_type(50), 100);
|
||||
BOOST_REQUIRE(listed.size() == 100);
|
||||
BOOST_REQUIRE(listed[ 0].id == nft_id_type( 50));
|
||||
BOOST_REQUIRE(listed[99].id == nft_id_type(149));
|
||||
BOOST_REQUIRE(all_of(listed.begin(), listed.end(), [alice_id](const nft_object &obj){ return obj.owner == alice_id; }));
|
||||
|
||||
// the last 5 must be returned, all bob's
|
||||
listed = db_api.nft_get_all_tokens(nft_id_type(245), 10);
|
||||
BOOST_REQUIRE(listed.size() == 5);
|
||||
BOOST_REQUIRE(listed[0].id == nft_id_type(245));
|
||||
BOOST_REQUIRE(listed[4].id == nft_id_type(249));
|
||||
BOOST_REQUIRE(all_of(listed.begin(), listed.end(), [bob_id](const nft_object &obj){ return obj.owner == bob_id; }));
|
||||
|
||||
// 10 from the middle of the set, half alice's, half bob's
|
||||
listed = db_api.nft_get_all_tokens(nft_id_type(195), 10);
|
||||
BOOST_REQUIRE(listed.size() == 10);
|
||||
BOOST_REQUIRE(listed[0].id == nft_id_type(195));
|
||||
BOOST_REQUIRE(listed[9].id == nft_id_type(204));
|
||||
BOOST_REQUIRE(listed[0].owner == alice_id);
|
||||
BOOST_REQUIRE(listed[4].owner == alice_id);
|
||||
BOOST_REQUIRE(listed[5].owner == bob_id);
|
||||
BOOST_REQUIRE(listed[9].owner == bob_id);
|
||||
|
||||
// too much requested at once
|
||||
BOOST_CHECK_THROW(db_api.nft_get_all_tokens(nft_id_type(0), 101), fc::exception);
|
||||
|
||||
//
|
||||
// listing tokens by owner:
|
||||
//
|
||||
// first 100 alice's
|
||||
listed = db_api.nft_get_tokens_by_owner(alice_id, nft_id_type(0), 100);
|
||||
BOOST_REQUIRE(listed.size() == 100);
|
||||
BOOST_REQUIRE(all_of(listed.begin(), listed.end(), [alice_id](const nft_object &obj){ return obj.owner == alice_id; }));
|
||||
BOOST_REQUIRE(listed[ 0].id == nft_id_type( 0));
|
||||
BOOST_REQUIRE(listed[99].id == nft_id_type(99));
|
||||
|
||||
// the last 5 alice's must be returned
|
||||
listed = db_api.nft_get_tokens_by_owner(alice_id, nft_id_type(195), 10);
|
||||
BOOST_REQUIRE(listed.size() == 5);
|
||||
BOOST_REQUIRE(all_of(listed.begin(), listed.end(), [alice_id](const nft_object &obj){ return obj.owner == alice_id; }));
|
||||
BOOST_REQUIRE(listed[0].id == nft_id_type(195));
|
||||
BOOST_REQUIRE(listed[4].id == nft_id_type(199));
|
||||
|
||||
// all 50 bob's
|
||||
listed = db_api.nft_get_tokens_by_owner(bob_id, nft_id_type(0), 60);
|
||||
BOOST_REQUIRE(listed.size() == 50);
|
||||
BOOST_REQUIRE(all_of(listed.begin(), listed.end(), [bob_id](const nft_object &obj){ return obj.owner == bob_id; }));
|
||||
BOOST_REQUIRE(listed[ 0].id == nft_id_type(200));
|
||||
BOOST_REQUIRE(listed[49].id == nft_id_type(249));
|
||||
|
||||
// too much requested at once
|
||||
BOOST_CHECK_THROW(db_api.nft_get_tokens_by_owner(alice_id, nft_id_type(0), 101), fc::exception);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ using namespace graphene::chain::test;
|
|||
BOOST_FIXTURE_TEST_SUITE( son_operation_tests, database_fixture )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( create_son_test ) {
|
||||
generate_blocks(HARDFORK_SON_TIME);
|
||||
generate_blocks(HARDFORK_SON_FOR_ETHEREUM_TIME);
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
|
|
@ -314,7 +314,7 @@ BOOST_AUTO_TEST_CASE( son_pay_test )
|
|||
const dynamic_global_property_object& dpo = db.get_dynamic_global_properties();
|
||||
const auto block_interval = db.get_global_properties().parameters.block_interval;
|
||||
BOOST_CHECK( dpo.son_budget.value == 0);
|
||||
generate_blocks(HARDFORK_SON_TIME);
|
||||
generate_blocks(HARDFORK_SON_FOR_ETHEREUM_TIME);
|
||||
while (db.head_block_time() <= HARDFORK_SON_TIME) {
|
||||
generate_block();
|
||||
}
|
||||
|
|
@ -574,7 +574,7 @@ BOOST_AUTO_TEST_CASE( son_pay_test )
|
|||
BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::hive), 12);
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::ethereum), 18);
|
||||
// Check that Alice and Bob are paid for signing the transactions in the previous day/cycle
|
||||
BOOST_REQUIRE_EQUAL(db.get_balance(obj1->son_account, asset_id_type()).amount.value, 80+obj1_balance);
|
||||
BOOST_REQUIRE_EQUAL(db.get_balance(obj1->son_account, asset_id_type()).amount.value, 79+obj1_balance);
|
||||
BOOST_REQUIRE_EQUAL(db.get_balance(obj2->son_account, asset_id_type()).amount.value, 120+obj2_balance);
|
||||
// Check the SON Budget is again allocated after maintenance
|
||||
BOOST_CHECK( dpo.son_budget.value == 200);
|
||||
|
|
@ -634,16 +634,18 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
|||
// Modify SON's status to active
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.statuses[sidechain_type::bitcoin] = son_status::active;
|
||||
_s.statuses[sidechain_type::hive] = son_status::active;
|
||||
_s.statuses[sidechain_type::ethereum] = son_status::active;
|
||||
for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
|
||||
{
|
||||
_s.statuses[active_sidechain_type] = son_status::active;
|
||||
}
|
||||
});
|
||||
|
||||
db.modify( *son_stats_obj, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time());
|
||||
_s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time());
|
||||
_s.last_down_timestamp[sidechain_type::ethereum] = fc::time_point_sec(db.head_block_time());
|
||||
for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
|
||||
{
|
||||
_s.last_down_timestamp[active_sidechain_type] = fc::time_point_sec(db.head_block_time());
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
|
|
@ -660,9 +662,10 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
|||
PUSH_TX( db, trx, ~0);
|
||||
generate_block();
|
||||
trx.clear();
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance);
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::request_maintenance);
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::request_maintenance);
|
||||
for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
|
||||
{
|
||||
BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::request_maintenance);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -679,23 +682,26 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
|||
PUSH_TX( db, trx, ~0);
|
||||
generate_block();
|
||||
trx.clear();
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active);
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active);
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active);
|
||||
for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
|
||||
{
|
||||
BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::active);
|
||||
}
|
||||
}
|
||||
|
||||
// Modify SON's status to in_maintenance
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance;
|
||||
_s.statuses[sidechain_type::hive] = son_status::in_maintenance;
|
||||
_s.statuses[sidechain_type::ethereum] = son_status::in_maintenance;
|
||||
for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
|
||||
{
|
||||
_s.statuses[active_sidechain_type] = son_status::in_maintenance;
|
||||
}
|
||||
});
|
||||
|
||||
flat_map<sidechain_type, uint64_t> downtime;
|
||||
downtime[sidechain_type::bitcoin] = 0;
|
||||
downtime[sidechain_type::hive] = 0;
|
||||
downtime[sidechain_type::ethereum] = 0;
|
||||
for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
|
||||
{
|
||||
downtime[active_sidechain_type] = 0;
|
||||
}
|
||||
|
||||
{
|
||||
generate_block();
|
||||
|
|
@ -711,36 +717,34 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
|||
PUSH_TX( db, trx, ~0);
|
||||
generate_block();
|
||||
trx.clear();
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch());
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch());
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch());
|
||||
downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch();
|
||||
downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch();
|
||||
downtime[sidechain_type::ethereum] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch();
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::inactive);
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::inactive);
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::inactive);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts);
|
||||
|
||||
for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
|
||||
{
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(active_sidechain_type), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(active_sidechain_type).sec_since_epoch());
|
||||
downtime[active_sidechain_type] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(active_sidechain_type).sec_since_epoch();
|
||||
BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::inactive);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(active_sidechain_type) == op.ts);
|
||||
}
|
||||
}
|
||||
|
||||
// Modify SON's status to in_maintenance
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance;
|
||||
_s.statuses[sidechain_type::hive] = son_status::in_maintenance;
|
||||
_s.statuses[sidechain_type::ethereum] = son_status::in_maintenance;
|
||||
for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
|
||||
{
|
||||
_s.statuses[active_sidechain_type] = son_status::in_maintenance;
|
||||
}
|
||||
});
|
||||
|
||||
// SON is selected as one of the active SONs
|
||||
db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
{
|
||||
son_info son_inf;
|
||||
son_sidechain_info son_inf;
|
||||
son_inf.son_id = son_id_type(0);
|
||||
_gpo.active_sons[sidechain_type::bitcoin].push_back(son_inf);
|
||||
_gpo.active_sons[sidechain_type::hive].push_back(son_inf);
|
||||
_gpo.active_sons[sidechain_type::ethereum].push_back(son_inf);
|
||||
for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
|
||||
{
|
||||
_gpo.active_sons[active_sidechain_type].push_back(son_inf);
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
|
|
@ -758,18 +762,13 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
|||
generate_block();
|
||||
trx.clear();
|
||||
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch());
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch());
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), downtime.at(sidechain_type::ethereum) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch());
|
||||
downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch();
|
||||
downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch();
|
||||
downtime[sidechain_type::ethereum] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch();
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active);
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active);
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts);
|
||||
for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
|
||||
{
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(active_sidechain_type), downtime.at(active_sidechain_type) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(active_sidechain_type).sec_since_epoch());
|
||||
downtime[active_sidechain_type] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(active_sidechain_type).sec_since_epoch();
|
||||
BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::active);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(active_sidechain_type) == op.ts);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -786,15 +785,13 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
|||
PUSH_TX( db, trx, ~0);
|
||||
generate_block();
|
||||
trx.clear();
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin));
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive));
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), downtime.at(sidechain_type::ethereum));
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active);
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active);
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts);
|
||||
|
||||
for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
|
||||
{
|
||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(active_sidechain_type), downtime.at(active_sidechain_type));
|
||||
BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::active);
|
||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(active_sidechain_type) == op.ts);
|
||||
}
|
||||
}
|
||||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
|
@ -819,9 +816,10 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) {
|
|||
auto son_stats_obj = sidx.find( obj->statistics );
|
||||
BOOST_REQUIRE( son_stats_obj != sidx.end() );
|
||||
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active);
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active);
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active);
|
||||
for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
|
||||
{
|
||||
BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::active);
|
||||
}
|
||||
|
||||
{
|
||||
// Check that transaction fails if down_ts < last_active_timestamp
|
||||
|
|
@ -873,12 +871,11 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) {
|
|||
generate_block();
|
||||
trx.clear();
|
||||
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::in_maintenance);
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::in_maintenance);
|
||||
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::in_maintenance);
|
||||
BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin) == op.down_ts);
|
||||
BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::hive) == op.down_ts);
|
||||
BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum) == op.down_ts);
|
||||
for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time()))
|
||||
{
|
||||
BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::in_maintenance);
|
||||
BOOST_CHECK( son_stats_obj->last_down_timestamp.at(active_sidechain_type) == op.down_ts);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -160,8 +160,8 @@ BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) {
|
|||
si.son_id = son_id_type(0);
|
||||
si.weight = 1000;
|
||||
si.signing_key = alice_public_key;
|
||||
si.public_key = "";
|
||||
op.sons[sidechain_type::bitcoin].push_back(si);
|
||||
si.sidechain_public_keys[sidechain_type::bitcoin] = "";
|
||||
op.sons.push_back(si);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -169,8 +169,8 @@ BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) {
|
|||
si.son_id = son_id_type(1);
|
||||
si.weight = 1000;
|
||||
si.signing_key = bob_public_key;
|
||||
si.public_key = "";
|
||||
op.sons[sidechain_type::bitcoin].push_back(si);
|
||||
si.sidechain_public_keys[sidechain_type::bitcoin] = "";
|
||||
op.sons.push_back(si);
|
||||
}
|
||||
|
||||
trx.operations.push_back(op);
|
||||
|
|
|
|||
Loading…
Reference in a new issue