Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| efd3086af2 | |||
| 30f69ec758 | |||
| c673e984b0 | |||
| 26b8e59066 | |||
| b8b0e75513 | |||
| 472ccc3929 | |||
| b16ed1de3b | |||
| 167f9216be | |||
| 1a9de428cb | |||
| eaeaf4ab3d | |||
| cf24fe9cbf | |||
| d351840a70 | |||
| 78c6c77c00 | |||
| 46582601ea | |||
| cdb4156702 | |||
| 642653a37f |
@@ -10,17 +10,15 @@ RUN apt-get update && apt-get upgrade -y && \
|
||||
libcurl4-openssl-dev \
|
||||
libgmp-dev \
|
||||
libssl-dev \
|
||||
libusb-1.0-0-dev \
|
||||
llvm-7-dev \
|
||||
ninja-build \
|
||||
pkg-config \
|
||||
python3 \
|
||||
python3-numpy \
|
||||
python3-pip \
|
||||
software-properties-common \
|
||||
zlib1g-dev \
|
||||
zstd
|
||||
|
||||
RUN python3 -m pip install dataclasses
|
||||
|
||||
# GitHub's actions/checkout requires git 2.18+ but Ubuntu 18 only provides 2.17
|
||||
RUN add-apt-repository ppa:git-core/ppa && apt update && apt install -y git
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ RUN apt-get update && apt-get upgrade -y && \
|
||||
libcurl4-openssl-dev \
|
||||
libgmp-dev \
|
||||
libssl-dev \
|
||||
libusb-1.0-0-dev \
|
||||
llvm-11-dev \
|
||||
ninja-build \
|
||||
python3-numpy \
|
||||
pkg-config \
|
||||
zstd
|
||||
|
||||
@@ -10,7 +10,8 @@ RUN apt-get update && apt-get upgrade -y && \
|
||||
libcurl4-openssl-dev \
|
||||
libgmp-dev \
|
||||
libssl-dev \
|
||||
libusb-1.0-0-dev \
|
||||
llvm-11-dev \
|
||||
ninja-build \
|
||||
python3-numpy \
|
||||
pkg-config \
|
||||
zstd
|
||||
|
||||
@@ -12,7 +12,6 @@ jobs:
|
||||
label: triage
|
||||
org-project: 'Team Backlog'
|
||||
project-field: Status=Todo
|
||||
skip-if-existing-project: true
|
||||
secrets:
|
||||
token: ${{secrets.ENFCIBOT_REPO_AND_PROJECTS}}
|
||||
|
||||
|
||||
+2
-4
@@ -77,9 +77,8 @@ witness_node_data_dir
|
||||
|
||||
Testing/*
|
||||
build.tar.gz
|
||||
[Bb]uild*/*
|
||||
deps/*
|
||||
dependencies/*
|
||||
build/*
|
||||
build-debug/*
|
||||
|
||||
etc/eosio/node_*
|
||||
var/lib/node_*
|
||||
@@ -87,7 +86,6 @@ var/lib/node_*
|
||||
.idea/
|
||||
*.iws
|
||||
.DS_Store
|
||||
CMakeLists.txt.user
|
||||
|
||||
!*.swagger.*
|
||||
|
||||
|
||||
+9
-6
@@ -13,6 +13,9 @@
|
||||
[submodule "libraries/rapidjson"]
|
||||
path = libraries/rapidjson
|
||||
url = https://github.com/Tencent/rapidjson/
|
||||
[submodule "libraries/yubihsm"]
|
||||
path = libraries/yubihsm
|
||||
url = https://github.com/Yubico/yubihsm-shell
|
||||
[submodule "tests/abieos"]
|
||||
path = tests/abieos
|
||||
url = https://github.com/AntelopeIO/abieos
|
||||
@@ -22,9 +25,9 @@
|
||||
[submodule "libraries/libfc/secp256k1/secp256k1"]
|
||||
path = libraries/libfc/secp256k1/secp256k1
|
||||
url = https://github.com/bitcoin-core/secp256k1
|
||||
[submodule "libraries/libfc/libraries/bn256"]
|
||||
path = libraries/libfc/libraries/bn256
|
||||
url = https://github.com/AntelopeIO/bn256
|
||||
[submodule "libraries/cli11/cli11"]
|
||||
path = libraries/cli11/cli11
|
||||
url = https://github.com/AntelopeIO/CLI11.git
|
||||
[submodule "libraries/libfc/libraries/ff"]
|
||||
path = libraries/libfc/libraries/ff
|
||||
url = https://github.com/AntelopeIO/libff
|
||||
[submodule "libraries/prometheus-cpp"]
|
||||
path = libraries/prometheus-cpp
|
||||
url = https://github.com/jupp0r/prometheus-cpp.git
|
||||
|
||||
+10
-18
@@ -13,8 +13,8 @@ set( CMAKE_CXX_STANDARD 17 )
|
||||
set( CMAKE_CXX_EXTENSIONS ON )
|
||||
set( CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(VERSION_MAJOR 4)
|
||||
set(VERSION_MINOR 0)
|
||||
set(VERSION_MAJOR 3)
|
||||
set(VERSION_MINOR 2)
|
||||
set(VERSION_PATCH 0)
|
||||
set(VERSION_SUFFIX dev)
|
||||
|
||||
@@ -24,12 +24,6 @@ else()
|
||||
set(VERSION_FULL "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
|
||||
endif()
|
||||
|
||||
|
||||
# Options
|
||||
option(ENABLE_WERROR "Enable `-Werror` compilation flag." Off)
|
||||
option(ENABLE_WEXTRA "Enable `-Wextra` compilation flag." Off)
|
||||
|
||||
|
||||
set( CLI_CLIENT_EXECUTABLE_NAME cleos )
|
||||
set( NODE_EXECUTABLE_NAME nodeos )
|
||||
set( KEY_STORE_EXECUTABLE_NAME keosd )
|
||||
@@ -66,6 +60,8 @@ if(APPLE AND UNIX AND "${OPENSSL_ROOT_DIR}" STREQUAL "")
|
||||
set(OPENSSL_ROOT_DIR "/usr/local/opt/openssl@3;/usr/local/opt/openssl@1.1")
|
||||
endif()
|
||||
endif()
|
||||
# fc also adds these definitions to its public interface. once fc becomes the sole importer of openssl, this should be removed
|
||||
add_definitions(-DOPENSSL_API_COMPAT=0x10100000L -DOPENSSL_NO_DEPRECATED)
|
||||
|
||||
option(ENABLE_OC "Enable eosvm-oc on supported platforms" ON)
|
||||
|
||||
@@ -132,16 +128,6 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_WERROR)
|
||||
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror" )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror" )
|
||||
endif()
|
||||
if(ENABLE_WEXTRA)
|
||||
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra" )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra" )
|
||||
endif()
|
||||
|
||||
|
||||
option(EOSIO_ENABLE_DEVELOPER_OPTIONS "enable developer options for Leap" OFF)
|
||||
|
||||
# based on http://www.delorie.com/gnu/docs/gdb/gdb_70.html
|
||||
@@ -234,8 +220,11 @@ configure_file(libraries/softfloat/COPYING.txt licen
|
||||
configure_file(libraries/wasm-jit/LICENSE licenses/leap/LICENSE.wavm COPYONLY)
|
||||
configure_file(libraries/libfc/secp256k1/secp256k1/COPYING licenses/leap/LICENSE.secp256k1 COPYONLY)
|
||||
configure_file(libraries/libfc/include/fc/crypto/webauthn_json/license.txt licenses/leap/LICENSE.rapidjson COPYONLY)
|
||||
configure_file(libraries/libfc/src/network/LICENSE.go licenses/leap/LICENSE.go COPYONLY)
|
||||
configure_file(libraries/eos-vm/LICENSE licenses/leap/LICENSE.eos-vm COPYONLY)
|
||||
configure_file(libraries/libfc/libraries/ff/LICENSE licenses/leap/LICENSE.libff COPYONLY)
|
||||
configure_file(programs/cleos/LICENSE.CLI11 licenses/leap/LICENSE.CLI11 COPYONLY)
|
||||
configure_file(libraries/yubihsm/LICENSE licenses/leap/LICENSE.yubihsm COPYONLY)
|
||||
|
||||
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/licenses/leap" DESTINATION "${CMAKE_INSTALL_FULL_DATAROOTDIR}/licenses/" COMPONENT base)
|
||||
|
||||
@@ -254,6 +243,9 @@ add_custom_target(dev-install
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
get_property(_CTEST_CUSTOM_TESTS_IGNORE GLOBAL PROPERTY CTEST_CUSTOM_TESTS_IGNORE)
|
||||
file(WRITE "${CMAKE_BINARY_DIR}/CTestCustom.cmake" "SET(CTEST_CUSTOM_TESTS_IGNORE ${_CTEST_CUSTOM_TESTS_IGNORE})")
|
||||
|
||||
include(doxygen)
|
||||
|
||||
include(package.cmake)
|
||||
|
||||
@@ -56,8 +56,8 @@ find_library(libtester eosio_testing @CMAKE_INSTALL_FULL_LIBDIR@ NO_DEFAULT_PATH
|
||||
find_library(libchain eosio_chain @CMAKE_INSTALL_FULL_LIBDIR@ NO_DEFAULT_PATH)
|
||||
find_library(libfc fc @CMAKE_INSTALL_FULL_LIBDIR@ NO_DEFAULT_PATH)
|
||||
find_library(libsecp256k1 secp256k1 @CMAKE_INSTALL_FULL_LIBDIR@ NO_DEFAULT_PATH)
|
||||
find_library(libbn256 bn256 @CMAKE_INSTALL_FULL_LIBDIR@ NO_DEFAULT_PATH)
|
||||
|
||||
find_library(libff ff @CMAKE_INSTALL_FULL_LIBDIR@ NO_DEFAULT_PATH)
|
||||
find_library(libwasm WASM @CMAKE_INSTALL_FULL_LIBDIR@ NO_DEFAULT_PATH)
|
||||
find_library(libwast WAST @CMAKE_INSTALL_FULL_LIBDIR@ NO_DEFAULT_PATH)
|
||||
find_library(libir IR @CMAKE_INSTALL_FULL_LIBDIR@ NO_DEFAULT_PATH)
|
||||
@@ -98,7 +98,7 @@ macro(add_eosio_test_executable test_name)
|
||||
${libchainbase}
|
||||
${libbuiltins}
|
||||
${libsecp256k1}
|
||||
${libbn256}
|
||||
${libff}
|
||||
@GMP_LIBRARY@
|
||||
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
@@ -115,7 +115,7 @@ macro(add_eosio_test_executable test_name)
|
||||
${WRAP_MAIN}
|
||||
Threads::Threads
|
||||
)
|
||||
|
||||
|
||||
#adds -ltr. Ubuntu eosio.contracts build breaks without this
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_link_libraries(${test_name} ${LIBRT})
|
||||
|
||||
@@ -51,10 +51,10 @@ find_package(Boost @Boost_MAJOR_VERSION@.@Boost_MINOR_VERSION@ EXACT REQUIRED CO
|
||||
|
||||
find_library(libtester eosio_testing @CMAKE_BINARY_DIR@/libraries/testing NO_DEFAULT_PATH)
|
||||
find_library(libchain eosio_chain @CMAKE_BINARY_DIR@/libraries/chain NO_DEFAULT_PATH)
|
||||
find_library(libfc fc @CMAKE_BINARY_DIR@/libraries/libfc NO_DEFAULT_PATH)
|
||||
find_library(libfc fc @CMAKE_BINARY_DIR@/libraries/fc NO_DEFAULT_PATH)
|
||||
find_library(libsecp256k1 secp256k1 @CMAKE_BINARY_DIR@/libraries/libfc/secp256k1 NO_DEFAULT_PATH)
|
||||
find_library(libbn256 bn256 @CMAKE_BINARY_DIR@/libraries/libfc/libraries/bn256/src NO_DEFAULT_PATH)
|
||||
|
||||
find_library(libff ff @CMAKE_BINARY_DIR@/libraries/libfc/libraries/ff/libff NO_DEFAULT_PATH)
|
||||
find_library(libwasm WASM @CMAKE_BINARY_DIR@/libraries/wasm-jit/Source/WASM NO_DEFAULT_PATH)
|
||||
find_library(libwast WAST @CMAKE_BINARY_DIR@/libraries/wasm-jit/Source/WAST NO_DEFAULT_PATH)
|
||||
find_library(libir IR @CMAKE_BINARY_DIR@/libraries/wasm-jit/Source/IR NO_DEFAULT_PATH)
|
||||
@@ -95,7 +95,7 @@ macro(add_eosio_test_executable test_name)
|
||||
${libchainbase}
|
||||
${libbuiltins}
|
||||
${libsecp256k1}
|
||||
${libbn256}
|
||||
${libff}
|
||||
@GMP_LIBRARY@
|
||||
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
@@ -112,7 +112,7 @@ macro(add_eosio_test_executable test_name)
|
||||
${WRAP_MAIN}
|
||||
Threads::Threads
|
||||
)
|
||||
|
||||
|
||||
#adds -ltr. Ubuntu eosio.contracts build breaks without this
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_link_libraries(${test_name} ${LIBRT})
|
||||
|
||||
@@ -1,257 +1,126 @@
|
||||
# Leap
|
||||
Leap is a C++ implementation of the [Antelope](https://github.com/AntelopeIO) protocol. It contains blockchain node software and supporting tools for developers and node operators.
|
||||
|
||||
## Branches
|
||||
The `main` branch is the development branch; do not use it for production. Refer to the [release page](https://github.com/AntelopeIO/leap/releases) for current information on releases, pre-releases, and obsolete releases, as well as the corresponding tags for those releases.
|
||||
Leap is blockchain node software and supporting tools that implements the [Antelope](https://github.com/AntelopeIO) protocol.
|
||||
|
||||
## Supported Operating Systems
|
||||
We currently support the following operating systems.
|
||||
- Ubuntu 22.04 Jammy
|
||||
- Ubuntu 20.04 Focal
|
||||
- Ubuntu 18.04 Bionic
|
||||
## Repo Organization
|
||||
|
||||
Other Unix derivatives such as macOS are tended to on a best-effort basis and may not be full featured. If you aren't using Ubuntu, please visit the "[Build Unsupported OS](./docs/00_install/01_build-from-source/00_build-unsupported-os.md)" page to explore your options.
|
||||
`main` branch is the development branch: do not use this for production. Refer to the [release page](https://github.com/AntelopeIO/leap/releases) for current information on releases, pre-releases, and obsolete releases as well as the corresponding tags for those releases.
|
||||
|
||||
If you are running an unsupported Ubuntu derivative, such as Linux Mint, you can find the version of Ubuntu your distribution was based on by using this command:
|
||||
```bash
|
||||
cat /etc/upstream-release/lsb-release
|
||||
## Software Installation
|
||||
|
||||
Visit the [release page](https://github.com/AntelopeIO/leap/releases) for Ubuntu binaries. This is the fastest way to get started with the software.
|
||||
|
||||
### Building From Source
|
||||
|
||||
Recent Ubuntu LTS releases are the only Linux distributions that we fully support. Other Linux distros and other POSIX operating systems (such as macOS) are tended to on a best-effort basis and may not be full featured. Notable requirements to build are:
|
||||
* C++17 compiler and standard library
|
||||
* boost 1.67+
|
||||
* CMake 3.8+
|
||||
* (for Linux only) LLVM 7 - 11 (newer versions do not work)
|
||||
|
||||
A few other common libraries are tools also required such as openssl 1.1+, libcurl, curl, libusb, GMP, Python 3, and zlib.
|
||||
|
||||
**A Warning On Parallel Compilation Jobs (`-j` flag)**: When building C/C++ software often the build is performed in parallel via a command such as `make -j $(nproc)` which uses the number of CPU cores as the number of compilation jobs to perform simultaneously. However, be aware that some compilation units (.cpp files) in Leap are extremely complex and will consume nearly 4GB of memory to compile. You may need to reduce the level of parallelization depending on the amount of memory on your build host. e.g. instead of `make -j $(nproc)` run `make -j2`. Failures due to memory exhaustion will typically but not always manifest as compiler crashes.
|
||||
|
||||
Generally we recommend performing what we refer to as a "pinned build" which ensures the compiler and boost version remain the same between builds of different Leap versions (Leap requires these versions remain the same otherwise its state needs to be repopulated from a portable snapshot).
|
||||
|
||||
#### Building Pinned Build Binary Packages
|
||||
In the directory `<leap src>/scripts` you will find the two scripts `install_deps.sh` and `pinned_build.sh`. If you haven't installed build dependencies then run `install_deps.sh`. Then run `pinned_build.sh <dependencies directory> <leap build directory> <number of jobs>`.
|
||||
|
||||
The dependencies directory is where the script will pull the C++ dependencies that need to be built with the pinned compiler for building the pinned binaries for binary packaging.
|
||||
|
||||
The binary package will be produced in the Leap build directory that was supplied.
|
||||
|
||||
#### Manual (non "pinned") Build Instructions
|
||||
|
||||
<details>
|
||||
<summary>Ubuntu 20.04 & 22.04 Build Instructions</summary>
|
||||
|
||||
Install required dependencies:
|
||||
```
|
||||
Your best bet is to follow the instructions for your Ubuntu base, but we make no guarantees.
|
||||
|
||||
## Binary Installation
|
||||
This is the fastest way to get started. From the [latest release](https://github.com/AntelopeIO/leap/releases/latest) page, download a binary for one of our [supported operating systems](#supported-operating-systems), or visit the [release tags](https://github.com/AntelopeIO/leap/releases) page to download a binary for a specific version of Leap.
|
||||
|
||||
Once you have a `*.deb` file downloaded for your version of Ubuntu, you can install it as follows:
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ~/Downloads/leap*.deb
|
||||
apt-get update && apt-get install \
|
||||
build-essential \
|
||||
cmake \
|
||||
curl \
|
||||
git \
|
||||
libboost-all-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libgmp-dev \
|
||||
libssl-dev \
|
||||
libusb-1.0-0-dev \
|
||||
llvm-11-dev \
|
||||
pkg-config
|
||||
```
|
||||
Your download path may vary. If you are in an Ubuntu docker container, omit `sudo` because you run as `root` by default.
|
||||
|
||||
Finally, verify Leap was installed correctly:
|
||||
```bash
|
||||
nodeos --full-version
|
||||
and perform the build:
|
||||
```
|
||||
You should see a [semantic version](https://semver.org) string followed by a `git` commit hash with no errors. For example:
|
||||
```
|
||||
v3.1.2-0b64f879e3ebe2e4df09d2e62f1fc164cc1125d1
|
||||
```
|
||||
|
||||
## Build and Install from Source
|
||||
You can also build and install Leap from source.
|
||||
|
||||
### Prerequisites
|
||||
You will need to build on a [supported operating system](#supported-operating-systems).
|
||||
|
||||
Requirements to build:
|
||||
- C++17 compiler and standard library
|
||||
- boost 1.67+
|
||||
- CMake 3.8+
|
||||
- LLVM 7 - 11 - for Linux only
|
||||
- newer versions do not work
|
||||
- openssl 1.1+
|
||||
- curl
|
||||
- libcurl 7.40.0+
|
||||
- git
|
||||
- GMP
|
||||
- Python 3
|
||||
- zlib
|
||||
|
||||
### Step 1 - Clone
|
||||
If you don't have the Leap repo cloned to your computer yet, [open a terminal](https://itsfoss.com/open-terminal-ubuntu) and navigate to the folder where you want to clone the Leap repository:
|
||||
```bash
|
||||
cd ~/Downloads
|
||||
```
|
||||
Clone Leap using either HTTPS...
|
||||
```bash
|
||||
git clone --recursive https://github.com/AntelopeIO/leap.git
|
||||
```
|
||||
...or SSH:
|
||||
```bash
|
||||
git clone --recursive git@github.com:AntelopeIO/leap.git
|
||||
```
|
||||
|
||||
> ℹ️ **HTTPS vs. SSH Clone** ℹ️
|
||||
Both an HTTPS or SSH git clone will yield the same result - a folder named `leap` containing our source code. It doesn't matter which type you use.
|
||||
|
||||
Navigate into that folder:
|
||||
```bash
|
||||
cd leap
|
||||
```
|
||||
|
||||
### Step 2 - Checkout Release Tag or Branch
|
||||
Choose which [release](https://github.com/AntelopeIO/leap/releases) or [branch](#branches) you would like to build, then check it out. If you are not sure, use the [latest release](https://github.com/AntelopeIO/leap/releases/latest). For example, if you want to build release 3.1.2 then you would check it out using its tag, `v3.1.2`. In the example below, replace `v0.0.0` with your selected release tag accordingly:
|
||||
```bash
|
||||
git fetch --all --tags
|
||||
git checkout v0.0.0
|
||||
```
|
||||
|
||||
Once you are on the branch or release tag you want to build, make sure everything is up-to-date:
|
||||
```bash
|
||||
git pull
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
### Step 3 - Build
|
||||
Select build instructions below for a [pinned build](#pinned-build) (preferred) or an [unpinned build](#unpinned-build).
|
||||
|
||||
> ℹ️ **Pinned vs. Unpinned Build** ℹ️
|
||||
We have two types of builds for Leap: "pinned" and "unpinned." The only difference is that pinned builds use specific versions for some dependencies hand-picked by the Leap engineers - they are "pinned" to those versions. In contrast, unpinned builds use the default dependency versions available on the build system at the time. We recommend performing a "pinned" build to ensure the compiler and boost versions remain the same between builds of different Leap versions. Leap requires these versions to remain the same, otherwise its state might need to be recovered from a portable snapshot or the chain needs to be replayed.
|
||||
|
||||
> ⚠️ **A Warning On Parallel Compilation Jobs (`-j` flag)** ⚠️
|
||||
When building C/C++ software, often the build is performed in parallel via a command such as `make -j "$(nproc)"` which uses all available CPU threads. However, be aware that some compilation units (`*.cpp` files) in Leap will consume nearly 4GB of memory. Failures due to memory exhaustion will typically, but not always, manifest as compiler crashes. Using all available CPU threads may also prevent you from doing other things on your computer during compilation. For these reasons, consider reducing this value.
|
||||
|
||||
> 🐋 **Docker and `sudo`** 🐋
|
||||
If you are in an Ubuntu docker container, omit `sudo` from all commands because you run as `root` by default. Most other docker containers also exclude `sudo`, especially Debian-family containers. If your shell prompt is a hash tag (`#`), omit `sudo`.
|
||||
|
||||
#### Pinned Build
|
||||
Make sure you are in the root of the `leap` repo, then run the `install_depts.sh` script to install dependencies:
|
||||
```bash
|
||||
sudo scripts/install_deps.sh
|
||||
```
|
||||
|
||||
Next, run the pinned build script. You have to give it three arguments, in the following order:
|
||||
- A temporary folder, for all dependencies that need to be built from source.
|
||||
- A build folder, where the binaries you need to install will be built to.
|
||||
- The number of jobs or CPU cores/threads to use (note the [jobs flag](#step-3---build) warning above).
|
||||
|
||||
The following command runs the `pinned_build.sh` script, specifies a `deps` and `build` folder in the root of the Leap repo for the first two arguments, then builds the packages using all of your computer's CPU threads (Note: you don't need `sudo` for this command):
|
||||
```bash
|
||||
scripts/pinned_build.sh deps build "$(nproc)"
|
||||
```
|
||||
Now you can optionally [test](#step-4---test) your build, or [install](#step-5---install) the `*.deb` binary packages, which will be in the root of your build directory.
|
||||
|
||||
#### Unpinned Build
|
||||
The following instructions are valid for this branch. Other release branches may have different requirements, so ensure you follow the directions in the branch or release you intend to build. If you are in an Ubuntu docker container, omit `sudo` because you run as `root` by default.
|
||||
|
||||
<details> <summary>Ubuntu 22.04 Jammy & Ubuntu 20.04 Focal</summary>
|
||||
|
||||
Install dependencies:
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
cmake \
|
||||
curl \
|
||||
git \
|
||||
libboost-all-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libgmp-dev \
|
||||
libssl-dev \
|
||||
llvm-11-dev \
|
||||
python3-numpy
|
||||
```
|
||||
To build, make sure you are in the root of the `leap` repo, then run the following command:
|
||||
```bash
|
||||
mkdir -p build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/usr/lib/llvm-11 ..
|
||||
make -j "$(nproc)" package
|
||||
make -j $(nproc) package
|
||||
```
|
||||
</details>
|
||||
|
||||
<details> <summary>Ubuntu 18.04 Bionic</summary>
|
||||
<details>
|
||||
<summary>Ubuntu 18.04 Build Instructions</summary>
|
||||
|
||||
Install dependencies:
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
cmake \
|
||||
curl \
|
||||
g++-8 \
|
||||
git \
|
||||
libcurl4-openssl-dev \
|
||||
libgmp-dev \
|
||||
libssl-dev \
|
||||
llvm-7-dev \
|
||||
python3 \
|
||||
python3-numpy \
|
||||
python3-pip \
|
||||
Install required dependencies. You will need to build Boost from source on this distribution.
|
||||
```
|
||||
apt-get update && apt-get install \
|
||||
build-essential \
|
||||
cmake \
|
||||
curl \
|
||||
g++-8 \
|
||||
git \
|
||||
libcurl4-openssl-dev \
|
||||
libgmp-dev \
|
||||
libssl-dev \
|
||||
libusb-1.0-0-dev \
|
||||
llvm-7-dev \
|
||||
pkg-config \
|
||||
python3 \
|
||||
zlib1g-dev
|
||||
|
||||
python3 -m pip install dataclasses
|
||||
|
||||
curl -L https://boostorg.jfrog.io/artifactory/main/release/1.79.0/source/boost_1_79_0.tar.bz2 | tar jx && \
|
||||
cd boost_1_79_0 && \
|
||||
./bootstrap.sh --prefix=$HOME/boost1.79 && \
|
||||
./b2 --with-iostreams --with-date_time --with-filesystem --with-system \
|
||||
--with-program_options --with-chrono --with-test -j$(nproc) install && \
|
||||
cd ..
|
||||
```
|
||||
You need to build Boost from source on this distribution:
|
||||
```bash
|
||||
curl -fL https://boostorg.jfrog.io/artifactory/main/release/1.79.0/source/boost_1_79_0.tar.bz2 -o ~/Downloads/boost_1_79_0.tar.bz2
|
||||
tar -jvxf ~/Downloads/boost_1_79_0.tar.bz2 -C ~/Downloads/
|
||||
pushd ~/Downloads/boost_1_79_0
|
||||
./bootstrap.sh --prefix="$HOME/boost1.79"
|
||||
./b2 --with-iostreams --with-date_time --with-filesystem --with-system --with-program_options --with-chrono --with-test -j "$(nproc)" install
|
||||
popd
|
||||
and perform the build:
|
||||
```
|
||||
The Boost `*.tar.bz2` download and `boost_1_79_0` folder can be removed now if you want more space.
|
||||
```bash
|
||||
rm -r ~/Downloads/boost_1_79_0.tar.bz2 ~/Downloads/boost_1_79_0
|
||||
```
|
||||
From a terminal in the root of the `leap` repo, build.
|
||||
```bash
|
||||
mkdir -p build
|
||||
git submodule update --init --recursive
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 -DCMAKE_PREFIX_PATH="$HOME/boost1.79;/usr/lib/llvm-7/" -DCMAKE_BUILD_TYPE=Release ..
|
||||
make -j "$(nproc)" package
|
||||
cmake -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 \
|
||||
-DCMAKE_PREFIX_PATH="$HOME/boost1.79;/usr/lib/llvm-7/" -DCMAKE_BUILD_TYPE=Release .. \
|
||||
make -j $(nproc) package
|
||||
```
|
||||
After building, you may remove the `~/boost1.79` directory or you may keep it around for your next build.
|
||||
After building you may remove the `$HOME/boost1.79` directory, or you may keep it around until next time building the software.
|
||||
</details>
|
||||
|
||||
Now you can optionally [test](#step-4---test) your build, or [install](#step-5---install) the `*.deb` binary packages, which will be in the root of your build directory.
|
||||
### Running Tests
|
||||
|
||||
### Step 4 - Test
|
||||
Leap supports the following test suites:
|
||||
When building from source it's recommended to run at least what we refer to as the "parallelizable tests". Not included by default in the "parallelizable tests" are the WASM spec tests which can add additional coverage and can also be run in parallel.
|
||||
|
||||
Test Suite | Test Type | [Test Size](https://testing.googleblog.com/2010/12/test-sizes.html) | Notes
|
||||
---|:---:|:---:|---
|
||||
[Parallelizable tests](#parallelizable-tests) | Unit tests | Small
|
||||
[WASM spec tests](#wasm-spec-tests) | Unit tests | Small | Unit tests for our WASM runtime, each short but _very_ CPU-intensive
|
||||
[Serial tests](#serial-tests) | Component/Integration | Medium
|
||||
[Long-running tests](#long-running-tests) | Integration | Medium-to-Large | Tests which take an extraordinarily long amount of time to run
|
||||
```
|
||||
cd build
|
||||
|
||||
When building from source, we recommended running at least the [parallelizable tests](#parallelizable-tests).
|
||||
# "parallelizable tests": the minimum test set that should be run
|
||||
ctest -j $(nproc) -LE _tests
|
||||
|
||||
#### Parallelizable Tests
|
||||
This test suite consists of any test that does not require shared resources, such as file descriptors, specific folders, or ports, and can therefore be run concurrently in different threads without side effects (hence, easily parallelized). These are mostly unit tests and [small tests](https://testing.googleblog.com/2010/12/test-sizes.html) which complete in a short amount of time.
|
||||
|
||||
You can invoke them by running `ctest` from a terminal in your Leap build directory and specifying the following arguments:
|
||||
```bash
|
||||
ctest -j "$(nproc)" -LE _tests
|
||||
# Also consider running the WASM spec tests for more coverage
|
||||
ctest -j $(nproc) -L wasm_spec_tests
|
||||
```
|
||||
|
||||
#### WASM Spec Tests
|
||||
The WASM spec tests verify that our WASM execution engine is compliant with the web assembly standard. These are very [small](https://testing.googleblog.com/2010/12/test-sizes.html), very fast unit tests. However, there are over a thousand of them so the suite can take a little time to run. These tests are extremely CPU-intensive.
|
||||
|
||||
You can invoke them by running `ctest` from a terminal in your Leap build directory and specifying the following arguments:
|
||||
```bash
|
||||
ctest -j "$(nproc)" -L wasm_spec_tests
|
||||
Some other tests are available and recommended but be aware they can be sensitive to other software running on the same host and they may **SIGKILL** other nodeos instances running on the host.
|
||||
```
|
||||
We have observed severe performance issues when multiple virtual machines are running this test suite on the same physical host at the same time, for example in a CICD system. This can be resolved by disabling hyperthreading on the host.
|
||||
cd build
|
||||
|
||||
#### Serial Tests
|
||||
The serial test suite consists of [medium](https://testing.googleblog.com/2010/12/test-sizes.html) component or integration tests that use specific paths, ports, rely on process names, or similar, and cannot be run concurrently with other tests. Serial tests can be sensitive to other software running on the same host and they may `SIGKILL` other `nodeos` processes. These tests take a moderate amount of time to complete, but we recommend running them.
|
||||
|
||||
You can invoke them by running `ctest` from a terminal in your Leap build directory and specifying the following arguments:
|
||||
```bash
|
||||
# These tests can't run in parallel but are recommended.
|
||||
ctest -L "nonparallelizable_tests"
|
||||
```
|
||||
|
||||
#### Long-Running Tests
|
||||
The long-running tests are [medium-to-large](https://testing.googleblog.com/2010/12/test-sizes.html) integration tests that rely on shared resources and take a very long time to run.
|
||||
|
||||
You can invoke them by running `ctest` from a terminal in your Leap build directory and specifying the following arguments:
|
||||
```bash
|
||||
# These tests can't run in parallel. They also take a long time to run.
|
||||
ctest -L "long_running_tests"
|
||||
```
|
||||
|
||||
### Step 5 - Install
|
||||
Once you have [built](#step-3---build-the-source-code) Leap and [tested](#step-4---test) your build, you can install Leap on your system. Don't forget to omit `sudo` if you are running in a docker container.
|
||||
|
||||
We recommend installing the binary package you just built. Navigate to your Leap build directory in a terminal and run this command:
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ./leap[-_][0-9]*.deb
|
||||
```
|
||||
|
||||
It is also possible to install using `make` instead:
|
||||
```bash
|
||||
sudo make install
|
||||
```
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
file(GLOB BENCHMARK "*.cpp")
|
||||
add_executable( benchmark ${BENCHMARK} )
|
||||
|
||||
target_link_libraries( benchmark fc Boost::program_options bn256)
|
||||
target_link_libraries( benchmark fc Boost::program_options )
|
||||
target_include_directories( benchmark PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
|
||||
+36
-25
@@ -1,6 +1,6 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <bn256/bn256.h>
|
||||
#include <fc/crypto/alt_bn128.hpp>
|
||||
|
||||
#include <benchmark.hpp>
|
||||
|
||||
@@ -10,20 +10,22 @@ using bytes = std::vector<char>;
|
||||
using g1g2_pair = std::vector<std::string>;
|
||||
|
||||
void add_benchmarking() {
|
||||
const unsigned char point1[64] = {
|
||||
std::vector<unsigned char> point1_raw = {
|
||||
12,175,215,144,98,95,151,228,179,85,31,170,172,159,40,255,250,252,68,28,235,65,172,180,69,164,153,29,187,239,220,201, // x
|
||||
18,102,219,76,79,148,120,28,39,149,42,172,41,248,120,249,255,69,42,51,160,239,13,219,239,183,77,174,217,158,130,10,}; // y
|
||||
const unsigned char point2[64] = {
|
||||
std::vector<unsigned char> point2_raw = {
|
||||
12,144,10,32,104,85,103,36,222,232,48,152,108,217,40,145,230,48,8,54,0,7,134,164,7,10,139,110,95,205,124,121, // x
|
||||
22,254,176,251,18,168,78,220,142,100,102,113,58,176,83,186,212,62,154,138,235,135,34,46,237,117,54,36,198,40,79,73,}; // y
|
||||
|
||||
unsigned char ans[64] ={};
|
||||
|
||||
22,254,176,251,18,168,78,220,142,100,102,113,58,176,83,186,212,62,154,138,235,135,34,46,237,117,54,36,198,40,79,73,}; // y
|
||||
|
||||
bytes point1, point2;
|
||||
point1.insert(point1.begin(), point1_raw.begin(), point1_raw.end());
|
||||
point2.insert(point2.begin(), point2_raw.begin(), point2_raw.end());
|
||||
|
||||
auto f = [&]() {
|
||||
auto res = bn256::g1_add(point1, point2, ans);
|
||||
if (res == -1) {
|
||||
std::cout << "alt_bn128_add failed" << std::endl;
|
||||
auto res = fc::alt_bn128_add(point1, point2);
|
||||
if (std::holds_alternative<fc::alt_bn128_error>(res)) {
|
||||
std::cout << "alt_bn128_add failed: "
|
||||
<< (int)std::get<fc::alt_bn128_error>(res) << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -31,18 +33,21 @@ void add_benchmarking() {
|
||||
}
|
||||
|
||||
void mul_benchmarking() {
|
||||
const unsigned char point[64] = {
|
||||
std::vector<unsigned char> point_raw = {
|
||||
12,175,215,144,98,95,151,228,179,85,31,170,172,159,40,255,250,252,68,28,235,65,172,180,69,164,153,29,187,239,220,201, // x
|
||||
18,102,219,76,79,148,120,28,39,149,42,172,41,248,120,249,255,69,42,51,160,239,13,219,239,183,77,174,217,158,130,10,}; // y
|
||||
const unsigned char scalar[32] = {
|
||||
std::vector<unsigned char> scaler_raw = {
|
||||
25,62,182,170,104,140,135,90,37,150,0,77,2,77,146,71,54,101,113,69,177,216,157,4,229,213,33,215,169,99,150,91, }; // scaler size 256 bits
|
||||
|
||||
unsigned char ans[64] = {};
|
||||
|
||||
bytes point, scaler;
|
||||
point.insert(point.begin(), point_raw.begin(), point_raw.end());
|
||||
scaler.insert(scaler.begin(), scaler_raw.begin(), scaler_raw.end());
|
||||
|
||||
auto f = [&]() {
|
||||
auto res = bn256::g1_scalar_mul(point, scalar, ans);
|
||||
if (res == -1) {
|
||||
std::cout << "alt_bn128_mul failed" << std::endl;
|
||||
auto res = fc::alt_bn128_mul(point, scaler);
|
||||
if (std::holds_alternative<fc::alt_bn128_error>(res)) {
|
||||
std::cout << "alt_bn128_mul failed: "
|
||||
<< (int)std::get<fc::alt_bn128_error>(res) << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -50,7 +55,7 @@ void mul_benchmarking() {
|
||||
}
|
||||
|
||||
void pair_benchmarking() {
|
||||
const unsigned char g1_g2_pairs[] = {
|
||||
std::vector<unsigned char> g1_g2_pairs_raw = {
|
||||
/* pair 1 */
|
||||
12,175,215,144,98,95,151,228,179,85,31,170,172,159,40,255,250,252,68,28,235,65,172,180,69,164,153,29,187,239,220,201,18,102,219,76,79,148,120,28,39,149,42,172,41,248,120,249,255,69,42,51,160,239,13,219,239,183,77,174,217,158,130,10,
|
||||
46,24,234,176,148,82,198,136,44,30,157,18,217,25,49,241,63,197,2,151,14,150,136,210,114,4,74,124,145,225,131,139,13,60,0,50,236,103,39,15,150,226,246,189,209,113,0,46,151,39,41,6,87,119,228,213,45,225,29,234,161,110,43,87,3,237,74,227,93,23,171,129,49,118,228,173,154,111,168,220,161,46,140,103,155,56,168,207,254,90,228,76,188,232,25,34,44,102,159,35,193,216,177,31,131,247,226,19,170,222,26,227,86,37,81,149,202,144,188,126,120,168,9,195,32,169,147,161,
|
||||
@@ -82,21 +87,27 @@ void pair_benchmarking() {
|
||||
30,204,212,77,212,159,135,173,195,194,165,112,62,248,180,204,66,73,253,99,65,111,39,171,19,211,171,203,35,66,146,20,33,241,46,6,167,133,80,76,238,165,59,232,120,58,211,157,50,212,86,191,95,6,134,164,36,227,79,58,119,98,108,171,
|
||||
48,49,12,141,166,151,158,56,136,255,197,138,114,195,39,59,71,236,82,57,149,249,170,55,187,95,193,171,14,124,45,87,7,157,47,178,153,237,194,157,142,194,100,14,40,51,61,201,244,93,61,196,154,59,14,135,209,72,102,186,14,228,228,152,40,246,109,82,93,249,92,105,191,121,77,108,20,87,3,87,167,173,171,255,189,34,155,239,218,95,181,153,222,20,120,195,27,28,47,82,113,3,218,129,54,210,185,165,206,99,126,61,217,19,237,5,12,90,148,246,128,231,63,53,37,223,204,195
|
||||
};
|
||||
|
||||
|
||||
// benchmarking 1 pair of points
|
||||
bytes g1_g2_1_pair;
|
||||
g1_g2_1_pair.insert(g1_g2_1_pair.begin(), g1_g2_pairs_raw.begin(), g1_g2_pairs_raw.begin() + 384);
|
||||
auto f_1_pair = [&]() {
|
||||
auto res = bn256::pairing_check({ g1_g2_pairs, 384}, [](){});
|
||||
if (res == -1) {
|
||||
std::cout << "alt_bn128_pair 1 pair failed" << std::endl;
|
||||
auto res = fc::alt_bn128_pair(g1_g2_1_pair, [](){});
|
||||
if (std::holds_alternative<fc::alt_bn128_error>(res)) {
|
||||
std::cout << "alt_bn128_pair 1 pair failed: "
|
||||
<< (int)std::get<fc::alt_bn128_error>(res) << std::endl;
|
||||
}
|
||||
};
|
||||
benchmarking("alt_bn128_pair (1 pair)", f_1_pair);
|
||||
|
||||
// benchmarking 10 pair of points
|
||||
bytes g1_g2_10_pairs;
|
||||
g1_g2_10_pairs.insert(g1_g2_10_pairs.begin(), g1_g2_pairs_raw.begin(), g1_g2_pairs_raw.end());
|
||||
auto f_10_pairs = [&]() {
|
||||
auto res = bn256::pairing_check(g1_g2_pairs, [](){});
|
||||
if (res == -1) {
|
||||
std::cout << "alt_bn128_pair 10 pair failed" << std::endl;
|
||||
auto res = fc::alt_bn128_pair(g1_g2_10_pairs, [](){});
|
||||
if (std::holds_alternative<fc::alt_bn128_error>(res)) {
|
||||
std::cout << "alt_bn128_pair 10 pairs failed: "
|
||||
<< (int)std::get<fc::alt_bn128_error>(res) << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "eos",
|
||||
"generators": [
|
||||
{
|
||||
"name": "collate_markdown",
|
||||
"options": {
|
||||
"docs_dir": "docs"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "swagger",
|
||||
"options": {
|
||||
"swagger_path": "plugins/chain_api_plugin/chain.swagger.yaml",
|
||||
"swagger_dest_path": "nodeos/plugins/chain_api_plugin/api-reference",
|
||||
"disable_filters": true,
|
||||
"disable_summary_gen": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "swagger",
|
||||
"options": {
|
||||
"swagger_path": "plugins/db_size_api_plugin/db_size.swagger.yaml",
|
||||
"swagger_dest_path": "nodeos/plugins/db_size_api_plugin/api-reference",
|
||||
"disable_filters": true,
|
||||
"disable_summary_gen": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "swagger",
|
||||
"options": {
|
||||
"swagger_path": "plugins/producer_api_plugin/producer.swagger.yaml",
|
||||
"swagger_dest_path": "nodeos/plugins/producer_api_plugin/api-reference",
|
||||
"disable_filters": true,
|
||||
"disable_summary_gen": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "swagger",
|
||||
"options": {
|
||||
"swagger_path": "plugins/net_api_plugin/net.swagger.yaml",
|
||||
"swagger_dest_path": "nodeos/plugins/net_api_plugin/api-reference",
|
||||
"disable_filters": true,
|
||||
"disable_summary_gen": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "swagger",
|
||||
"options": {
|
||||
"swagger_path": "plugins/test_control_api_plugin/test_control.swagger.yaml",
|
||||
"swagger_dest_path": "nodeos/plugins/test_control_api_plugin/api-reference",
|
||||
"disable_filters": true,
|
||||
"disable_summary_gen": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "swagger",
|
||||
"options": {
|
||||
"swagger_path": "plugins/trace_api_plugin/trace_api.swagger.yaml",
|
||||
"swagger_dest_path": "nodeos/plugins/trace_api_plugin/api-reference",
|
||||
"disable_filters": true,
|
||||
"disable_summary_gen": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2,19 +2,123 @@
|
||||
content_title: Build Antelope from Source
|
||||
---
|
||||
|
||||
The shell scripts previously recommended for building the software have been removed in favor of a build process entirely driven by CMake. Those wishing to build from source are now responsible for installing the necessary dependencies. The list of dependencies and the recommended build procedure are in the [`README.md`](https://github.com/AntelopeIO/leap/blob/main/README.md) file. Instructions are also included for efficiently running the tests.
|
||||
The shell scripts previously recommended for building the software have been removed in favor of a build process entirely driven by CMake. Those wishing to build from source are now responsible for installing the necessary dependencies. The list of dependencies and the recommended build procedure are in the README.md file. Instructions are also included for efficiently running the tests.
|
||||
|
||||
### Using DUNE
|
||||
As an alternative to building from source, try [Docker Utilities for Node Execution](https://github.com/AntelopeIO/DUNE) for the easiest way to get started and for multi-platform support.
|
||||
|
||||
As an alternative to building from source try [Docker Utilities for Node Execution](https://github.com/AntelopeIO/DUNE) for the easiest way to get started, and for multi-platform support.
|
||||
|
||||
### Building From Source
|
||||
You can also build and install Leap from source. Instructions for that currently live [here](https://github.com/AntelopeIO/leap/blob/main/README.md#build-and-install-from-source).
|
||||
|
||||
Recent Ubuntu LTS releases are the only Linux distributions that we fully support. Other Linux distros and other POSIX operating systems (such as macOS) are tended to on a best-effort basis and may not be full featured. Notable requirements to build are:
|
||||
* C++17 compiler and standard library
|
||||
* boost 1.67+
|
||||
* CMake 3.8+
|
||||
* (for Linux only) LLVM 7 - 11 (newer versions do not work)
|
||||
|
||||
A few other common libraries are tools also required such as openssl 1.1+, libcurl, curl, libusb, GMP, Python 3, and zlib.
|
||||
|
||||
**A Warning On Parallel Compilation Jobs (`-j` flag)**: When building C/C++ software often the build is performed in parallel via a command such as `make -j $(nproc)` which uses the number of CPU cores as the number of compilation jobs to perform simultaneously. However, be aware that some compilation units (.cpp files) in mandel are extremely complex and will consume nearly 4GB of memory to compile. You may need to reduce the level of parallelization depending on the amount of memory on your build host. e.g. instead of `make -j $(nproc)` run `make -j2`. Failures due to memory exhaustion will typically but not always manifest as compiler crashes.
|
||||
|
||||
Generally we recommend performing what we refer to as a "pinned build" which ensures the compiler and boost version remain the same between builds of different mandel versions (mandel requires these versions remain the same otherwise its state needs to be repopulated from a portable snapshot).
|
||||
|
||||
#### Building Pinned Build Binary Packages
|
||||
The pinned build instructions have moved [here](https://github.com/AntelopeIO/leap/blob/main/README.md#pinned-build). You may want to look at the [prerequisites](https://github.com/AntelopeIO/leap/blob/main/README.md#prerequisites) and our warning on parallelization using the `-j` jobs flag [here](https://github.com/AntelopeIO/leap/blob/main/README.md#step-3---build) before you build.
|
||||
In the directory `<mandel src>/scripts` you will find the two scripts `install_deps.sh` and `pinned_build.sh`. If you haven't installed build dependencies then run `install_deps.sh`. Then run `pinned_build.sh <dependencies directory> <mandel build directory> <number of jobs>`.
|
||||
|
||||
The dependencies directory is where the script will pull the C++ dependencies that need to be built with the pinned compiler for building the pinned binaries for binary packaging.
|
||||
|
||||
The binary package will be produced in the mandel build directory that was supplied.
|
||||
|
||||
#### Manual (non "pinned") Build Instructions
|
||||
The unpinned build instructions have moved [here](https://github.com/AntelopeIO/leap/blob/main/README.md#unpinned-build). You may want to look at the [prerequisites](https://github.com/AntelopeIO/leap/blob/main/README.md#prerequisites) and our warning on parallelization using the `-j` jobs flag [here](https://github.com/AntelopeIO/leap/blob/main/README.md#step-3---build) before you build.
|
||||
|
||||
<details>
|
||||
<summary>Ubuntu 20.04 & 22.04 Build Instructions</summary>
|
||||
|
||||
Install required dependencies:
|
||||
```
|
||||
apt-get update && apt-get install \
|
||||
build-essential \
|
||||
cmake \
|
||||
curl \
|
||||
git \
|
||||
libboost-all-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libgmp-dev \
|
||||
libssl-dev \
|
||||
libusb-1.0-0-dev \
|
||||
llvm-11-dev \
|
||||
pkg-config
|
||||
```
|
||||
and perform the build:
|
||||
```
|
||||
git submodule update --init --recursive
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
make -j $(nproc) package
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Ubuntu 18.04 Build Instructions</summary>
|
||||
|
||||
Install required dependencies. You will need to build Boost from source on this distribution.
|
||||
```
|
||||
apt-get update && apt-get install \
|
||||
build-essential \
|
||||
cmake \
|
||||
curl \
|
||||
g++-8 \
|
||||
git \
|
||||
libcurl4-openssl-dev \
|
||||
libgmp-dev \
|
||||
libssl-dev \
|
||||
libusb-1.0-0-dev \
|
||||
llvm-7-dev \
|
||||
pkg-config \
|
||||
python3 \
|
||||
zlib1g-dev
|
||||
|
||||
curl -L https://boostorg.jfrog.io/artifactory/main/release/1.79.0/source/boost_1_79_0.tar.bz2 | tar jx && \
|
||||
cd boost_1_79_0 && \
|
||||
./bootstrap.sh --prefix=$HOME/boost1.79 && \
|
||||
./b2 --with-iostreams --with-date_time --with-filesystem --with-system \
|
||||
--with-program_options --with-chrono --with-test -j$(nproc) install && \
|
||||
cd ..
|
||||
```
|
||||
and perform the build:
|
||||
```
|
||||
git submodule update --init --recursive
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 \
|
||||
-DCMAKE_PREFIX_PATH="$HOME/boost1.79;/usr/lib/llvm-7/" -DCMAKE_BUILD_TYPE=Release .. \
|
||||
make -j $(nproc) package
|
||||
```
|
||||
After building you may remove the `$HOME/boost1.79` directory, or you may keep it around until next time building the software.
|
||||
</details>
|
||||
|
||||
### Running Tests
|
||||
Documentation on available test suites and how to run them has moved [here](https://github.com/AntelopeIO/leap/blob/main/README.md#test).
|
||||
|
||||
When building from source it's recommended to run at least what we refer to as the "parallelizable tests". Not included by default in the "parallelizable tests" are the WASM spec tests which can add additional coverage and can also be run in parallel.
|
||||
|
||||
```
|
||||
cd build
|
||||
|
||||
# "parallelizable tests": the minimum test set that should be run
|
||||
ctest -j $(nproc) -LE _tests
|
||||
|
||||
# Also consider running the WASM spec tests for more coverage
|
||||
ctest -j $(nproc) -L wasm_spec_tests
|
||||
```
|
||||
|
||||
Some other tests are available and recommended but be aware they can be sensitive to other software running on the same host and they may **SIGKILL** other nodeos instances running on the host.
|
||||
```
|
||||
cd build
|
||||
|
||||
# These tests can't run in parallel but are recommended.
|
||||
ctest -L "nonparallelizable_tests"
|
||||
|
||||
# These tests can't run in parallel. They also take a long time to run.
|
||||
ctest -L "long_running_tests"
|
||||
```
|
||||
|
||||
@@ -10,9 +10,9 @@ The best way to install and use the Antelope software is to build it from source
|
||||
|
||||
Antelope currently supports the following operating systems:
|
||||
|
||||
1. Ubuntu 22.04 Jammy
|
||||
2. Ubuntu 20.04 Focal
|
||||
3. Ubuntu 18.04 Bionic
|
||||
1. Ubuntu 18.04
|
||||
2. Ubuntu 20.04
|
||||
3. Ubuntu 22.04
|
||||
|
||||
[[info | Note]]
|
||||
| It may be possible to build and install Antelope on other Unix-based operating systems. We gathered helpful information on the following page but please keep in mind that it is experimental and not officially supported.
|
||||
|
||||
@@ -21,35 +21,33 @@ These can only be specified from the `nodeos` command-line:
|
||||
```console
|
||||
Command Line Options for eosio::chain_plugin:
|
||||
--genesis-json arg File to read Genesis State from
|
||||
--genesis-timestamp arg override the initial timestamp in the
|
||||
--genesis-timestamp arg override the initial timestamp in the
|
||||
Genesis State file
|
||||
--print-genesis-json extract genesis_state from blocks.log
|
||||
--print-genesis-json extract genesis_state from blocks.log
|
||||
as JSON, print to console, and exit
|
||||
--extract-genesis-json arg extract genesis_state from blocks.log
|
||||
--extract-genesis-json arg extract genesis_state from blocks.log
|
||||
as JSON, write into specified file, and
|
||||
exit
|
||||
--print-build-info print build environment information to
|
||||
--print-build-info print build environment information to
|
||||
console as JSON and exit
|
||||
--extract-build-info arg extract build environment information
|
||||
--extract-build-info arg extract build environment information
|
||||
as JSON, write into specified file, and
|
||||
exit
|
||||
--force-all-checks do not skip any validation checks while
|
||||
replaying blocks (useful for replaying
|
||||
blocks from untrusted source)
|
||||
--force-all-checks do not skip any checks that can be
|
||||
skipped while replaying irreversible
|
||||
blocks
|
||||
--disable-replay-opts disable optimizations that specifically
|
||||
target replay
|
||||
--replay-blockchain clear chain state database and replay
|
||||
--replay-blockchain clear chain state database and replay
|
||||
all blocks
|
||||
--hard-replay-blockchain clear chain state database, recover as
|
||||
many blocks as possible from the block
|
||||
--hard-replay-blockchain clear chain state database, recover as
|
||||
many blocks as possible from the block
|
||||
log, and then replay those blocks
|
||||
--delete-all-blocks clear chain state database and block
|
||||
--delete-all-blocks clear chain state database and block
|
||||
log
|
||||
--truncate-at-block arg (=0) stop hard replay / block log recovery
|
||||
at this block number (if set to
|
||||
--truncate-at-block arg (=0) stop hard replay / block log recovery
|
||||
at this block number (if set to
|
||||
non-zero number)
|
||||
--terminate-at-block arg (=0) terminate after reaching this block
|
||||
number (if set to a non-zero number)
|
||||
--snapshot arg File to read Snapshot State from
|
||||
|
||||
```
|
||||
@@ -60,163 +58,162 @@ These can be specified from both the `nodeos` command-line or the `config.ini` f
|
||||
|
||||
```console
|
||||
Config Options for eosio::chain_plugin:
|
||||
--blocks-dir arg (="blocks") the location of the blocks directory
|
||||
(absolute path or relative to
|
||||
application data dir)
|
||||
--state-dir arg (="state") the location of the state directory
|
||||
(absolute path or relative to
|
||||
--blocks-dir arg (="blocks") the location of the blocks directory
|
||||
(absolute path or relative to
|
||||
application data dir)
|
||||
--protocol-features-dir arg (="protocol_features")
|
||||
the location of the protocol_features
|
||||
the location of the protocol_features
|
||||
directory (absolute path or relative to
|
||||
application config dir)
|
||||
--checkpoint arg Pairs of [BLOCK_NUM,BLOCK_ID] that
|
||||
--checkpoint arg Pairs of [BLOCK_NUM,BLOCK_ID] that
|
||||
should be enforced as checkpoints.
|
||||
--wasm-runtime runtime (=eos-vm-jit) Override default WASM runtime (
|
||||
--wasm-runtime runtime (=eos-vm-jit) Override default WASM runtime (
|
||||
"eos-vm-jit", "eos-vm")
|
||||
"eos-vm-jit" : A WebAssembly runtime
|
||||
that compiles WebAssembly code to
|
||||
"eos-vm-jit" : A WebAssembly runtime
|
||||
that compiles WebAssembly code to
|
||||
native x86 code prior to execution.
|
||||
"eos-vm" : A WebAssembly interpreter.
|
||||
|
||||
--profile-account arg The name of an account whose code will
|
||||
|
||||
--profile-account arg The name of an account whose code will
|
||||
be profiled
|
||||
--abi-serializer-max-time-ms arg (=15)
|
||||
Override default maximum ABI
|
||||
Override default maximum ABI
|
||||
serialization time allowed in ms
|
||||
--chain-state-db-size-mb arg (=1024) Maximum size (in MiB) of the chain
|
||||
--chain-state-db-size-mb arg (=1024) Maximum size (in MiB) of the chain
|
||||
state database
|
||||
--chain-state-db-guard-size-mb arg (=128)
|
||||
Safely shut down node when free space
|
||||
remaining in the chain state database
|
||||
Safely shut down node when free space
|
||||
remaining in the chain state database
|
||||
drops below this size (in MiB).
|
||||
--signature-cpu-billable-pct arg (=50)
|
||||
Percentage of actual signature recovery
|
||||
cpu to bill. Whole number percentages,
|
||||
cpu to bill. Whole number percentages,
|
||||
e.g. 50 for 50%
|
||||
--chain-threads arg (=2) Number of worker threads in controller
|
||||
--chain-threads arg (=2) Number of worker threads in controller
|
||||
thread pool
|
||||
--contracts-console print contract's output to console
|
||||
--deep-mind print deeper information about chain
|
||||
--deep-mind print deeper information about chain
|
||||
operations
|
||||
--actor-whitelist arg Account added to actor whitelist (may
|
||||
--actor-whitelist arg Account added to actor whitelist (may
|
||||
specify multiple times)
|
||||
--actor-blacklist arg Account added to actor blacklist (may
|
||||
--actor-blacklist arg Account added to actor blacklist (may
|
||||
specify multiple times)
|
||||
--contract-whitelist arg Contract account added to contract
|
||||
--contract-whitelist arg Contract account added to contract
|
||||
whitelist (may specify multiple times)
|
||||
--contract-blacklist arg Contract account added to contract
|
||||
--contract-blacklist arg Contract account added to contract
|
||||
blacklist (may specify multiple times)
|
||||
--action-blacklist arg Action (in the form code::action) added
|
||||
to action blacklist (may specify
|
||||
to action blacklist (may specify
|
||||
multiple times)
|
||||
--key-blacklist arg Public key added to blacklist of keys
|
||||
that should not be included in
|
||||
authorities (may specify multiple
|
||||
--key-blacklist arg Public key added to blacklist of keys
|
||||
that should not be included in
|
||||
authorities (may specify multiple
|
||||
times)
|
||||
--sender-bypass-whiteblacklist arg Deferred transactions sent by accounts
|
||||
in this list do not have any of the
|
||||
subjective whitelist/blacklist checks
|
||||
applied to them (may specify multiple
|
||||
--sender-bypass-whiteblacklist arg Deferred transactions sent by accounts
|
||||
in this list do not have any of the
|
||||
subjective whitelist/blacklist checks
|
||||
applied to them (may specify multiple
|
||||
times)
|
||||
--read-mode arg (=head) Database read mode ("head",
|
||||
"irreversible").
|
||||
--read-mode arg (=speculative) Database read mode ("speculative",
|
||||
"head", "read-only", "irreversible").
|
||||
In "speculative" mode: database
|
||||
contains state changes by transactions
|
||||
in the blockchain up to the head block
|
||||
as well as some transactions not yet
|
||||
included in the blockchain.
|
||||
In "head" mode: database contains state
|
||||
changes up to the head block;
|
||||
transactions received by the node are
|
||||
changes by only transactions in the
|
||||
blockchain up to the head block;
|
||||
transactions received by the node are
|
||||
relayed if valid.
|
||||
In "irreversible" mode: database
|
||||
contains state changes up to the last
|
||||
irreversible block; transactions
|
||||
received via the P2P network are not
|
||||
relayed and transactions cannot be
|
||||
pushed via the chain API.
|
||||
|
||||
--api-accept-transactions arg (=1) Allow API transactions to be evaluated
|
||||
In "read-only" mode: (DEPRECATED: see
|
||||
p2p-accept-transactions &
|
||||
api-accept-transactions) database
|
||||
contains state changes by only
|
||||
transactions in the blockchain up to
|
||||
the head block; transactions received
|
||||
via the P2P network are not relayed and
|
||||
transactions cannot be pushed via the
|
||||
chain API.
|
||||
In "irreversible" mode: database
|
||||
contains state changes by only
|
||||
transactions in the blockchain up to
|
||||
the last irreversible block;
|
||||
transactions received via the P2P
|
||||
network are not relayed and
|
||||
transactions cannot be pushed via the
|
||||
chain API.
|
||||
|
||||
--api-accept-transactions arg (=1) Allow API transactions to be evaluated
|
||||
and relayed if valid.
|
||||
--validation-mode arg (=full) Chain validation mode ("full" or
|
||||
--validation-mode arg (=full) Chain validation mode ("full" or
|
||||
"light").
|
||||
In "full" mode all incoming blocks will
|
||||
be fully validated.
|
||||
In "light" mode all incoming blocks
|
||||
headers will be fully validated;
|
||||
transactions in those validated blocks
|
||||
will be trusted
|
||||
|
||||
--disable-ram-billing-notify-checks Disable the check which subjectively
|
||||
In "light" mode all incoming blocks
|
||||
headers will be fully validated;
|
||||
transactions in those validated blocks
|
||||
will be trusted
|
||||
|
||||
--disable-ram-billing-notify-checks Disable the check which subjectively
|
||||
fails a transaction if a contract bills
|
||||
more RAM to another account within the
|
||||
more RAM to another account within the
|
||||
context of a notification handler (i.e.
|
||||
when the receiver is not the code of
|
||||
when the receiver is not the code of
|
||||
the action).
|
||||
--maximum-variable-signature-length arg (=16384)
|
||||
Subjectively limit the maximum length
|
||||
of variable components in a variable
|
||||
Subjectively limit the maximum length
|
||||
of variable components in a variable
|
||||
legnth signature to this size in bytes
|
||||
--trusted-producer arg Indicate a producer whose blocks
|
||||
headers signed by it will be fully
|
||||
validated, but transactions in those
|
||||
--trusted-producer arg Indicate a producer whose blocks
|
||||
headers signed by it will be fully
|
||||
validated, but transactions in those
|
||||
validated blocks will be trusted.
|
||||
--database-map-mode arg (=mapped) Database map mode ("mapped", "heap", or
|
||||
"locked").
|
||||
In "mapped" mode database is memory
|
||||
In "mapped" mode database is memory
|
||||
mapped as a file.
|
||||
In "heap" mode database is preloaded in
|
||||
to swappable memory and will use huge
|
||||
to swappable memory and will use huge
|
||||
pages if available.
|
||||
In "locked" mode database is preloaded,
|
||||
locked in to memory, and will use huge
|
||||
locked in to memory, and will use huge
|
||||
pages if available.
|
||||
|
||||
--eos-vm-oc-cache-size-mb arg (=1024) Maximum size (in MiB) of the EOS VM OC
|
||||
code cache
|
||||
--eos-vm-oc-compile-threads arg (=1) Number of threads to use for EOS VM OC
|
||||
tier-up
|
||||
--eos-vm-oc-enable Enable EOS VM OC tier-up runtime
|
||||
--enable-account-queries arg (=0) enable queries to find accounts by
|
||||
|
||||
--enable-account-queries arg (=0) enable queries to find accounts by
|
||||
various metadata.
|
||||
--max-nonprivileged-inline-action-size arg (=4096)
|
||||
maximum allowed size (in bytes) of an
|
||||
inline action for a nonprivileged
|
||||
maximum allowed size (in bytes) of an
|
||||
inline action for a nonprivileged
|
||||
account
|
||||
--transaction-retry-max-storage-size-gb arg
|
||||
Maximum size (in GiB) allowed to be
|
||||
allocated for the Transaction Retry
|
||||
feature. Setting above 0 enables this
|
||||
Maximum size (in GiB) allowed to be
|
||||
allocated for the Transaction Retry
|
||||
feature. Setting above 0 enables this
|
||||
feature.
|
||||
--transaction-retry-interval-sec arg (=20)
|
||||
How often, in seconds, to resend an
|
||||
incoming transaction to network if not
|
||||
How often, in seconds, to resend an
|
||||
incoming transaction to network if not
|
||||
seen in a block.
|
||||
--transaction-retry-max-expiration-sec arg (=120)
|
||||
Maximum allowed transaction expiration
|
||||
for retry transactions, will retry
|
||||
--transaction-retry-max-expiration-sec arg (=90)
|
||||
Maximum allowed transaction expiration
|
||||
for retry transactions, will retry
|
||||
transactions up to this value.
|
||||
--transaction-finality-status-max-storage-size-gb arg
|
||||
Maximum size (in GiB) allowed to be
|
||||
allocated for the Transaction Finality
|
||||
Maximum size (in GiB) allowed to be
|
||||
allocated for the Transaction Finality
|
||||
Status feature. Setting above 0 enables
|
||||
this feature.
|
||||
--transaction-finality-status-success-duration-sec arg (=180)
|
||||
Duration (in seconds) a successful
|
||||
transaction's Finality Status will
|
||||
remain available from being first
|
||||
Duration (in seconds) a successful
|
||||
transaction's Finality Status will
|
||||
remain available from being first
|
||||
identified.
|
||||
--transaction-finality-status-failure-duration-sec arg (=180)
|
||||
Duration (in seconds) a failed
|
||||
transaction's Finality Status will
|
||||
remain available from being first
|
||||
Duration (in seconds) a failed
|
||||
transaction's Finality Status will
|
||||
remain available from being first
|
||||
identified.
|
||||
--integrity-hash-on-start Log the state integrity hash on startup
|
||||
--integrity-hash-on-stop Log the state integrity hash on
|
||||
shutdown
|
||||
--block-log-retain-blocks arg If set to greater than 0, periodically
|
||||
prune the block log to store only
|
||||
configured number of most recent
|
||||
blocks.
|
||||
If set to 0, no blocks are be written
|
||||
to the block log; block log file is
|
||||
removed after startup.
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
[[warning | Deprecation Notice]]
|
||||
| The `history_plugin` that the `history_api_plugin` depends upon is deprecated and will no longer be maintained. Please use the [`state_history_plugin`](../state_history_plugin/index.md) instead.
|
||||
|
||||
## Description
|
||||
|
||||
The `history_api_plugin` exposes functionality from the [`history_plugin`](../history_plugin/index.md) to the RPC API interface managed by the [`http_plugin`](../http_plugin/index.md), providing read-only access to blockchain data.
|
||||
|
||||
It provides four RPC API endpoints:
|
||||
|
||||
* get_actions
|
||||
* get_transaction
|
||||
* get_key_accounts
|
||||
* get_controlled_accounts
|
||||
|
||||
The four actions listed above are used by the following `cleos` commands (matching order):
|
||||
|
||||
* get actions
|
||||
* get transaction
|
||||
* get accounts
|
||||
* get servants
|
||||
|
||||
## Usage
|
||||
|
||||
```console
|
||||
# config.ini
|
||||
plugin = eosio::history_api_plugin
|
||||
```
|
||||
```sh
|
||||
# command-line
|
||||
nodeos ... --plugin eosio::history_api_plugin
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
None
|
||||
|
||||
## Dependencies
|
||||
|
||||
* [`history_plugin`](../history_plugin/index.md)
|
||||
* [`chain_plugin`](../chain_plugin/index.md)
|
||||
* [`http_plugin`](../http_plugin/index.md)
|
||||
|
||||
### Load Dependency Examples
|
||||
|
||||
```console
|
||||
# config.ini
|
||||
plugin = eosio::history_plugin
|
||||
[options]
|
||||
plugin = eosio::chain_plugin
|
||||
[options]
|
||||
plugin = eosio::http_plugin
|
||||
[options]
|
||||
```
|
||||
```sh
|
||||
# command-line
|
||||
nodeos ... --plugin eosio::history_plugin [options] \
|
||||
--plugin eosio::chain_plugin [operations] [options] \
|
||||
--plugin eosio::http_plugin [options]
|
||||
```
|
||||
@@ -0,0 +1,41 @@
|
||||
[[warning | Deprecation Notice]]
|
||||
| The `history_plugin` is deprecated and will no longer be maintained. Please use the [`state_history_plugin`](../state_history_plugin/index.md) instead.
|
||||
|
||||
## Description
|
||||
|
||||
The `history_plugin` provides a cache layer to obtain historical data about the blockchain objects. It depends on [`chain_plugin`](../chain_plugin/index.md) for the data.
|
||||
|
||||
## Usage
|
||||
|
||||
```console
|
||||
# config.ini
|
||||
plugin = eosio::history_plugin
|
||||
[options]
|
||||
```
|
||||
```sh
|
||||
# command-line
|
||||
nodeos ... --plugin eosio::history_plugin [options]
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
These can be specified from both the `nodeos` command-line or the `config.ini` file:
|
||||
|
||||
```console
|
||||
Config Options for eosio::history_plugin:
|
||||
-f [ --filter-on ] arg Track actions which match
|
||||
receiver:action:actor. Actor may be
|
||||
blank to include all. Action and Actor
|
||||
both blank allows all from Recieiver.
|
||||
Receiver may not be blank.
|
||||
-F [ --filter-out ] arg Do not track actions which match
|
||||
receiver:action:actor. Action and Actor
|
||||
both blank excludes all from Reciever.
|
||||
Actor blank excludes all from
|
||||
reciever:action. Receiver may not be
|
||||
blank.
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
* [`chain_plugin`](../chain_plugin/index.md)
|
||||
@@ -22,59 +22,49 @@ These can be specified from both the command-line or the `config.ini` file:
|
||||
|
||||
```console
|
||||
Config Options for eosio::http_plugin:
|
||||
--unix-socket-path arg The filename (relative to data-dir) to
|
||||
create a unix socket for HTTP RPC; set
|
||||
blank to disable.
|
||||
--http-server-address arg (=127.0.0.1:8888)
|
||||
The local IP and port to listen for
|
||||
--unix-socket-path arg The filename (relative to data-dir) to
|
||||
create a unix socket for HTTP RPC; set
|
||||
blank to disable (=keosd.sock for keosd)
|
||||
--http-server-address arg (=127.0.0.1:8888 for nodeos)
|
||||
The local IP and port to listen for
|
||||
incoming http connections; set blank to
|
||||
disable.
|
||||
--https-server-address arg The local IP and port to listen for
|
||||
--https-server-address arg The local IP and port to listen for
|
||||
incoming https connections; leave blank
|
||||
to disable.
|
||||
--https-certificate-chain-file arg Filename with the certificate chain to
|
||||
present on https connections. PEM
|
||||
--https-certificate-chain-file arg Filename with the certificate chain to
|
||||
present on https connections. PEM
|
||||
format. Required for https.
|
||||
--https-private-key-file arg Filename with https private key in PEM
|
||||
--https-private-key-file arg Filename with https private key in PEM
|
||||
format. Required for https
|
||||
--https-ecdh-curve arg (=secp384r1) Configure https ECDH curve to use:
|
||||
--https-ecdh-curve arg (=secp384r1) Configure https ECDH curve to use:
|
||||
secp384r1 or prime256v1
|
||||
--access-control-allow-origin arg Specify the Access-Control-Allow-Origin
|
||||
to be returned on each request
|
||||
to be returned on each request.
|
||||
--access-control-allow-headers arg Specify the Access-Control-Allow-Header
|
||||
s to be returned on each request
|
||||
--access-control-max-age arg Specify the Access-Control-Max-Age to
|
||||
s to be returned on each request.
|
||||
--access-control-max-age arg Specify the Access-Control-Max-Age to
|
||||
be returned on each request.
|
||||
--access-control-allow-credentials Specify if Access-Control-Allow-Credent
|
||||
ials: true should be returned on each
|
||||
ials: true should be returned on each
|
||||
request.
|
||||
--max-body-size arg (=2097152) The maximum body size in bytes allowed
|
||||
--max-body-size arg (=1048576) The maximum body size in bytes allowed
|
||||
for incoming RPC requests
|
||||
--http-max-bytes-in-flight-mb arg (=500)
|
||||
Maximum size in megabytes http_plugin
|
||||
should use for processing http
|
||||
requests. -1 for unlimited. 429 error
|
||||
response when exceeded.
|
||||
--http-max-in-flight-requests arg (=-1)
|
||||
Maximum number of requests http_plugin
|
||||
should use for processing http
|
||||
requests. 429 error response when
|
||||
Maximum size in megabytes http_plugin
|
||||
should use for processing http
|
||||
requests. 503 error response when
|
||||
exceeded.
|
||||
--http-max-response-time-ms arg (=30) Maximum time for processing a request,
|
||||
-1 for unlimited
|
||||
--verbose-http-errors Append the error log to HTTP responses
|
||||
--http-validate-host arg (=1) If set to false, then any incoming
|
||||
--http-validate-host arg (=1) If set to false, then any incoming
|
||||
"Host" header is considered valid
|
||||
--http-alias arg Additionaly acceptable values for the
|
||||
"Host" header of incoming HTTP
|
||||
requests, can be specified multiple
|
||||
times. Includes http/s_server_address
|
||||
--http-alias arg Additionaly acceptable values for the
|
||||
"Host" header of incoming HTTP
|
||||
requests, can be specified multiple
|
||||
times. Includes http/s_server_address
|
||||
by default.
|
||||
--http-threads arg (=2) Number of worker threads in http thread
|
||||
pool
|
||||
--http-keep-alive arg (=1) If set to false, do not keep HTTP
|
||||
connections alive, even if client
|
||||
requests.
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
@@ -13,6 +13,7 @@ For information on specific plugins, just select from the list below:
|
||||
* [`db_size_api_plugin`](db_size_api_plugin/index.md)
|
||||
* [`http_client_plugin`](http_client_plugin/index.md)
|
||||
* [`http_plugin`](http_plugin/index.md)
|
||||
* [`login_plugin`](login_plugin/index.md)
|
||||
* [`net_api_plugin`](net_api_plugin/index.md)
|
||||
* [`net_plugin`](net_plugin/index.md)
|
||||
* [`producer_plugin`](producer_plugin/index.md)
|
||||
@@ -20,6 +21,7 @@ For information on specific plugins, just select from the list below:
|
||||
* [`test_control_api_plugin`](test_control_api_plugin/index.md)
|
||||
* [`test_control_plugin`](test_control_plugin/index.md)
|
||||
* [`trace_api_plugin`](trace_api_plugin/index.md)
|
||||
* [`txn_test_gen_plugin`](txn_test_gen_plugin/index.md)
|
||||
|
||||
[[info | Nodeos is modular]]
|
||||
| Plugins add incremental functionality to `nodeos`. Unlike runtime plugins, `nodeos` plugins are built at compile-time.
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
## Description
|
||||
|
||||
The `login_plugin` supports the concept of applications authenticating with the Antelope blockchain. The `login_plugin` API allows an application to verify whether an account is allowed to sign in order to satisfy a specified authority.
|
||||
|
||||
## Usage
|
||||
|
||||
```console
|
||||
# config.ini
|
||||
plugin = eosio::login_plugin
|
||||
[options]
|
||||
```
|
||||
```sh
|
||||
# command-line
|
||||
nodeos ... --plugin eosio::login_plugin [options]
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
These can be specified from both the `nodeos` command-line or the `config.ini` file:
|
||||
|
||||
```console
|
||||
Config Options for eosio::login_plugin:
|
||||
--max-login-requests arg (=1000000) The maximum number of pending login
|
||||
requests
|
||||
--max-login-timeout arg (=60) The maximum timeout for pending login
|
||||
requests (in seconds)
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
* [`chain_plugin`](../chain_plugin/index.md)
|
||||
* [`http_plugin`](../http_plugin/index.md)
|
||||
|
||||
### Load Dependency Examples
|
||||
|
||||
```console
|
||||
# config.ini
|
||||
plugin = eosio::chain_plugin
|
||||
[options]
|
||||
plugin = eosio::http_plugin
|
||||
[options]
|
||||
```
|
||||
```sh
|
||||
# command-line
|
||||
nodeos ... --plugin eosio::chain_plugin [options] \
|
||||
--plugin eosio::http_plugin [options]
|
||||
```
|
||||
@@ -44,7 +44,7 @@ Config Options for eosio::net_plugin:
|
||||
--p2p-accept-transactions arg (=1) Allow transactions received over p2p
|
||||
network to be evaluated and relayed if
|
||||
valid.
|
||||
--agent-name arg (=EOS Test Agent) The name supplied to identify this node
|
||||
--agent-name arg (="EOS Test Agent") The name supplied to identify this node
|
||||
amongst the peers.
|
||||
--allowed-connection arg (=any) Can be 'any' or 'producers' or
|
||||
'specified' or 'none'. If 'specified',
|
||||
@@ -73,15 +73,13 @@ Config Options for eosio::net_plugin:
|
||||
synchronization
|
||||
--use-socket-read-watermark arg (=0) Enable experimental socket read
|
||||
watermark optimization
|
||||
--peer-log-format arg (=["${_name}" - ${_cid} ${_ip}:${_port}] )
|
||||
--peer-log-format arg (=["${_name}" ${_ip}:${_port}])
|
||||
The string used to format peers when
|
||||
logging messages about them. Variables
|
||||
are escaped with ${<variable name>}.
|
||||
Available Variables:
|
||||
_name self-reported name
|
||||
|
||||
_cid assigned connection id
|
||||
|
||||
_id self-reported ID (64 hex
|
||||
characters)
|
||||
|
||||
@@ -96,10 +94,7 @@ Config Options for eosio::net_plugin:
|
||||
peer
|
||||
|
||||
_lport local port number connected
|
||||
to peer
|
||||
--p2p-keepalive-interval-ms arg (=10000)
|
||||
peer heartbeat keepalive message
|
||||
interval in milliseconds
|
||||
to peer
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
@@ -23,6 +23,7 @@ These can be specified from both the `nodeos` command-line or the `config.ini` f
|
||||
|
||||
```console
|
||||
Config Options for eosio::producer_plugin:
|
||||
|
||||
-e [ --enable-stale-production ] Enable block production, even if the
|
||||
chain is stale.
|
||||
-x [ --pause-on-startup ] Start this node in a state where
|
||||
@@ -48,17 +49,17 @@ Config Options for eosio::producer_plugin:
|
||||
<public-key>=<provider-spec>
|
||||
Where:
|
||||
<public-key> is a string form of
|
||||
a valid EOS public
|
||||
a vaild Antelope public
|
||||
key
|
||||
|
||||
<provider-spec> is a string in the
|
||||
form <provider-type>
|
||||
:<data>
|
||||
|
||||
<provider-type> is KEY, KEOSD, or SE
|
||||
<provider-type> is KEY, or KEOSD
|
||||
|
||||
KEY:<data> is a string form of
|
||||
a valid EOS
|
||||
a valid Antelope
|
||||
private key which
|
||||
maps to the provided
|
||||
public key
|
||||
@@ -68,6 +69,10 @@ Config Options for eosio::producer_plugin:
|
||||
and the approptiate
|
||||
wallet(s) are
|
||||
unlocked
|
||||
--keosd-provider-timeout arg (=5) Limits the maximum time (in
|
||||
milliseconds) that is allowed for
|
||||
sending blocks to a keosd provider for
|
||||
signing
|
||||
--greylist-account arg account that can not access to extended
|
||||
CPU/NET virtual resources
|
||||
--greylist-limit arg (=1000) Limit (between 1 and 1000) on the
|
||||
@@ -102,10 +107,9 @@ Config Options for eosio::producer_plugin:
|
||||
--max-scheduled-transaction-time-per-block-ms arg (=100)
|
||||
Maximum wall-clock time, in
|
||||
milliseconds, spent retiring scheduled
|
||||
transactions (and incoming transactions
|
||||
according to incoming-defer-ratio) in
|
||||
any block before returning to normal
|
||||
transaction processing.
|
||||
transactions in any block before
|
||||
returning to normal transaction
|
||||
processing.
|
||||
--subjective-cpu-leeway-us arg (=31000)
|
||||
Time in microseconds allowed for a
|
||||
transaction that starts with
|
||||
@@ -115,6 +119,11 @@ Config Options for eosio::producer_plugin:
|
||||
Sets the maximum amount of failures
|
||||
that are allowed for a given account
|
||||
per block.
|
||||
Disregarded for accounts that have been
|
||||
whitelisted by disabling subjective
|
||||
billing for the account using the
|
||||
disable-subjective-account-billing
|
||||
configuration option.
|
||||
--subjective-account-decay-time-minutes arg (=1440)
|
||||
Sets the time to return full subjective
|
||||
cpu for accounts
|
||||
@@ -126,11 +135,16 @@ Config Options for eosio::producer_plugin:
|
||||
transaction queue. Exceeding this value
|
||||
will subjectively drop transaction with
|
||||
resource exhaustion.
|
||||
--disable-api-persisted-trx Disable the re-apply of API
|
||||
transactions.
|
||||
--disable-subjective-billing arg (=1) Disable subjective CPU billing for
|
||||
API/P2P transactions
|
||||
--disable-subjective-account-billing arg
|
||||
Account which is excluded from
|
||||
subjective CPU billing
|
||||
Account is considered whitelisted and
|
||||
will not be subject to enforcement of
|
||||
subjective-account-max-failures.
|
||||
--disable-subjective-p2p-billing arg (=1)
|
||||
Disable subjective CPU billing for P2P
|
||||
transactions
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
|
||||
## Overview
|
||||
|
||||
The `resource_monitor_plugin` monitors space usage in the computing system where `nodeos` is running. Specifically, every `resource-monitor-interval-seconds` seconds, it measures the individual space used by each of the file systems mounted by `data-dir`, `state-dir`, `blocks-log-dir`, `snapshots-dir`, `state-history-dir`, and `trace-dir`. When space usage in any of the monitored file system is within `5%` of the threshold specified by `resource-monitor-space-threshold`, a warning containing the file system path and percentage of space has used is printed out. When space usage exceeds the threshold, if `resource-monitor-not-shutdown-on-threshold-exceeded` is not set, `nodeos` gracefully shuts down; if `resource-monitor-not-shutdown-on-threshold-exceeded` is set, `nodeos` prints out warnings periodically until space usage goes under the threshold.
|
||||
The `resource_monitor_plugin` monitors space usage in the computing system where `nodeos` is running. Specifically, every `resource-monitor-interval-seconds` seconds,
|
||||
it measures the individual space used by each of the file systems mounted
|
||||
by `data-dir`, `state-dir`, `blocks-log-dir`, `snapshots-dir`,
|
||||
`state-history-dir`, and `trace-dir`.
|
||||
When space usage in any of the monitored file system is within `5%` of the threshold
|
||||
specified by `resource-monitor-space-threshold`, a warning containing the file system
|
||||
path and percentage of space has used is printed out.
|
||||
When space usage exceeds the threshold,
|
||||
if `resource-monitor-not-shutdown-on-threshold-exceeded` is not set,
|
||||
`nodeos` gracefully shuts down; if `resource-monitor-not-shutdown-on-threshold-exceeded` is set, `nodeos` prints out warnings periodically
|
||||
until space usage goes under the threshold.
|
||||
|
||||
`resource_monitor_plugin` is always loaded.
|
||||
|
||||
## Usage
|
||||
|
||||
```console
|
||||
@@ -23,28 +32,33 @@ These can be specified from both the `nodeos` command-line or the `config.ini` f
|
||||
|
||||
```console
|
||||
Config Options for eosio::resource_monitor_plugin:
|
||||
|
||||
--resource-monitor-interval-seconds arg (=2)
|
||||
Time in seconds between two consecutive
|
||||
checks of resource usage. Should be
|
||||
between 1 and 300
|
||||
Time in seconds between two consecutive checks
|
||||
of space usage. Should be between 1 and 300.
|
||||
--resource-monitor-space-threshold arg (=90)
|
||||
Threshold in terms of percentage of
|
||||
used space vs total space. If used
|
||||
space is above (threshold - 5%), a
|
||||
warning is generated. Unless
|
||||
resource-monitor-not-shutdown-on-thresh
|
||||
old-exceeded is enabled, a graceful
|
||||
shutdown is initiated if used space is
|
||||
above the threshold. The value should
|
||||
be between 6 and 99
|
||||
Threshold in terms of percentage of used space
|
||||
vs total space. If the used space is within
|
||||
`5%` of the threshold, a warning is generated.
|
||||
If the used space is above the threshold and
|
||||
`resource-monitor-not-shutdown-on-threshold-exceeded`
|
||||
is enabled, a shutdown is initiated; otherwise
|
||||
a warning will be continuously printed out.
|
||||
The value should be between 6 and 99.
|
||||
--resource-monitor-not-shutdown-on-threshold-exceeded
|
||||
Used to indicate nodeos will not
|
||||
shutdown when threshold is exceeded.
|
||||
A switch used to indicate `nodeos` will "not"
|
||||
shutdown when threshold is exceeded. When not
|
||||
set, `nodeos` will shutdown.
|
||||
--resource-monitor-warning-interval arg (=30)
|
||||
Number of resource monitor intervals
|
||||
between two consecutive warnings when
|
||||
the threshold is hit. Should be between
|
||||
1 and 450
|
||||
Number of monitor intervals between which a
|
||||
warning is displayed. For example, if
|
||||
`resource-monitor-warning-interval` is to 10
|
||||
and `resource-monitor-interval-seconds` is 2,
|
||||
a warning will be displayed every 20 seconds,
|
||||
even though the space usage is checked every
|
||||
2 seconds. This is used to throttle the
|
||||
number of warnings in the `nodeos` log file.
|
||||
Should be between 1 and 450.
|
||||
```
|
||||
|
||||
## Plugin Dependencies
|
||||
|
||||
@@ -31,24 +31,19 @@ These can be specified from both the `nodeos` command-line or the `config.ini` f
|
||||
|
||||
```console
|
||||
Config Options for eosio::state_history_plugin:
|
||||
|
||||
--state-history-dir arg (="state-history")
|
||||
the location of the state-history
|
||||
the location of the state-history
|
||||
directory (absolute path or relative to
|
||||
application data dir)
|
||||
--trace-history enable trace history
|
||||
--chain-state-history enable chain state history
|
||||
--state-history-endpoint arg (=127.0.0.1:8080)
|
||||
the endpoint upon which to listen for
|
||||
incoming connections. Caution: only
|
||||
expose this port to your internal
|
||||
the endpoint upon which to listen for
|
||||
incoming connections. Caution: only
|
||||
expose this port to your internal
|
||||
network.
|
||||
--state-history-unix-socket-path arg the path (relative to data-dir) to
|
||||
create a unix socket upon which to
|
||||
listen for incoming connections.
|
||||
--trace-history-debug-mode enable debug mode for trace history
|
||||
--state-history-log-retain-blocks arg if set, periodically prune the state
|
||||
history files to store only configured
|
||||
number of most recent blocks
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
## Description
|
||||
|
||||
The `txn_test_gen_plugin` is used for transaction test purposes.
|
||||
|
||||
[[info | For More Information]]
|
||||
For more information, check the [txn_test_gen_plugin/README.md](https://github.com/AntelopeIO/leap/tree/main/plugins/txn_test_gen_plugin) on the `AntelopeIO/leap` repository.
|
||||
|
||||
## Usage
|
||||
|
||||
```console
|
||||
# config.ini
|
||||
plugin = eosio::txn_test_gen_plugin
|
||||
[options]
|
||||
```
|
||||
```sh
|
||||
# command-line
|
||||
nodeos ... --plugin eosio::txn_test_gen_plugin [options]
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
These can be specified from both the `nodeos` command-line or the `config.ini` file:
|
||||
|
||||
```console
|
||||
Config Options for eosio::txn_test_gen_plugin:
|
||||
--txn-reference-block-lag arg (=0) Lag in number of blocks from the head
|
||||
block when selecting the reference
|
||||
block for transactions (-1 means Last
|
||||
Irreversible Block)
|
||||
--txn-test-gen-threads arg (=2) Number of worker threads in
|
||||
txn_test_gen thread pool
|
||||
--txn-test-gen-account-prefix arg (=txn.test.)
|
||||
Prefix to use for accounts generated
|
||||
and used by this plugin
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
None
|
||||
@@ -27,17 +27,38 @@ The `nodeos` service provides query access to the chain database via the HTTP [R
|
||||
|
||||
The `nodeos` service can be run in different "read" modes. These modes control how the node operates and how it processes blocks and transactions:
|
||||
|
||||
- `speculative`: this includes the side effects of confirmed and unconfirmed transactions.
|
||||
- `head`: this only includes the side effects of confirmed transactions, this mode processes unconfirmed transactions but does not include them.
|
||||
- `read-only`: this mode is deprecated. Similar functionality can be achieved by combining options: `read-mode = head`, `p2p-accept-transactions = false`, `api-accept-transactions = false`. When these options are set, the local database will contain state changes made by transactions in the chain up to the head block. Also, transactions received via the P2P network are not relayed and transactions cannot be pushed via the chain API.
|
||||
- `irreversible`: this mode also includes confirmed transactions only up to those included in the last irreversible block.
|
||||
|
||||
A transaction is considered confirmed when a `nodeos` instance has received, processed, and written it to a block on the blockchain, i.e. it is in the head block or an earlier block.
|
||||
|
||||
### Speculative Mode
|
||||
|
||||
Clients such as `cleos` and the RPC API, will see database state as of the current head block plus changes made by all transactions known to this node but potentially not included in the chain, unconfirmed transactions for example.
|
||||
|
||||
Speculative mode is low latency but fragile, there is no guarantee that the transactions reflected in the state will be included in the chain OR that they will reflected in the same order the state implies.
|
||||
|
||||
This mode features the lowest latency, but is the least consistent.
|
||||
|
||||
In speculative mode `nodeos` is able to execute transactions which have TaPoS (Transaction as Proof of Stake) pointing to any valid block in a fork considered to be the best fork by this node.
|
||||
|
||||
### Head Mode
|
||||
|
||||
Clients such as `cleos` and the RPC API will see database state as of the current head block of the chain. Since current head block is not yet irreversible and short-lived forks are possible, state read in this mode may become inaccurate if `nodeos` switches to a better fork.
|
||||
Clients such as `cleos` and the RPC API will see database state as of the current head block of the chain. Since current head block is not yet irreversible and short-lived forks are possible, state read in this mode may become inaccurate if `nodeos` switches to a better fork. Note that this is also true of speculative mode.
|
||||
|
||||
This mode represents a good trade-off between highly consistent views of the data and latency.
|
||||
|
||||
In this mode `nodeos` is able to execute transactions which have TaPoS pointing to any valid block in a fork considered to be the best fork by this node.
|
||||
|
||||
### Read-Only Mode
|
||||
|
||||
[[caution | Deprecation Notice]]
|
||||
| The explicit `read-mode = read-only` mode is deprecated. Similar functionality can now be achieved in `head` mode by combining options: `read-mode = head`, `p2p-accept-transactions = false`, `api-accept-transactions = false`.
|
||||
|
||||
Clients such as `cleos` and the RPC API will see database state as of the current head block of the chain. It **will not** include changes made by transactions known to this node but not included in the chain, such as unconfirmed transactions.
|
||||
|
||||
### Irreversible Mode
|
||||
|
||||
When `nodeos` is configured to be in irreversible read mode, it will still track the most up-to-date blocks in the fork database, but the state will lag behind the current best head block, sometimes referred to as the fork DB head, to always reflect the state of the last irreversible block.
|
||||
|
||||
@@ -91,6 +91,11 @@ Config Options for eosio::wallet_plugin:
|
||||
number of seconds of inactivity.
|
||||
Activity is defined as any wallet
|
||||
command e.g. list-wallets.
|
||||
--yubihsm-url URL Override default URL of
|
||||
http://localhost:12345 for connecting
|
||||
to yubihsm-connector
|
||||
--yubihsm-authkey key_num Enables YubiHSM support using given
|
||||
Authkey
|
||||
|
||||
Application Config Options:
|
||||
--plugin arg Plugin(s) to enable, may be specified
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
content_title: How To Attach a YubiHSM Hard Wallet
|
||||
link_text: How To Attach a YubiHSM Hard Wallet
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
Attach a YubiHSM as a hard wallet
|
||||
|
||||
## Before you begin
|
||||
|
||||
* Install the currently supported version of `keosd`
|
||||
|
||||
* Install YubiHSM2 Software Toolkit (YubiHSM2 SDK)
|
||||
|
||||
* Create an AuthKey with at least the following Capabilities:
|
||||
|
||||
* sign-ecdsa
|
||||
* generate-asymmetric-key
|
||||
* export-wrapped
|
||||
|
||||
* **Delete the default AuthKey**
|
||||
|
||||
[[warning | Security]]
|
||||
| It is extremely important to create a new AuthKey and remove the default AuthKey before proceed to the following steps.
|
||||
|
||||
## Steps
|
||||
|
||||
### Configure `keosd`
|
||||
|
||||
There are two options to connect `keosd` to YubiHSM:
|
||||
|
||||
#### Using a YubiHSM connector
|
||||
|
||||
By default, `keosd` will connect to the YubiHSM connector on the default host and port. If a non-default URL is used, set the `--yubihsm-url` option or `yubihsm-url` in `config.ini` with the correct connector URL
|
||||
|
||||
#### Directly connect via USB
|
||||
|
||||
`keosd` also can directly connect to YubiHSM via USB protocol
|
||||
|
||||
If this option is used, set `keosd` startup option as the below:
|
||||
|
||||
```sh
|
||||
--yubihsm-url=ysb://
|
||||
```
|
||||
|
||||
### Start `keosd` with AuthKey:
|
||||
|
||||
```sh
|
||||
--yubihsm-authkey Your_AuthKey_Object_Number
|
||||
```
|
||||
|
||||
if a YubiHSM connector is used, check the YubiHSM connector is up and running by visiting YubiHSM URL:
|
||||
http://YubiHSM_HOST:YubiHSM_PORT/connector/status ((Default HOST and Port: http://127.0.0.1:12345)
|
||||
|
||||
You should see something like this:
|
||||
|
||||
```console
|
||||
status=OK
|
||||
serial=*
|
||||
version=2.0.0
|
||||
pid=666
|
||||
address=localhost
|
||||
port=12345
|
||||
```
|
||||
|
||||
### Unlock YubiHSM wallet with the password of AuthKey using the following option:
|
||||
|
||||
```sh
|
||||
cleos wallet unlock -n YubiHSM --password YOUR_AUTHKEY_PASSWORD
|
||||
```
|
||||
|
||||
After unlocking the wallet, you can use `cleos wallet` commands as usual. Beware as a part of security mechanism, some wallet subcommands, such as retrieve private keys, or remove a key, are not supported when a YubiHSM is used
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
content_title: Keosd How-to Guides
|
||||
---
|
||||
|
||||
* [How to attach a YubiHSM hard wallet](how-to-attach-a-yubihsm-hard-wallet.md)
|
||||
+7
-7
@@ -4,8 +4,8 @@
|
||||
# Project related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = "LEAP"
|
||||
PROJECT_NUMBER = "${DOXY_EOS_VERSION}"
|
||||
PROJECT_NAME = "EOS.IO"
|
||||
PROJECT_NUMBER = "EOSIO ${DOXY_EOS_VERSION}"
|
||||
PROJECT_BRIEF =
|
||||
PROJECT_LOGO = eos-logo.png
|
||||
OUTPUT_DIRECTORY =
|
||||
@@ -209,9 +209,9 @@ HTML_DYNAMIC_SECTIONS = NO
|
||||
HTML_INDEX_NUM_ENTRIES = 100
|
||||
GENERATE_DOCSET = NO
|
||||
DOCSET_FEEDNAME = "Doxygen generated docs"
|
||||
DOCSET_BUNDLE_ID = io.antelope
|
||||
DOCSET_PUBLISHER_ID = com.eosnetwork
|
||||
DOCSET_PUBLISHER_NAME = eosnetwork.com
|
||||
DOCSET_BUNDLE_ID = io.eos
|
||||
DOCSET_PUBLISHER_ID = one.block
|
||||
DOCSET_PUBLISHER_NAME = block.one
|
||||
GENERATE_HTMLHELP = NO
|
||||
CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
@@ -221,14 +221,14 @@ BINARY_TOC = NO
|
||||
TOC_EXPAND = NO
|
||||
GENERATE_QHP = NO
|
||||
QCH_FILE =
|
||||
QHP_NAMESPACE = io.antelope
|
||||
QHP_NAMESPACE = io.eos
|
||||
QHP_VIRTUAL_FOLDER = doc
|
||||
QHP_CUST_FILTER_NAME =
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
QHG_LOCATION =
|
||||
GENERATE_ECLIPSEHELP = NO
|
||||
ECLIPSE_DOC_ID = io.antelope
|
||||
ECLIPSE_DOC_ID = io.eos
|
||||
DISABLE_INDEX = NO
|
||||
GENERATE_TREEVIEW = NO
|
||||
ENUM_VALUES_PER_LINE = 4
|
||||
|
||||
@@ -3,7 +3,6 @@ set(FC_INSTALL_COMPONENT "dev")
|
||||
set(APPBASE_INSTALL_COMPONENT "dev")
|
||||
set(SOFTFLOAT_INSTALL_COMPONENT "dev")
|
||||
set(EOSVM_INSTALL_COMPONENT "dev")
|
||||
set(BN256_INSTALL_COMPONENT "dev")
|
||||
|
||||
add_subdirectory( libfc )
|
||||
add_subdirectory( builtins )
|
||||
@@ -27,3 +26,25 @@ set(ENABLE_PROFILE OFF CACHE BOOL "Enable for profile builds")
|
||||
if(eos-vm IN_LIST EOSIO_WASM_RUNTIMES OR eos-vm-jit IN_LIST EOSIO_WASM_RUNTIMES)
|
||||
add_subdirectory( eos-vm )
|
||||
endif()
|
||||
|
||||
#yubihsm's openssl discovery is via pkg-config instead of find_package. Help it out on macOS otherwise openssl's pkgconfig
|
||||
# files may not be found down in the /opt/homebrew/opt directory
|
||||
if(APPLE)
|
||||
get_filename_component(OPENSSL_LIB_PATH "${OPENSSL_CRYPTO_LIBRARY}" DIRECTORY)
|
||||
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${OPENSSL_LIB_PATH}/pkgconfig")
|
||||
endif()
|
||||
set(ENABLE_STATIC ON)
|
||||
set(CMAKE_MACOSX_RPATH OFF)
|
||||
set(BUILD_ONLY_LIB ON CACHE BOOL "Library only build")
|
||||
message(STATUS "Starting yubihsm configuration...")
|
||||
add_subdirectory( yubihsm EXCLUDE_FROM_ALL )
|
||||
target_compile_options(yubihsm_static PRIVATE -fno-lto -fcommon)
|
||||
message(STATUS "yubihsm configuration complete")
|
||||
|
||||
get_property(_CTEST_CUSTOM_TESTS_IGNORE GLOBAL PROPERTY CTEST_CUSTOM_TESTS_IGNORE)
|
||||
set_property(GLOBAL PROPERTY CTEST_CUSTOM_TESTS_IGNORE
|
||||
"change_authkey import_ed decrypt_ec decrypt_rsa ssh logs generate_rsa import_ec echo\
|
||||
yubico_otp wrap_data wrap info import_rsa import_authkey generate_hmac generate_ec\
|
||||
attest pbkdf2 parsing ${_CTEST_CUSTOM_TESTS_IGNORE}")
|
||||
|
||||
add_subdirectory(prometheus)
|
||||
@@ -42,7 +42,7 @@ if("eos-vm-oc" IN_LIST EOSIO_WASM_RUNTIMES)
|
||||
endif()
|
||||
|
||||
llvm_map_components_to_libnames(LLVM_LIBS support core passes mcjit native orcjit)
|
||||
include_directories(SYSTEM ${LLVM_INCLUDE_DIRS})
|
||||
include_directories(${LLVM_INCLUDE_DIRS})
|
||||
add_definitions(${LLVM_DEFINITIONS})
|
||||
|
||||
option(EOSVMOC_ENABLE_DEVELOPER_OPTIONS "enable developer options for EOS VM OC" OFF)
|
||||
@@ -128,7 +128,7 @@ add_library( eosio_chain
|
||||
${HEADERS}
|
||||
)
|
||||
|
||||
target_link_libraries( eosio_chain PUBLIC bn256 fc chainbase eosio_rapidjson Logging IR WAST WASM Runtime
|
||||
target_link_libraries( eosio_chain PUBLIC fc chainbase eosio_rapidjson Logging IR WAST WASM Runtime
|
||||
softfloat builtins ${CHAIN_EOSVM_LIBRARIES} ${LLVM_LIBS} ${CHAIN_RT_LINKAGE}
|
||||
)
|
||||
target_include_directories( eosio_chain
|
||||
|
||||
@@ -75,7 +75,7 @@ void apply_context::exec_one()
|
||||
privileged = receiver_account->is_privileged();
|
||||
auto native = control.find_apply_handler( receiver, act->account, act->name );
|
||||
if( native ) {
|
||||
if( trx_context.enforce_whiteblacklist && control.is_speculative_block() ) {
|
||||
if( trx_context.enforce_whiteblacklist && control.is_producing_block() ) {
|
||||
control.check_contract_list( receiver );
|
||||
control.check_action_list( act->account, act->name );
|
||||
}
|
||||
@@ -89,7 +89,7 @@ void apply_context::exec_one()
|
||||
|| control.is_builtin_activated( builtin_protocol_feature_t::forward_setcode )
|
||||
)
|
||||
) {
|
||||
if( trx_context.enforce_whiteblacklist && control.is_speculative_block() ) {
|
||||
if( trx_context.enforce_whiteblacklist && control.is_producing_block() ) {
|
||||
control.check_contract_list( receiver );
|
||||
control.check_action_list( act->account, act->name );
|
||||
}
|
||||
@@ -316,12 +316,12 @@ void apply_context::execute_inline( action&& a ) {
|
||||
EOS_ASSERT( code != nullptr, action_validate_exception,
|
||||
"inline action's code account ${account} does not exist", ("account", a.account) );
|
||||
|
||||
bool enforce_actor_whitelist_blacklist = trx_context.enforce_whiteblacklist && control.is_speculative_block();
|
||||
bool enforce_actor_whitelist_blacklist = trx_context.enforce_whiteblacklist && control.is_producing_block();
|
||||
flat_set<account_name> actors;
|
||||
|
||||
bool disallow_send_to_self_bypass = control.is_builtin_activated( builtin_protocol_feature_t::restrict_action_to_self );
|
||||
bool send_to_self = (a.account == receiver);
|
||||
bool inherit_parent_authorizations = (!disallow_send_to_self_bypass && send_to_self && (receiver == act->account) && control.is_speculative_block());
|
||||
bool inherit_parent_authorizations = (!disallow_send_to_self_bypass && send_to_self && (receiver == act->account) && control.is_producing_block());
|
||||
|
||||
flat_set<permission_level> inherited_authorizations;
|
||||
if( inherit_parent_authorizations ) {
|
||||
@@ -347,7 +347,7 @@ void apply_context::execute_inline( action&& a ) {
|
||||
control.check_actor_list( actors );
|
||||
}
|
||||
|
||||
if( !privileged && control.is_speculative_block() ) {
|
||||
if( !privileged && control.is_producing_block() ) {
|
||||
const auto& chain_config = control.get_global_properties().configuration;
|
||||
EOS_ASSERT( a.data.size() < std::min(chain_config.max_inline_action_size, control.get_max_nonprivileged_inline_action_size()),
|
||||
inline_action_too_big_nonprivileged,
|
||||
@@ -363,7 +363,7 @@ void apply_context::execute_inline( action&& a ) {
|
||||
control.pending_block_time() - trx_context.published,
|
||||
std::bind(&transaction_context::checktime, &this->trx_context),
|
||||
false,
|
||||
trx_context.is_dry_run(),
|
||||
trx_context.is_read_only,
|
||||
inherited_authorizations
|
||||
);
|
||||
|
||||
@@ -373,7 +373,7 @@ void apply_context::execute_inline( action&& a ) {
|
||||
} catch( const fc::exception& e ) {
|
||||
if( disallow_send_to_self_bypass || !send_to_self ) {
|
||||
throw;
|
||||
} else if( control.is_speculative_block() ) {
|
||||
} else if( control.is_producing_block() ) {
|
||||
subjective_block_production_exception new_exception(FC_LOG_MESSAGE( error, "Authorization failure with inline action sent to self"));
|
||||
for (const auto& log: e.get_log()) {
|
||||
new_exception.append_log(log);
|
||||
@@ -383,7 +383,7 @@ void apply_context::execute_inline( action&& a ) {
|
||||
} catch( ... ) {
|
||||
if( disallow_send_to_self_bypass || !send_to_self ) {
|
||||
throw;
|
||||
} else if( control.is_speculative_block() ) {
|
||||
} else if( control.is_producing_block() ) {
|
||||
EOS_THROW(subjective_block_production_exception, "Unexpected exception occurred validating inline action sent to self");
|
||||
}
|
||||
}
|
||||
@@ -407,7 +407,7 @@ void apply_context::execute_context_free_inline( action&& a ) {
|
||||
EOS_ASSERT( a.authorization.size() == 0, action_validate_exception,
|
||||
"context-free actions cannot have authorizations" );
|
||||
|
||||
if( !privileged && control.is_speculative_block() ) {
|
||||
if( !privileged && control.is_producing_block() ) {
|
||||
const auto& chain_config = control.get_global_properties().configuration;
|
||||
EOS_ASSERT( a.data.size() < std::min(chain_config.max_inline_action_size, control.get_max_nonprivileged_inline_action_size()),
|
||||
inline_action_too_big_nonprivileged,
|
||||
@@ -428,7 +428,7 @@ void apply_context::execute_context_free_inline( action&& a ) {
|
||||
void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, account_name payer, transaction&& trx, bool replace_existing ) {
|
||||
EOS_ASSERT( trx.context_free_actions.size() == 0, cfa_inside_generated_tx, "context free actions are not currently allowed in generated transactions" );
|
||||
|
||||
bool enforce_actor_whitelist_blacklist = trx_context.enforce_whiteblacklist && control.is_speculative_block()
|
||||
bool enforce_actor_whitelist_blacklist = trx_context.enforce_whiteblacklist && control.is_producing_block()
|
||||
&& !control.sender_avoids_whitelist_blacklist_enforcement( receiver );
|
||||
trx_context.validate_referenced_accounts( trx, enforce_actor_whitelist_blacklist );
|
||||
|
||||
@@ -530,7 +530,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
|
||||
} catch( const fc::exception& e ) {
|
||||
if( disallow_send_to_self_bypass || !is_sending_only_to_self(receiver) ) {
|
||||
throw;
|
||||
} else if( control.is_speculative_block() ) {
|
||||
} else if( control.is_producing_block() ) {
|
||||
subjective_block_production_exception new_exception(FC_LOG_MESSAGE( error, "Authorization failure with sent deferred transaction consisting only of actions to self"));
|
||||
for (const auto& log: e.get_log()) {
|
||||
new_exception.append_log(log);
|
||||
@@ -540,7 +540,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
|
||||
} catch( ... ) {
|
||||
if( disallow_send_to_self_bypass || !is_sending_only_to_self(receiver) ) {
|
||||
throw;
|
||||
} else if( control.is_speculative_block() ) {
|
||||
} else if( control.is_producing_block() ) {
|
||||
EOS_THROW(subjective_block_production_exception, "Unexpected exception occurred validating sent deferred transaction consisting only of actions to self");
|
||||
}
|
||||
}
|
||||
@@ -552,7 +552,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
|
||||
|
||||
bool replace_deferred_activated = control.is_builtin_activated(builtin_protocol_feature_t::replace_deferred);
|
||||
|
||||
EOS_ASSERT( replace_deferred_activated || !control.is_speculative_block()
|
||||
EOS_ASSERT( replace_deferred_activated || !control.is_producing_block()
|
||||
|| control.all_subjective_mitigations_disabled(),
|
||||
subjective_block_production_exception,
|
||||
"Replacing a deferred transaction is temporarily disabled." );
|
||||
|
||||
+116
-165
@@ -155,9 +155,8 @@ struct pending_state {
|
||||
|
||||
maybe_session _db_session;
|
||||
block_stage_type _block_stage;
|
||||
controller::block_status _block_status = controller::block_status::ephemeral;
|
||||
controller::block_status _block_status = controller::block_status::incomplete;
|
||||
std::optional<block_id_type> _producer_block_id;
|
||||
controller::block_report _block_report{};
|
||||
|
||||
/** @pre _block_stage cannot hold completed_block alternative */
|
||||
const pending_block_header_state& get_pending_block_header_state()const {
|
||||
@@ -230,13 +229,13 @@ struct controller_impl {
|
||||
protocol_feature_manager protocol_features;
|
||||
controller::config conf;
|
||||
const chain_id_type chain_id; // read by thread_pool threads, value will not be changed
|
||||
bool replaying = false;
|
||||
db_read_mode read_mode = db_read_mode::HEAD;
|
||||
std::optional<fc::time_point> replay_head_time;
|
||||
db_read_mode read_mode = db_read_mode::SPECULATIVE;
|
||||
bool in_trx_requiring_checks = false; ///< if true, checks that are normally skipped on replay (e.g. auth checks) cannot be skipped
|
||||
std::optional<fc::microseconds> subjective_cpu_leeway;
|
||||
bool trusted_producer_light_validation = false;
|
||||
uint32_t snapshot_head_block = 0;
|
||||
named_thread_pool<eosio::chain::make_name_v("chain")> thread_pool;
|
||||
named_thread_pool thread_pool;
|
||||
platform_timer timer;
|
||||
deep_mind_handler* deep_mind_logger = nullptr;
|
||||
bool okay_to_print_integrity_hash_on_stop = false;
|
||||
@@ -256,7 +255,7 @@ struct controller_impl {
|
||||
prev = fork_db.root();
|
||||
}
|
||||
|
||||
if ( read_mode == db_read_mode::HEAD ) {
|
||||
if ( read_mode == db_read_mode::SPECULATIVE ) {
|
||||
EOS_ASSERT( head->block, block_validate_exception, "attempting to pop a block that was sparsely loaded from a snapshot");
|
||||
}
|
||||
|
||||
@@ -301,7 +300,7 @@ struct controller_impl {
|
||||
conf( cfg ),
|
||||
chain_id( chain_id ),
|
||||
read_mode( cfg.read_mode ),
|
||||
thread_pool()
|
||||
thread_pool( "chain", cfg.thread_pool_size )
|
||||
{
|
||||
fork_db.open( [this]( block_timestamp_type timestamp,
|
||||
const flat_set<digest_type>& cur_features,
|
||||
@@ -309,11 +308,6 @@ struct controller_impl {
|
||||
{ check_protocol_features( timestamp, cur_features, new_features ); }
|
||||
);
|
||||
|
||||
thread_pool.start( cfg.thread_pool_size, [this]( const fc::exception& e ) {
|
||||
elog( "Exception in chain thread pool, exiting: ${e}", ("e", e.to_detail_string()) );
|
||||
if( shutdown ) shutdown();
|
||||
} );
|
||||
|
||||
set_activation_handler<builtin_protocol_feature_t::preactivate_feature>();
|
||||
set_activation_handler<builtin_protocol_feature_t::replace_deferred>();
|
||||
set_activation_handler<builtin_protocol_feature_t::get_sender>();
|
||||
@@ -415,7 +409,7 @@ struct controller_impl {
|
||||
std::vector<std::future<std::vector<char>>> v;
|
||||
v.reserve( branch.size() );
|
||||
for( auto bitr = branch.rbegin(); bitr != branch.rend(); ++bitr ) {
|
||||
v.emplace_back( post_async_task( thread_pool.get_executor(), [b=(*bitr)->block]() { return fc::raw::pack(*b); } ) );
|
||||
v.emplace_back( async_thread_pool( thread_pool.get_executor(), [b=(*bitr)->block]() { return fc::raw::pack(*b); } ) );
|
||||
}
|
||||
auto it = v.begin();
|
||||
|
||||
@@ -488,7 +482,8 @@ struct controller_impl {
|
||||
}
|
||||
|
||||
auto blog_head = blog.head();
|
||||
replaying = true;
|
||||
auto blog_head_time = blog_head ? blog_head->timestamp.to_time_point() : fork_db.root()->header.timestamp.to_time_point();
|
||||
replay_head_time = blog_head_time;
|
||||
auto start_block_num = head->block_num + 1;
|
||||
auto start = fc::time_point::now();
|
||||
|
||||
@@ -556,7 +551,7 @@ struct controller_impl {
|
||||
ilog( "replayed ${n} blocks in ${duration} seconds, ${mspb} ms/block",
|
||||
("n", head->block_num + 1 - start_block_num)("duration", (end-start).count()/1000000)
|
||||
("mspb", ((end-start).count()/1000.0)/(head->block_num-start_block_num)) );
|
||||
replaying = false;
|
||||
replay_head_time.reset();
|
||||
|
||||
if( except_ptr ) {
|
||||
std::rethrow_exception( except_ptr );
|
||||
@@ -825,10 +820,7 @@ struct controller_impl {
|
||||
});
|
||||
}
|
||||
|
||||
void add_to_snapshot( const snapshot_writer_ptr& snapshot ) {
|
||||
// clear in case the previous call to clear did not finish in time of deadline
|
||||
clear_expired_input_transactions( fc::time_point::maximum() );
|
||||
|
||||
void add_to_snapshot( const snapshot_writer_ptr& snapshot ) const {
|
||||
snapshot->write_section<chain_snapshot_header>([this]( auto §ion ){
|
||||
section.add_row(chain_snapshot_header(), db);
|
||||
});
|
||||
@@ -1001,7 +993,7 @@ struct controller_impl {
|
||||
);
|
||||
}
|
||||
|
||||
sha256 calculate_integrity_hash() {
|
||||
sha256 calculate_integrity_hash() const {
|
||||
sha256::encoder enc;
|
||||
auto hash_writer = std::make_shared<integrity_hash_snapshot_writer>(enc);
|
||||
add_to_snapshot(hash_writer);
|
||||
@@ -1260,8 +1252,7 @@ struct controller_impl {
|
||||
uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time = false )
|
||||
{ try {
|
||||
|
||||
auto start = fc::time_point::now();
|
||||
const bool validating = !self.is_speculative_block();
|
||||
const bool validating = !self.is_producing_block();
|
||||
EOS_ASSERT( !validating || explicit_billed_cpu_time, transaction_exception, "validating requires explicit billing" );
|
||||
|
||||
maybe_session undo_session;
|
||||
@@ -1300,10 +1291,6 @@ struct controller_impl {
|
||||
trace->scheduled = true;
|
||||
trace->receipt = push_receipt( gtrx.trx_id, transaction_receipt::expired, billed_cpu_time_us, 0 ); // expire the transaction
|
||||
trace->account_ram_delta = account_delta( gtrx.payer, trx_removal_ram_delta );
|
||||
trace->elapsed = fc::time_point::now() - start;
|
||||
pending->_block_report.total_cpu_usage_us += billed_cpu_time_us;
|
||||
pending->_block_report.total_elapsed_time += trace->elapsed;
|
||||
pending->_block_report.total_time += trace->elapsed;
|
||||
emit( self.accepted_transaction, trx );
|
||||
dmlog_applied_transaction(trace);
|
||||
emit( self.applied_transaction, std::tie(trace, trx->packed_trx()) );
|
||||
@@ -1334,7 +1321,7 @@ struct controller_impl {
|
||||
trace->error_code = controller::convert_exception_to_error_code( e );
|
||||
trace->except = e;
|
||||
trace->except_ptr = std::current_exception();
|
||||
trace->elapsed = fc::time_point::now() - start;
|
||||
trace->elapsed = fc::time_point::now() - trx_context.start;
|
||||
|
||||
if (auto dm_logger = get_deep_mind_logger()) {
|
||||
dm_logger->on_fail_deferred();
|
||||
@@ -1344,7 +1331,7 @@ struct controller_impl {
|
||||
try {
|
||||
trx_context.init_for_deferred_trx( gtrx.published );
|
||||
|
||||
if( trx_context.enforce_whiteblacklist && self.is_speculative_block() ) {
|
||||
if( trx_context.enforce_whiteblacklist && pending->_block_status == controller::block_status::incomplete ) {
|
||||
flat_set<account_name> actors;
|
||||
for( const auto& act : trx->packed_trx()->get_transaction().actions ) {
|
||||
for( const auto& auth : act.authorization ) {
|
||||
@@ -1378,11 +1365,6 @@ struct controller_impl {
|
||||
|
||||
restore.cancel();
|
||||
|
||||
pending->_block_report.total_net_usage += trace->net_usage;
|
||||
pending->_block_report.total_cpu_usage_us += trace->receipt->cpu_usage_us;
|
||||
pending->_block_report.total_elapsed_time += trace->elapsed;
|
||||
pending->_block_report.total_time += fc::time_point::now() - start;
|
||||
|
||||
return trace;
|
||||
} catch( const disallowed_transaction_extensions_bad_block_exception& ) {
|
||||
throw;
|
||||
@@ -1413,18 +1395,13 @@ struct controller_impl {
|
||||
trace = error_trace;
|
||||
if( !trace->except_ptr ) {
|
||||
trace->account_ram_delta = account_delta( gtrx.payer, trx_removal_ram_delta );
|
||||
trace->elapsed = fc::time_point::now() - start;
|
||||
emit( self.accepted_transaction, trx );
|
||||
dmlog_applied_transaction(trace);
|
||||
emit( self.applied_transaction, std::tie(trace, trx->packed_trx()) );
|
||||
undo_session.squash();
|
||||
pending->_block_report.total_net_usage += trace->net_usage;
|
||||
if( trace->receipt ) pending->_block_report.total_cpu_usage_us += trace->receipt->cpu_usage_us;
|
||||
pending->_block_report.total_elapsed_time += trace->elapsed;
|
||||
pending->_block_report.total_time += trace->elapsed;
|
||||
return trace;
|
||||
}
|
||||
trace->elapsed = fc::time_point::now() - start;
|
||||
trace->elapsed = fc::time_point::now() - trx_context.start;
|
||||
}
|
||||
|
||||
// Only subjective OR hard failure logic below:
|
||||
@@ -1471,11 +1448,6 @@ struct controller_impl {
|
||||
emit( self.applied_transaction, std::tie(trace, trx->packed_trx()) );
|
||||
}
|
||||
|
||||
pending->_block_report.total_net_usage += trace->net_usage;
|
||||
if( trace->receipt ) pending->_block_report.total_cpu_usage_us += trace->receipt->cpu_usage_us;
|
||||
pending->_block_report.total_elapsed_time += trace->elapsed;
|
||||
pending->_block_report.total_time += fc::time_point::now() - start;
|
||||
|
||||
return trace;
|
||||
} FC_CAPTURE_AND_RETHROW() } /// push_scheduled_transaction
|
||||
|
||||
@@ -1517,7 +1489,7 @@ struct controller_impl {
|
||||
transaction_trace_ptr trace;
|
||||
try {
|
||||
auto start = fc::time_point::now();
|
||||
const bool check_auth = !self.skip_auth_check() && !trx->implicit();
|
||||
const bool check_auth = !self.skip_auth_check() && !trx->implicit;
|
||||
const fc::microseconds sig_cpu_usage = trx->signature_cpu_usage();
|
||||
|
||||
if( !explicit_billed_cpu_time ) {
|
||||
@@ -1532,8 +1504,8 @@ struct controller_impl {
|
||||
|
||||
const signed_transaction& trn = trx->packed_trx()->get_signed_transaction();
|
||||
transaction_checktime_timer trx_timer(timer);
|
||||
transaction_context trx_context(self, *trx->packed_trx(), std::move(trx_timer), start, trx->get_trx_type());
|
||||
if ((bool)subjective_cpu_leeway && self.is_speculative_block()) {
|
||||
transaction_context trx_context(self, *trx->packed_trx(), std::move(trx_timer), start, trx->read_only);
|
||||
if ((bool)subjective_cpu_leeway && pending->_block_status == controller::block_status::incomplete) {
|
||||
trx_context.leeway = *subjective_cpu_leeway;
|
||||
}
|
||||
trx_context.block_deadline = block_deadline;
|
||||
@@ -1552,12 +1524,14 @@ struct controller_impl {
|
||||
};
|
||||
|
||||
try {
|
||||
if( trx->implicit() ) {
|
||||
if( trx->implicit ) {
|
||||
trx_context.init_for_implicit_trx();
|
||||
trx_context.enforce_whiteblacklist = false;
|
||||
} else {
|
||||
bool skip_recording = replay_head_time && (time_point(trn.expiration) < *replay_head_time);
|
||||
trx_context.init_for_input_trx( trx->packed_trx()->get_unprunable_size(),
|
||||
trx->packed_trx()->get_prunable_size() );
|
||||
trx->packed_trx()->get_prunable_size(),
|
||||
skip_recording);
|
||||
}
|
||||
|
||||
trx_context.delay = fc::seconds(trn.delay_sec);
|
||||
@@ -1570,7 +1544,7 @@ struct controller_impl {
|
||||
trx_context.delay,
|
||||
[&trx_context](){ trx_context.checktime(); },
|
||||
false,
|
||||
trx->is_dry_run()
|
||||
trx->read_only
|
||||
);
|
||||
}
|
||||
trx_context.exec();
|
||||
@@ -1578,7 +1552,7 @@ struct controller_impl {
|
||||
|
||||
auto restore = make_block_restore_point();
|
||||
|
||||
if (!trx->implicit()) {
|
||||
if (!trx->implicit) {
|
||||
transaction_receipt::status_enum s = (trx_context.delay == fc::seconds(0))
|
||||
? transaction_receipt::executed
|
||||
: transaction_receipt::delayed;
|
||||
@@ -1597,7 +1571,7 @@ struct controller_impl {
|
||||
std::move(trx_context.executed_action_receipt_digests) );
|
||||
|
||||
// call the accept signal but only once for this transaction
|
||||
if (!trx->is_dry_run()) {
|
||||
if (!trx->read_only) {
|
||||
if (!trx->accepted) {
|
||||
trx->accepted = true;
|
||||
emit(self.accepted_transaction, trx);
|
||||
@@ -1608,28 +1582,14 @@ struct controller_impl {
|
||||
}
|
||||
|
||||
|
||||
if ( trx->is_dry_run() ) {
|
||||
// remove trx from pending block by not canceling 'restore'
|
||||
trx_context.undo(); // this will happen automatically in destructor, but make it more explicit
|
||||
} else if ( pending->_block_status == controller::block_status::ephemeral ) {
|
||||
// An ephemeral block will never become a full block, but on a producer node the trxs should be saved
|
||||
// in the un-applied transaction queue for execution during block production. For a non-producer node
|
||||
// save the trxs in the un-applied transaction queue for use during block validation to skip signature
|
||||
// recovery.
|
||||
restore.cancel(); // maintain trx metas for abort block
|
||||
trx_context.undo(); // this will happen automatically in destructor, but make it more explicit
|
||||
if ( (read_mode != db_read_mode::SPECULATIVE && pending->_block_status == controller::block_status::incomplete) || trx->read_only ) {
|
||||
//this may happen automatically in destructor, but I prefer make it more explicit
|
||||
trx_context.undo();
|
||||
} else {
|
||||
restore.cancel();
|
||||
trx_context.squash();
|
||||
}
|
||||
|
||||
if( !trx->is_dry_run() ) {
|
||||
pending->_block_report.total_net_usage += trace->net_usage;
|
||||
pending->_block_report.total_cpu_usage_us += trace->receipt->cpu_usage_us;
|
||||
pending->_block_report.total_elapsed_time += trace->elapsed;
|
||||
pending->_block_report.total_time += fc::time_point::now() - start;
|
||||
}
|
||||
|
||||
return trace;
|
||||
} catch( const disallowed_transaction_extensions_bad_block_exception& ) {
|
||||
throw;
|
||||
@@ -1646,15 +1606,10 @@ struct controller_impl {
|
||||
handle_exception(wrapper);
|
||||
}
|
||||
|
||||
if (!trx->is_dry_run()) {
|
||||
emit(self.accepted_transaction, trx);
|
||||
dmlog_applied_transaction(trace);
|
||||
emit(self.applied_transaction, std::tie(trace, trx->packed_trx()));
|
||||
|
||||
pending->_block_report.total_net_usage += trace->net_usage;
|
||||
if( trace->receipt ) pending->_block_report.total_cpu_usage_us += trace->receipt->cpu_usage_us;
|
||||
pending->_block_report.total_elapsed_time += trace->elapsed;
|
||||
pending->_block_report.total_time += fc::time_point::now() - start;
|
||||
if (!trx->read_only) {
|
||||
emit(self.accepted_transaction, trx);
|
||||
dmlog_applied_transaction(trace);
|
||||
emit(self.applied_transaction, std::tie(trace, trx->packed_trx()));
|
||||
}
|
||||
|
||||
return trace;
|
||||
@@ -1697,9 +1652,8 @@ struct controller_impl {
|
||||
auto& bb = std::get<building_block>(pending->_block_stage);
|
||||
const auto& pbhs = bb._pending_block_header_state;
|
||||
|
||||
// block status is either ephemeral or incomplete. Modify state of speculative block only if we are building a
|
||||
// speculative incomplete block (otherwise we need clean state for head mode, ephemeral block)
|
||||
if ( pending->_block_status != controller::block_status::ephemeral )
|
||||
// modify state of speculative block only if we are in speculative read mode (otherwise we need clean state for head or read-only modes)
|
||||
if ( read_mode == db_read_mode::SPECULATIVE || pending->_block_status != controller::block_status::incomplete )
|
||||
{
|
||||
const auto& pso = db.get<protocol_state_object>();
|
||||
|
||||
@@ -1775,7 +1729,7 @@ struct controller_impl {
|
||||
)
|
||||
{
|
||||
// Promote proposed schedule to pending schedule.
|
||||
if( !replaying ) {
|
||||
if( !replay_head_time ) {
|
||||
ilog( "promoting proposed schedule (set in block ${proposed_num}) to pending; current block: ${n} lib: ${lib} schedule: ${schedule} ",
|
||||
("proposed_num", *gpo.proposed_schedule_block_num)("n", pbhs.block_num)
|
||||
("lib", pbhs.dpos_irreversible_blocknum)
|
||||
@@ -1840,17 +1794,17 @@ struct controller_impl {
|
||||
|
||||
auto& bb = std::get<building_block>(pending->_block_stage);
|
||||
|
||||
auto action_merkle_fut = post_async_task( thread_pool.get_executor(),
|
||||
[ids{std::move( bb._action_receipt_digests )}]() mutable {
|
||||
return merkle( std::move( ids ) );
|
||||
} );
|
||||
auto action_merkle_fut = async_thread_pool( thread_pool.get_executor(),
|
||||
[ids{std::move( bb._action_receipt_digests )}]() mutable {
|
||||
return merkle( std::move( ids ) );
|
||||
} );
|
||||
const bool calc_trx_merkle = !std::holds_alternative<checksum256_type>(bb._trx_mroot_or_receipt_digests);
|
||||
std::future<checksum256_type> trx_merkle_fut;
|
||||
if( calc_trx_merkle ) {
|
||||
trx_merkle_fut = post_async_task( thread_pool.get_executor(),
|
||||
[ids{std::move( std::get<digests_t>(bb._trx_mroot_or_receipt_digests) )}]() mutable {
|
||||
return merkle( std::move( ids ) );
|
||||
} );
|
||||
trx_merkle_fut = async_thread_pool( thread_pool.get_executor(),
|
||||
[ids{std::move( std::get<digests_t>(bb._trx_mroot_or_receipt_digests) )}]() mutable {
|
||||
return merkle( std::move( ids ) );
|
||||
} );
|
||||
}
|
||||
|
||||
// Update resource limits:
|
||||
@@ -2109,6 +2063,12 @@ struct controller_impl {
|
||||
EOS_ASSERT( r == static_cast<const transaction_receipt_header&>(receipt),
|
||||
block_validate_exception, "receipt does not match, ${lhs} != ${rhs}",
|
||||
("lhs", r)("rhs", static_cast<const transaction_receipt_header&>(receipt)) );
|
||||
|
||||
if( trace ) {
|
||||
br.total_net_usage += trace->net_usage;
|
||||
if( trace->receipt ) br.total_cpu_usage_us += trace->receipt->cpu_usage_us;
|
||||
br.total_elapsed_time += trace->elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
finalize_block();
|
||||
@@ -2129,7 +2089,6 @@ struct controller_impl {
|
||||
// create completed_block with the existing block_state as we just verified it is the same as assembled_block
|
||||
pending->_block_stage = completed_block{ bsp };
|
||||
|
||||
br = pending->_block_report; // copy before commit block destroys pending
|
||||
commit_block(false);
|
||||
br.total_time = fc::time_point::now() - start;
|
||||
return;
|
||||
@@ -2148,65 +2107,44 @@ struct controller_impl {
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() } /// apply_block
|
||||
|
||||
|
||||
// thread safe, expected to be called from thread other than the main thread
|
||||
block_state_ptr create_block_state_i( const block_id_type& id, const signed_block_ptr& b, const block_header_state& prev ) {
|
||||
auto trx_mroot = calculate_trx_merkle( b->transactions );
|
||||
EOS_ASSERT( b->transaction_mroot == trx_mroot, block_validate_exception,
|
||||
"invalid block transaction merkle root ${b} != ${c}", ("b", b->transaction_mroot)("c", trx_mroot) );
|
||||
|
||||
const bool skip_validate_signee = false;
|
||||
auto bsp = std::make_shared<block_state>(
|
||||
prev,
|
||||
b,
|
||||
protocol_features.get_protocol_feature_set(),
|
||||
[this]( block_timestamp_type timestamp,
|
||||
const flat_set<digest_type>& cur_features,
|
||||
const vector<digest_type>& new_features )
|
||||
{ check_protocol_features( timestamp, cur_features, new_features ); },
|
||||
skip_validate_signee
|
||||
);
|
||||
|
||||
EOS_ASSERT( id == bsp->id, block_validate_exception,
|
||||
"provided id ${id} does not match block id ${bid}", ("id", id)("bid", bsp->id) );
|
||||
return bsp;
|
||||
}
|
||||
|
||||
std::future<block_state_ptr> create_block_state_future( const block_id_type& id, const signed_block_ptr& b ) {
|
||||
EOS_ASSERT( b, block_validate_exception, "null block" );
|
||||
|
||||
return post_async_task( thread_pool.get_executor(), [b, id, control=this]() {
|
||||
// no reason for a block_state if fork_db already knows about block
|
||||
auto existing = control->fork_db.get_block( id );
|
||||
EOS_ASSERT( !existing, fork_database_exception, "we already know about this block: ${id}", ("id", id) );
|
||||
|
||||
auto prev = control->fork_db.get_block_header( b->previous );
|
||||
EOS_ASSERT( prev, unlinkable_block_exception,
|
||||
"unlinkable block ${id}", ("id", id)("previous", b->previous) );
|
||||
|
||||
return control->create_block_state_i( id, b, *prev );
|
||||
} );
|
||||
}
|
||||
|
||||
// thread safe, expected to be called from thread other than the main thread
|
||||
block_state_ptr create_block_state( const block_id_type& id, const signed_block_ptr& b ) {
|
||||
EOS_ASSERT( b, block_validate_exception, "null block" );
|
||||
|
||||
// no reason for a block_state if fork_db already knows about block
|
||||
auto existing = fork_db.get_block( id );
|
||||
EOS_ASSERT( !existing, fork_database_exception, "we already know about this block: ${id}", ("id", id) );
|
||||
|
||||
// previous not found could mean that previous block not applied yet
|
||||
auto prev = fork_db.get_block_header( b->previous );
|
||||
if( !prev ) return {};
|
||||
EOS_ASSERT( prev, unlinkable_block_exception,
|
||||
"unlinkable block ${id}", ("id", id)("previous", b->previous) );
|
||||
|
||||
return create_block_state_i( id, b, *prev );
|
||||
return async_thread_pool( thread_pool.get_executor(), [b, prev, id, control=this]() {
|
||||
const bool skip_validate_signee = false;
|
||||
|
||||
auto trx_mroot = calculate_trx_merkle( b->transactions );
|
||||
EOS_ASSERT( b->transaction_mroot == trx_mroot, block_validate_exception,
|
||||
"invalid block transaction merkle root ${b} != ${c}", ("b", b->transaction_mroot)("c", trx_mroot) );
|
||||
|
||||
auto bsp = std::make_shared<block_state>(
|
||||
*prev,
|
||||
move( b ),
|
||||
control->protocol_features.get_protocol_feature_set(),
|
||||
[control]( block_timestamp_type timestamp,
|
||||
const flat_set<digest_type>& cur_features,
|
||||
const vector<digest_type>& new_features )
|
||||
{ control->check_protocol_features( timestamp, cur_features, new_features ); },
|
||||
skip_validate_signee
|
||||
);
|
||||
|
||||
EOS_ASSERT( id == bsp->id, block_validate_exception,
|
||||
"provided id ${id} does not match block id ${bid}", ("id", id)("bid", bsp->id) );
|
||||
return bsp;
|
||||
} );
|
||||
}
|
||||
|
||||
void push_block( controller::block_report& br,
|
||||
const block_state_ptr& bsp,
|
||||
const forked_branch_callback& forked_branch_cb,
|
||||
const trx_meta_cache_lookup& trx_lookup )
|
||||
std::future<block_state_ptr>& block_state_future,
|
||||
const forked_branch_callback& forked_branch_cb, const trx_meta_cache_lookup& trx_lookup )
|
||||
{
|
||||
controller::block_status s = controller::block_status::complete;
|
||||
EOS_ASSERT(!pending, block_validate_exception, "it is not valid to push a block when there is a pending block");
|
||||
@@ -2215,7 +2153,7 @@ struct controller_impl {
|
||||
trusted_producer_light_validation = old_value;
|
||||
});
|
||||
try {
|
||||
EOS_ASSERT( bsp, block_validate_exception, "null block" );
|
||||
block_state_ptr bsp = block_state_future.get();
|
||||
const auto& b = bsp->block;
|
||||
|
||||
if( conf.terminate_at_block > 0 && conf.terminate_at_block < self.head_block_num()) {
|
||||
@@ -2456,7 +2394,7 @@ struct controller_impl {
|
||||
//Look for expired transactions in the deduplication list, and remove them.
|
||||
auto& transaction_idx = db.get_mutable_index<transaction_multi_index>();
|
||||
const auto& dedupe_index = transaction_idx.indices().get<by_expiration>();
|
||||
auto now = self.is_building_block() ? self.pending_block_time() : self.head_block_time();
|
||||
auto now = self.pending_block_time();
|
||||
const auto total = dedupe_index.size();
|
||||
uint32_t num_removed = 0;
|
||||
while( (!dedupe_index.empty()) && ( now > fc::time_point(dedupe_index.begin()->expiration) ) ) {
|
||||
@@ -2732,7 +2670,7 @@ void controller::preactivate_feature( const digest_type& feature_digest ) {
|
||||
auto status = pfs.is_recognized( feature_digest, cur_time );
|
||||
switch( status ) {
|
||||
case protocol_feature_set::recognized_t::unrecognized:
|
||||
if( is_speculative_block() ) {
|
||||
if( is_producing_block() ) {
|
||||
EOS_THROW( subjective_block_production_exception,
|
||||
"protocol feature with digest '${digest}' is unrecognized", ("digest", feature_digest) );
|
||||
} else {
|
||||
@@ -2741,7 +2679,7 @@ void controller::preactivate_feature( const digest_type& feature_digest ) {
|
||||
}
|
||||
break;
|
||||
case protocol_feature_set::recognized_t::disabled:
|
||||
if( is_speculative_block() ) {
|
||||
if( is_producing_block() ) {
|
||||
EOS_THROW( subjective_block_production_exception,
|
||||
"protocol feature with digest '${digest}' is disabled", ("digest", feature_digest) );
|
||||
} else {
|
||||
@@ -2750,7 +2688,7 @@ void controller::preactivate_feature( const digest_type& feature_digest ) {
|
||||
}
|
||||
break;
|
||||
case protocol_feature_set::recognized_t::too_early:
|
||||
if( is_speculative_block() ) {
|
||||
if( is_producing_block() ) {
|
||||
EOS_THROW( subjective_block_production_exception,
|
||||
"${timestamp} is too early for the earliest allowed activation time of the protocol feature with digest '${digest}'", ("digest", feature_digest)("timestamp", cur_time) );
|
||||
} else {
|
||||
@@ -2761,7 +2699,7 @@ void controller::preactivate_feature( const digest_type& feature_digest ) {
|
||||
case protocol_feature_set::recognized_t::ready:
|
||||
break;
|
||||
default:
|
||||
if( is_speculative_block() ) {
|
||||
if( is_producing_block() ) {
|
||||
EOS_THROW( subjective_block_production_exception, "unexpected recognized_t status" );
|
||||
} else {
|
||||
EOS_THROW( protocol_feature_bad_block_exception, "unexpected recognized_t status" );
|
||||
@@ -2853,10 +2791,32 @@ void controller::validate_protocol_features( const vector<digest_type>& features
|
||||
features_to_activate );
|
||||
}
|
||||
|
||||
void controller::start_block( block_timestamp_type when, uint16_t confirm_block_count )
|
||||
{
|
||||
validate_db_available_size();
|
||||
|
||||
EOS_ASSERT( !my->pending, block_validate_exception, "pending block already exists" );
|
||||
|
||||
vector<digest_type> new_protocol_feature_activations;
|
||||
|
||||
const auto& pso = my->db.get<protocol_state_object>();
|
||||
if( pso.preactivated_protocol_features.size() > 0 ) {
|
||||
for( const auto& f : pso.preactivated_protocol_features ) {
|
||||
new_protocol_feature_activations.emplace_back( f );
|
||||
}
|
||||
}
|
||||
|
||||
if( new_protocol_feature_activations.size() > 0 ) {
|
||||
validate_protocol_features( new_protocol_feature_activations );
|
||||
}
|
||||
|
||||
my->start_block( when, confirm_block_count, new_protocol_feature_activations,
|
||||
block_status::incomplete, std::optional<block_id_type>(), fc::time_point::maximum() );
|
||||
}
|
||||
|
||||
void controller::start_block( block_timestamp_type when,
|
||||
uint16_t confirm_block_count,
|
||||
const vector<digest_type>& new_protocol_feature_activations,
|
||||
block_status bs,
|
||||
const fc::time_point& deadline )
|
||||
{
|
||||
validate_db_available_size();
|
||||
@@ -2865,13 +2825,11 @@ void controller::start_block( block_timestamp_type when,
|
||||
validate_protocol_features( new_protocol_feature_activations );
|
||||
}
|
||||
|
||||
EOS_ASSERT( bs == block_status::incomplete || bs == block_status::ephemeral, block_validate_exception, "speculative block type required" );
|
||||
|
||||
my->start_block( when, confirm_block_count, new_protocol_feature_activations,
|
||||
bs, std::optional<block_id_type>(), deadline );
|
||||
block_status::incomplete, std::optional<block_id_type>(), deadline );
|
||||
}
|
||||
|
||||
block_state_ptr controller::finalize_block( block_report& br, const signer_callback_type& signer_callback ) {
|
||||
block_state_ptr controller::finalize_block( const signer_callback_type& signer_callback ) {
|
||||
validate_db_available_size();
|
||||
|
||||
my->finalize_block();
|
||||
@@ -2892,8 +2850,6 @@ block_state_ptr controller::finalize_block( block_report& br, const signer_callb
|
||||
|
||||
my->pending->_block_stage = completed_block{ bsp };
|
||||
|
||||
br = my->pending->_block_report;
|
||||
|
||||
return bsp;
|
||||
}
|
||||
|
||||
@@ -2914,17 +2870,12 @@ std::future<block_state_ptr> controller::create_block_state_future( const block_
|
||||
return my->create_block_state_future( id, b );
|
||||
}
|
||||
|
||||
block_state_ptr controller::create_block_state( const block_id_type& id, const signed_block_ptr& b ) const {
|
||||
return my->create_block_state( id, b );
|
||||
}
|
||||
|
||||
void controller::push_block( controller::block_report& br,
|
||||
const block_state_ptr& bsp,
|
||||
const forked_branch_callback& forked_branch_cb,
|
||||
const trx_meta_cache_lookup& trx_lookup )
|
||||
std::future<block_state_ptr>& block_state_future,
|
||||
const forked_branch_callback& forked_branch_cb, const trx_meta_cache_lookup& trx_lookup )
|
||||
{
|
||||
validate_db_available_size();
|
||||
my->push_block( br, bsp, forked_branch_cb, trx_lookup );
|
||||
my->push_block( br, block_state_future, forked_branch_cb, trx_lookup );
|
||||
}
|
||||
|
||||
transaction_trace_ptr controller::push_transaction( const transaction_metadata_ptr& trx,
|
||||
@@ -2933,7 +2884,7 @@ transaction_trace_ptr controller::push_transaction( const transaction_metadata_p
|
||||
int64_t subjective_cpu_bill_us ) {
|
||||
validate_db_available_size();
|
||||
EOS_ASSERT( get_read_mode() != db_read_mode::IRREVERSIBLE, transaction_type_exception, "push transaction not allowed in irreversible mode" );
|
||||
EOS_ASSERT( trx && !trx->implicit() && !trx->scheduled(), transaction_type_exception, "Implicit/Scheduled transaction not allowed" );
|
||||
EOS_ASSERT( trx && !trx->implicit && !trx->scheduled, transaction_type_exception, "Implicit/Scheduled transaction not allowed" );
|
||||
return my->push_transaction(trx, block_deadline, max_transaction_time, billed_cpu_time_us, explicit_billed_cpu_time, subjective_cpu_bill_us );
|
||||
}
|
||||
|
||||
@@ -3153,11 +3104,11 @@ block_id_type controller::get_block_id_for_num( uint32_t block_num )const { try
|
||||
return id;
|
||||
} FC_CAPTURE_AND_RETHROW( (block_num) ) }
|
||||
|
||||
sha256 controller::calculate_integrity_hash() { try {
|
||||
sha256 controller::calculate_integrity_hash()const { try {
|
||||
return my->calculate_integrity_hash();
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
void controller::write_snapshot( const snapshot_writer_ptr& snapshot ) {
|
||||
void controller::write_snapshot( const snapshot_writer_ptr& snapshot ) const {
|
||||
EOS_ASSERT( !my->pending, block_validate_exception, "cannot take a consistent snapshot with a pending block" );
|
||||
return my->add_to_snapshot(snapshot);
|
||||
}
|
||||
@@ -3366,14 +3317,14 @@ bool controller::is_building_block()const {
|
||||
return my->pending.has_value();
|
||||
}
|
||||
|
||||
bool controller::is_speculative_block()const {
|
||||
bool controller::is_producing_block()const {
|
||||
if( !my->pending ) return false;
|
||||
|
||||
return (my->pending->_block_status == block_status::incomplete || my->pending->_block_status == block_status::ephemeral );
|
||||
return (my->pending->_block_status == block_status::incomplete);
|
||||
}
|
||||
|
||||
bool controller::is_ram_billing_in_notify_allowed()const {
|
||||
return my->conf.disable_all_subjective_mitigations || !is_speculative_block() || my->conf.allow_ram_billing_in_notify;
|
||||
return my->conf.disable_all_subjective_mitigations || !is_producing_block() || my->conf.allow_ram_billing_in_notify;
|
||||
}
|
||||
|
||||
uint32_t controller::configured_subjective_signature_length_limit()const {
|
||||
|
||||
@@ -54,7 +54,7 @@ void validate_authority_precondition( const apply_context& context, const author
|
||||
}
|
||||
}
|
||||
|
||||
if( context.trx_context.enforce_whiteblacklist && context.control.is_speculative_block() ) {
|
||||
if( context.trx_context.enforce_whiteblacklist && context.control.is_producing_block() ) {
|
||||
for( const auto& p : auth.keys ) {
|
||||
context.control.check_key_list( p.key );
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
#include <fc/io/fstream.hpp>
|
||||
#include <fstream>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace eosio { namespace chain {
|
||||
using boost::multi_index_container;
|
||||
@@ -61,44 +60,27 @@ namespace eosio { namespace chain {
|
||||
}
|
||||
|
||||
struct fork_database_impl {
|
||||
explicit fork_database_impl( const fc::path& data_dir )
|
||||
:datadir(data_dir)
|
||||
fork_database_impl( fork_database& self, const fc::path& data_dir )
|
||||
:self(self)
|
||||
,datadir(data_dir)
|
||||
{}
|
||||
|
||||
std::shared_mutex mtx;
|
||||
fork_database& self;
|
||||
fork_multi_index_type index;
|
||||
block_state_ptr root; // Only uses the block_header_state portion
|
||||
block_state_ptr head;
|
||||
fc::path datadir;
|
||||
|
||||
void open_impl( const std::function<void( block_timestamp_type,
|
||||
const flat_set<digest_type>&,
|
||||
const vector<digest_type>& )>& validator );
|
||||
void close_impl();
|
||||
|
||||
|
||||
block_header_state_ptr get_block_header_impl( const block_id_type& id )const;
|
||||
block_state_ptr get_block_impl( const block_id_type& id )const;
|
||||
void reset_impl( const block_header_state& root_bhs );
|
||||
void rollback_head_to_root_impl();
|
||||
void advance_root_impl( const block_id_type& id );
|
||||
void remove_impl( const block_id_type& id );
|
||||
branch_type fetch_branch_impl( const block_id_type& h, uint32_t trim_after_block_num )const;
|
||||
block_state_ptr search_on_branch_impl( const block_id_type& h, uint32_t block_num )const;
|
||||
pair<branch_type, branch_type> fetch_branch_from_impl( const block_id_type& first,
|
||||
const block_id_type& second )const;
|
||||
void mark_valid_impl( const block_state_ptr& h );
|
||||
|
||||
void add_impl( const block_state_ptr& n,
|
||||
bool ignore_duplicate, bool validate,
|
||||
const std::function<void( block_timestamp_type,
|
||||
const flat_set<digest_type>&,
|
||||
const vector<digest_type>& )>& validator );
|
||||
void add( const block_state_ptr& n,
|
||||
bool ignore_duplicate, bool validate,
|
||||
const std::function<void( block_timestamp_type,
|
||||
const flat_set<digest_type>&,
|
||||
const vector<digest_type>& )>& validator );
|
||||
};
|
||||
|
||||
|
||||
fork_database::fork_database( const fc::path& data_dir )
|
||||
:my( new fork_database_impl( data_dir ) )
|
||||
:my( new fork_database_impl( *this, data_dir ) )
|
||||
{}
|
||||
|
||||
|
||||
@@ -106,18 +88,10 @@ namespace eosio { namespace chain {
|
||||
const flat_set<digest_type>&,
|
||||
const vector<digest_type>& )>& validator )
|
||||
{
|
||||
std::lock_guard g( my->mtx );
|
||||
my->open_impl( validator );
|
||||
}
|
||||
if (!fc::is_directory(my->datadir))
|
||||
fc::create_directories(my->datadir);
|
||||
|
||||
void fork_database_impl::open_impl( const std::function<void( block_timestamp_type,
|
||||
const flat_set<digest_type>&,
|
||||
const vector<digest_type>& )>& validator )
|
||||
{
|
||||
if (!fc::is_directory(datadir))
|
||||
fc::create_directories(datadir);
|
||||
|
||||
auto fork_db_dat = datadir / config::forkdb_filename;
|
||||
auto fork_db_dat = my->datadir / config::forkdb_filename;
|
||||
if( fc::exists( fork_db_dat ) ) {
|
||||
try {
|
||||
string content;
|
||||
@@ -128,29 +102,29 @@ namespace eosio { namespace chain {
|
||||
// validate totem
|
||||
uint32_t totem = 0;
|
||||
fc::raw::unpack( ds, totem );
|
||||
EOS_ASSERT( totem == fork_database::magic_number, fork_database_exception,
|
||||
EOS_ASSERT( totem == magic_number, fork_database_exception,
|
||||
"Fork database file '${filename}' has unexpected magic number: ${actual_totem}. Expected ${expected_totem}",
|
||||
("filename", fork_db_dat.generic_string())
|
||||
("actual_totem", totem)
|
||||
("expected_totem", fork_database::magic_number)
|
||||
("expected_totem", magic_number)
|
||||
);
|
||||
|
||||
// validate version
|
||||
uint32_t version = 0;
|
||||
fc::raw::unpack( ds, version );
|
||||
EOS_ASSERT( version >= fork_database::min_supported_version && version <= fork_database::max_supported_version,
|
||||
EOS_ASSERT( version >= min_supported_version && version <= max_supported_version,
|
||||
fork_database_exception,
|
||||
"Unsupported version of fork database file '${filename}'. "
|
||||
"Fork database version is ${version} while code supports version(s) [${min},${max}]",
|
||||
("filename", fork_db_dat.generic_string())
|
||||
("version", version)
|
||||
("min", fork_database::min_supported_version)
|
||||
("max", fork_database::max_supported_version)
|
||||
("min", min_supported_version)
|
||||
("max", max_supported_version)
|
||||
);
|
||||
|
||||
block_header_state bhs;
|
||||
fc::raw::unpack( ds, bhs );
|
||||
reset_impl( bhs );
|
||||
reset( bhs );
|
||||
|
||||
unsigned_int size; fc::raw::unpack( ds, size );
|
||||
for( uint32_t i = 0, n = size.value; i < n; ++i ) {
|
||||
@@ -158,27 +132,27 @@ namespace eosio { namespace chain {
|
||||
fc::raw::unpack( ds, s );
|
||||
// do not populate transaction_metadatas, they will be created as needed in apply_block with appropriate key recovery
|
||||
s.header_exts = s.block->validate_and_extract_header_extensions();
|
||||
add_impl( std::make_shared<block_state>( std::move( s ) ), false, true, validator );
|
||||
my->add( std::make_shared<block_state>( move( s ) ), false, true, validator );
|
||||
}
|
||||
block_id_type head_id;
|
||||
fc::raw::unpack( ds, head_id );
|
||||
|
||||
if( root->id == head_id ) {
|
||||
head = root;
|
||||
if( my->root->id == head_id ) {
|
||||
my->head = my->root;
|
||||
} else {
|
||||
head = get_block_impl( head_id );
|
||||
EOS_ASSERT( head, fork_database_exception,
|
||||
my->head = get_block( head_id );
|
||||
EOS_ASSERT( my->head, fork_database_exception,
|
||||
"could not find head while reconstructing fork database from file; '${filename}' is likely corrupted",
|
||||
("filename", fork_db_dat.generic_string()) );
|
||||
}
|
||||
|
||||
auto candidate = index.get<by_lib_block_num>().begin();
|
||||
if( candidate == index.get<by_lib_block_num>().end() || !(*candidate)->is_valid() ) {
|
||||
EOS_ASSERT( head->id == root->id, fork_database_exception,
|
||||
auto candidate = my->index.get<by_lib_block_num>().begin();
|
||||
if( candidate == my->index.get<by_lib_block_num>().end() || !(*candidate)->is_valid() ) {
|
||||
EOS_ASSERT( my->head->id == my->root->id, fork_database_exception,
|
||||
"head not set to root despite no better option available; '${filename}' is likely corrupted",
|
||||
("filename", fork_db_dat.generic_string()) );
|
||||
} else {
|
||||
EOS_ASSERT( !first_preferred( **candidate, *head ), fork_database_exception,
|
||||
EOS_ASSERT( !first_preferred( **candidate, *my->head ), fork_database_exception,
|
||||
"head not set to best available option available; '${filename}' is likely corrupted",
|
||||
("filename", fork_db_dat.generic_string()) );
|
||||
}
|
||||
@@ -189,15 +163,10 @@ namespace eosio { namespace chain {
|
||||
}
|
||||
|
||||
void fork_database::close() {
|
||||
std::lock_guard g( my->mtx );
|
||||
my->close_impl();
|
||||
}
|
||||
auto fork_db_dat = my->datadir / config::forkdb_filename;
|
||||
|
||||
void fork_database_impl::close_impl() {
|
||||
auto fork_db_dat = datadir / config::forkdb_filename;
|
||||
|
||||
if( !root ) {
|
||||
if( index.size() > 0 ) {
|
||||
if( !my->root ) {
|
||||
if( my->index.size() > 0 ) {
|
||||
elog( "fork_database is in a bad state when closing; not writing out '${filename}'",
|
||||
("filename", fork_db_dat.generic_string()) );
|
||||
}
|
||||
@@ -205,13 +174,13 @@ namespace eosio { namespace chain {
|
||||
}
|
||||
|
||||
std::ofstream out( fork_db_dat.generic_string().c_str(), std::ios::out | std::ios::binary | std::ofstream::trunc );
|
||||
fc::raw::pack( out, fork_database::magic_number );
|
||||
fc::raw::pack( out, fork_database::max_supported_version ); // write out current version which is always max_supported_version
|
||||
fc::raw::pack( out, *static_cast<block_header_state*>(&*root) );
|
||||
uint32_t num_blocks_in_fork_db = index.size();
|
||||
fc::raw::pack( out, magic_number );
|
||||
fc::raw::pack( out, max_supported_version ); // write out current version which is always max_supported_version
|
||||
fc::raw::pack( out, *static_cast<block_header_state*>(&*my->root) );
|
||||
uint32_t num_blocks_in_fork_db = my->index.size();
|
||||
fc::raw::pack( out, unsigned_int{num_blocks_in_fork_db} );
|
||||
|
||||
const auto& indx = index.get<by_lib_block_num>();
|
||||
const auto& indx = my->index.get<by_lib_block_num>();
|
||||
|
||||
auto unvalidated_itr = indx.rbegin();
|
||||
auto unvalidated_end = boost::make_reverse_iterator( indx.lower_bound( false ) );
|
||||
@@ -246,40 +215,30 @@ namespace eosio { namespace chain {
|
||||
fc::raw::pack( out, *(*itr) );
|
||||
}
|
||||
|
||||
if( head ) {
|
||||
fc::raw::pack( out, head->id );
|
||||
if( my->head ) {
|
||||
fc::raw::pack( out, my->head->id );
|
||||
} else {
|
||||
elog( "head not set in fork database; '${filename}' will be corrupted",
|
||||
("filename", fork_db_dat.generic_string()) );
|
||||
}
|
||||
|
||||
index.clear();
|
||||
my->index.clear();
|
||||
}
|
||||
|
||||
fork_database::~fork_database() {
|
||||
my->close_impl();
|
||||
close();
|
||||
}
|
||||
|
||||
void fork_database::reset( const block_header_state& root_bhs ) {
|
||||
std::lock_guard g( my->mtx );
|
||||
my->reset_impl(root_bhs);
|
||||
}
|
||||
|
||||
void fork_database_impl::reset_impl( const block_header_state& root_bhs ) {
|
||||
index.clear();
|
||||
root = std::make_shared<block_state>();
|
||||
static_cast<block_header_state&>(*root) = root_bhs;
|
||||
root->validated = true;
|
||||
head = root;
|
||||
my->index.clear();
|
||||
my->root = std::make_shared<block_state>();
|
||||
static_cast<block_header_state&>(*my->root) = root_bhs;
|
||||
my->root->validated = true;
|
||||
my->head = my->root;
|
||||
}
|
||||
|
||||
void fork_database::rollback_head_to_root() {
|
||||
std::lock_guard g( my->mtx );
|
||||
my->rollback_head_to_root_impl();
|
||||
}
|
||||
|
||||
void fork_database_impl::rollback_head_to_root_impl() {
|
||||
auto& by_id_idx = index.get<by_block_id>();
|
||||
auto& by_id_idx = my->index.get<by_block_id>();
|
||||
auto itr = by_id_idx.begin();
|
||||
while (itr != by_id_idx.end()) {
|
||||
by_id_idx.modify( itr, [&]( block_state_ptr& bsp ) {
|
||||
@@ -287,18 +246,13 @@ namespace eosio { namespace chain {
|
||||
} );
|
||||
++itr;
|
||||
}
|
||||
head = root;
|
||||
my->head = my->root;
|
||||
}
|
||||
|
||||
void fork_database::advance_root( const block_id_type& id ) {
|
||||
std::lock_guard g( my->mtx );
|
||||
my->advance_root_impl( id );
|
||||
}
|
||||
EOS_ASSERT( my->root, fork_database_exception, "root not yet set" );
|
||||
|
||||
void fork_database_impl::advance_root_impl( const block_id_type& id ) {
|
||||
EOS_ASSERT( root, fork_database_exception, "root not yet set" );
|
||||
|
||||
auto new_root = get_block_impl( id );
|
||||
auto new_root = get_block( id );
|
||||
EOS_ASSERT( new_root, fork_database_exception,
|
||||
"cannot advance root to a block that does not exist in the fork database" );
|
||||
EOS_ASSERT( new_root->is_valid(), fork_database_exception,
|
||||
@@ -308,53 +262,48 @@ namespace eosio { namespace chain {
|
||||
deque<block_id_type> blocks_to_remove;
|
||||
for( auto b = new_root; b; ) {
|
||||
blocks_to_remove.emplace_back( b->header.previous );
|
||||
b = get_block_impl( blocks_to_remove.back() );
|
||||
EOS_ASSERT( b || blocks_to_remove.back() == root->id, fork_database_exception, "invariant violation: orphaned branch was present in forked database" );
|
||||
b = get_block( blocks_to_remove.back() );
|
||||
EOS_ASSERT( b || blocks_to_remove.back() == my->root->id, fork_database_exception, "invariant violation: orphaned branch was present in forked database" );
|
||||
}
|
||||
|
||||
// The new root block should be erased from the fork database index individually rather than with the remove method,
|
||||
// because we do not want the blocks branching off of it to be removed from the fork database.
|
||||
index.erase( index.find( id ) );
|
||||
my->index.erase( my->index.find( id ) );
|
||||
|
||||
// The other blocks to be removed are removed using the remove method so that orphaned branches do not remain in the fork database.
|
||||
for( const auto& block_id : blocks_to_remove ) {
|
||||
remove_impl( block_id );
|
||||
remove( block_id );
|
||||
}
|
||||
|
||||
// Even though fork database no longer needs block or trxs when a block state becomes a root of the tree,
|
||||
// avoid mutating the block state at all, for example clearing the block shared pointer, because other
|
||||
// parts of the code which run asynchronously may later expect it remain unmodified.
|
||||
|
||||
root = new_root;
|
||||
my->root = new_root;
|
||||
}
|
||||
|
||||
block_header_state_ptr fork_database::get_block_header( const block_id_type& id )const {
|
||||
std::shared_lock g( my->mtx );
|
||||
return my->get_block_header_impl( id );
|
||||
}
|
||||
|
||||
block_header_state_ptr fork_database_impl::get_block_header_impl( const block_id_type& id )const {
|
||||
if( root->id == id ) {
|
||||
return root;
|
||||
if( my->root->id == id ) {
|
||||
return my->root;
|
||||
}
|
||||
|
||||
auto itr = index.find( id );
|
||||
if( itr != index.end() )
|
||||
auto itr = my->index.find( id );
|
||||
if( itr != my->index.end() )
|
||||
return *itr;
|
||||
|
||||
return block_header_state_ptr();
|
||||
}
|
||||
|
||||
void fork_database_impl::add_impl( const block_state_ptr& n,
|
||||
bool ignore_duplicate, bool validate,
|
||||
const std::function<void( block_timestamp_type,
|
||||
const flat_set<digest_type>&,
|
||||
const vector<digest_type>& )>& validator )
|
||||
void fork_database_impl::add( const block_state_ptr& n,
|
||||
bool ignore_duplicate, bool validate,
|
||||
const std::function<void( block_timestamp_type,
|
||||
const flat_set<digest_type>&,
|
||||
const vector<digest_type>& )>& validator )
|
||||
{
|
||||
EOS_ASSERT( root, fork_database_exception, "root not yet set" );
|
||||
EOS_ASSERT( n, fork_database_exception, "attempt to add null block state" );
|
||||
|
||||
auto prev_bh = get_block_header_impl( n->header.previous );
|
||||
auto prev_bh = self.get_block_header( n->header.previous );
|
||||
|
||||
EOS_ASSERT( prev_bh, unlinkable_block_exception,
|
||||
"unlinkable block", ("id", n->id)("previous", n->header.previous) );
|
||||
@@ -383,27 +332,19 @@ namespace eosio { namespace chain {
|
||||
}
|
||||
|
||||
void fork_database::add( const block_state_ptr& n, bool ignore_duplicate ) {
|
||||
std::lock_guard g( my->mtx );
|
||||
my->add_impl( n, ignore_duplicate, false,
|
||||
[]( block_timestamp_type timestamp,
|
||||
const flat_set<digest_type>& cur_features,
|
||||
const vector<digest_type>& new_features )
|
||||
{}
|
||||
my->add( n, ignore_duplicate, false,
|
||||
[]( block_timestamp_type timestamp,
|
||||
const flat_set<digest_type>& cur_features,
|
||||
const vector<digest_type>& new_features )
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
block_state_ptr fork_database::root()const {
|
||||
std::shared_lock g( my->mtx );
|
||||
return my->root;
|
||||
}
|
||||
const block_state_ptr& fork_database::root()const { return my->root; }
|
||||
|
||||
block_state_ptr fork_database::head()const {
|
||||
std::shared_lock g( my->mtx );
|
||||
return my->head;
|
||||
}
|
||||
const block_state_ptr& fork_database::head()const { return my->head; }
|
||||
|
||||
block_state_ptr fork_database::pending_head()const {
|
||||
std::shared_lock g( my->mtx );
|
||||
const auto& indx = my->index.get<by_lib_block_num>();
|
||||
|
||||
auto itr = indx.lower_bound( false );
|
||||
@@ -416,13 +357,8 @@ namespace eosio { namespace chain {
|
||||
}
|
||||
|
||||
branch_type fork_database::fetch_branch( const block_id_type& h, uint32_t trim_after_block_num )const {
|
||||
std::shared_lock g( my->mtx );
|
||||
return my->fetch_branch_impl( h, trim_after_block_num );
|
||||
}
|
||||
|
||||
branch_type fork_database_impl::fetch_branch_impl( const block_id_type& h, uint32_t trim_after_block_num )const {
|
||||
branch_type result;
|
||||
for( auto s = get_block_impl(h); s; s = get_block_impl( s->header.previous ) ) {
|
||||
for( auto s = get_block(h); s; s = get_block( s->header.previous ) ) {
|
||||
if( s->block_num <= trim_after_block_num )
|
||||
result.push_back( s );
|
||||
}
|
||||
@@ -431,12 +367,7 @@ namespace eosio { namespace chain {
|
||||
}
|
||||
|
||||
block_state_ptr fork_database::search_on_branch( const block_id_type& h, uint32_t block_num )const {
|
||||
std::shared_lock g( my->mtx );
|
||||
return my->search_on_branch_impl( h, block_num );
|
||||
}
|
||||
|
||||
block_state_ptr fork_database_impl::search_on_branch_impl( const block_id_type& h, uint32_t block_num )const {
|
||||
for( auto s = get_block_impl(h); s; s = get_block_impl( s->header.previous ) ) {
|
||||
for( auto s = get_block(h); s; s = get_block( s->header.previous ) ) {
|
||||
if( s->block_num == block_num )
|
||||
return s;
|
||||
}
|
||||
@@ -450,15 +381,9 @@ namespace eosio { namespace chain {
|
||||
*/
|
||||
pair< branch_type, branch_type > fork_database::fetch_branch_from( const block_id_type& first,
|
||||
const block_id_type& second )const {
|
||||
std::shared_lock g( my->mtx );
|
||||
return my->fetch_branch_from_impl( first, second );
|
||||
}
|
||||
|
||||
pair< branch_type, branch_type > fork_database_impl::fetch_branch_from_impl( const block_id_type& first,
|
||||
const block_id_type& second )const {
|
||||
pair<branch_type,branch_type> result;
|
||||
auto first_branch = (first == root->id) ? root : get_block_impl(first);
|
||||
auto second_branch = (second == root->id) ? root : get_block_impl(second);
|
||||
auto first_branch = (first == my->root->id) ? my->root : get_block(first);
|
||||
auto second_branch = (second == my->root->id) ? my->root : get_block(second);
|
||||
|
||||
EOS_ASSERT(first_branch, fork_db_block_not_found, "block ${id} does not exist", ("id", first));
|
||||
EOS_ASSERT(second_branch, fork_db_block_not_found, "block ${id} does not exist", ("id", second));
|
||||
@@ -467,7 +392,7 @@ namespace eosio { namespace chain {
|
||||
{
|
||||
result.first.push_back(first_branch);
|
||||
const auto& prev = first_branch->header.previous;
|
||||
first_branch = (prev == root->id) ? root : get_block_impl( prev );
|
||||
first_branch = (prev == my->root->id) ? my->root : get_block( prev );
|
||||
EOS_ASSERT( first_branch, fork_db_block_not_found,
|
||||
"block ${id} does not exist",
|
||||
("id", prev)
|
||||
@@ -478,7 +403,7 @@ namespace eosio { namespace chain {
|
||||
{
|
||||
result.second.push_back( second_branch );
|
||||
const auto& prev = second_branch->header.previous;
|
||||
second_branch = (prev == root->id) ? root : get_block_impl( prev );
|
||||
second_branch = (prev == my->root->id) ? my->root : get_block( prev );
|
||||
EOS_ASSERT( second_branch, fork_db_block_not_found,
|
||||
"block ${id} does not exist",
|
||||
("id", prev)
|
||||
@@ -492,9 +417,9 @@ namespace eosio { namespace chain {
|
||||
result.first.push_back(first_branch);
|
||||
result.second.push_back(second_branch);
|
||||
const auto &first_prev = first_branch->header.previous;
|
||||
first_branch = get_block_impl( first_prev );
|
||||
first_branch = get_block( first_prev );
|
||||
const auto &second_prev = second_branch->header.previous;
|
||||
second_branch = get_block_impl( second_prev );
|
||||
second_branch = get_block( second_prev );
|
||||
EOS_ASSERT( first_branch, fork_db_block_not_found,
|
||||
"block ${id} does not exist",
|
||||
("id", first_prev)
|
||||
@@ -511,18 +436,13 @@ namespace eosio { namespace chain {
|
||||
result.second.push_back(second_branch);
|
||||
}
|
||||
return result;
|
||||
} /// fetch_branch_from_impl
|
||||
} /// fetch_branch_from
|
||||
|
||||
/// remove all of the invalid forks built off of this id including this id
|
||||
void fork_database::remove( const block_id_type& id ) {
|
||||
std::lock_guard g( my->mtx );
|
||||
return my->remove_impl( id );
|
||||
}
|
||||
|
||||
void fork_database_impl::remove_impl( const block_id_type& id ) {
|
||||
deque<block_id_type> remove_queue{id};
|
||||
const auto& previdx = index.get<by_prev>();
|
||||
const auto& head_id = head->id;
|
||||
const auto& previdx = my->index.get<by_prev>();
|
||||
const auto& head_id = my->head->id;
|
||||
|
||||
for( uint32_t i = 0; i < remove_queue.size(); ++i ) {
|
||||
EOS_ASSERT( remove_queue[i] != head_id, fork_database_exception,
|
||||
@@ -536,19 +456,16 @@ namespace eosio { namespace chain {
|
||||
}
|
||||
|
||||
for( const auto& block_id : remove_queue ) {
|
||||
index.erase( block_id );
|
||||
auto itr = my->index.find( block_id );
|
||||
if( itr != my->index.end() )
|
||||
my->index.erase(itr);
|
||||
}
|
||||
}
|
||||
|
||||
void fork_database::mark_valid( const block_state_ptr& h ) {
|
||||
std::lock_guard g( my->mtx );
|
||||
my->mark_valid_impl( h );
|
||||
}
|
||||
|
||||
void fork_database_impl::mark_valid_impl( const block_state_ptr& h ) {
|
||||
if( h->validated ) return;
|
||||
|
||||
auto& by_id_idx = index.get<by_block_id>();
|
||||
auto& by_id_idx = my->index.get<by_block_id>();
|
||||
|
||||
auto itr = by_id_idx.find( h->id );
|
||||
EOS_ASSERT( itr != by_id_idx.end(), fork_database_exception,
|
||||
@@ -559,20 +476,15 @@ namespace eosio { namespace chain {
|
||||
bsp->validated = true;
|
||||
} );
|
||||
|
||||
auto candidate = index.get<by_lib_block_num>().begin();
|
||||
if( first_preferred( **candidate, *head ) ) {
|
||||
head = *candidate;
|
||||
auto candidate = my->index.get<by_lib_block_num>().begin();
|
||||
if( first_preferred( **candidate, *my->head ) ) {
|
||||
my->head = *candidate;
|
||||
}
|
||||
}
|
||||
|
||||
block_state_ptr fork_database::get_block(const block_id_type& id)const {
|
||||
std::shared_lock g( my->mtx );
|
||||
return my->get_block_impl(id);
|
||||
}
|
||||
|
||||
block_state_ptr fork_database_impl::get_block_impl(const block_id_type& id)const {
|
||||
auto itr = index.find( id );
|
||||
if( itr != index.end() )
|
||||
block_state_ptr fork_database::get_block(const block_id_type& id)const {
|
||||
auto itr = my->index.find( id );
|
||||
if( itr != my->index.end() )
|
||||
return *itr;
|
||||
return block_state_ptr();
|
||||
}
|
||||
|
||||
@@ -48,7 +48,9 @@ namespace eosio { namespace chain {
|
||||
class fork_database;
|
||||
|
||||
enum class db_read_mode {
|
||||
SPECULATIVE,
|
||||
HEAD,
|
||||
READ_ONLY,
|
||||
IRREVERSIBLE
|
||||
};
|
||||
|
||||
@@ -90,7 +92,7 @@ namespace eosio { namespace chain {
|
||||
eosvmoc::config eosvmoc_config;
|
||||
bool eosvmoc_tierup = false;
|
||||
|
||||
db_read_mode read_mode = db_read_mode::HEAD;
|
||||
db_read_mode read_mode = db_read_mode::SPECULATIVE;
|
||||
validation_mode block_validation_mode = validation_mode::FULL;
|
||||
|
||||
pinnable_mapped_file::map_mode db_map_mode = pinnable_mapped_file::map_mode::mapped;
|
||||
@@ -106,8 +108,7 @@ namespace eosio { namespace chain {
|
||||
irreversible = 0, ///< this block has already been applied before by this node and is considered irreversible
|
||||
validated = 1, ///< this is a complete block signed by a valid producer and has been previously applied by this node and therefore validated but it is not yet irreversible
|
||||
complete = 2, ///< this is a complete block signed by a valid producer but is not yet irreversible nor has it yet been applied by this node
|
||||
incomplete = 3, ///< this is an incomplete block being produced by a producer
|
||||
ephemeral = 4 ///< this is an incomplete block created for speculative execution of trxs, will always be aborted
|
||||
incomplete = 3, ///< this is an incomplete block (either being produced by a producer or speculatively produced by a node)
|
||||
};
|
||||
|
||||
controller( const config& cfg, const chain_id_type& chain_id );
|
||||
@@ -126,12 +127,20 @@ namespace eosio { namespace chain {
|
||||
void validate_protocol_features( const vector<digest_type>& features_to_activate )const;
|
||||
|
||||
/**
|
||||
* Starts a new pending block session upon which new transactions can be pushed.
|
||||
* Starts a new pending block session upon which new transactions can
|
||||
* be pushed.
|
||||
*
|
||||
* Will only activate protocol features that have been pre-activated.
|
||||
*/
|
||||
void start_block( block_timestamp_type time = block_timestamp_type(), uint16_t confirm_block_count = 0 );
|
||||
|
||||
/**
|
||||
* Starts a new pending block session upon which new transactions can
|
||||
* be pushed.
|
||||
*/
|
||||
void start_block( block_timestamp_type time,
|
||||
uint16_t confirm_block_count,
|
||||
const vector<digest_type>& new_protocol_feature_activations,
|
||||
block_status bs,
|
||||
const fc::time_point& deadline = fc::time_point::maximum() );
|
||||
|
||||
/**
|
||||
@@ -155,30 +164,26 @@ namespace eosio { namespace chain {
|
||||
fc::time_point block_deadline, fc::microseconds max_transaction_time,
|
||||
uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time );
|
||||
|
||||
block_state_ptr finalize_block( const signer_callback_type& signer_callback );
|
||||
void sign_block( const signer_callback_type& signer_callback );
|
||||
void commit_block();
|
||||
|
||||
std::future<block_state_ptr> create_block_state_future( const block_id_type& id, const signed_block_ptr& b );
|
||||
|
||||
struct block_report {
|
||||
size_t total_net_usage = 0;
|
||||
size_t total_cpu_usage_us = 0;
|
||||
fc::microseconds total_elapsed_time{};
|
||||
fc::microseconds total_time{};
|
||||
};
|
||||
|
||||
block_state_ptr finalize_block( block_report& br, const signer_callback_type& signer_callback );
|
||||
void sign_block( const signer_callback_type& signer_callback );
|
||||
void commit_block();
|
||||
|
||||
// thread-safe
|
||||
std::future<block_state_ptr> create_block_state_future( const block_id_type& id, const signed_block_ptr& b );
|
||||
// thread-safe
|
||||
block_state_ptr create_block_state( const block_id_type& id, const signed_block_ptr& b ) const;
|
||||
|
||||
/**
|
||||
* @param br returns statistics for block
|
||||
* @param bsp block to push
|
||||
* @param block_state_future provide from call to create_block_state_future
|
||||
* @param cb calls cb with forked applied transactions for each forked block
|
||||
* @param trx_lookup user provided lookup function for externally cached transaction_metadata
|
||||
*/
|
||||
void push_block( block_report& br,
|
||||
const block_state_ptr& bsp,
|
||||
std::future<block_state_ptr>& block_state_future,
|
||||
const forked_branch_callback& cb,
|
||||
const trx_meta_cache_lookup& trx_lookup );
|
||||
|
||||
@@ -247,13 +252,12 @@ namespace eosio { namespace chain {
|
||||
signed_block_ptr fetch_block_by_id( block_id_type id )const;
|
||||
|
||||
block_state_ptr fetch_block_state_by_number( uint32_t block_num )const;
|
||||
// return block_state from forkdb, thread-safe
|
||||
block_state_ptr fetch_block_state_by_id( block_id_type id )const;
|
||||
|
||||
block_id_type get_block_id_for_num( uint32_t block_num )const;
|
||||
|
||||
sha256 calculate_integrity_hash();
|
||||
void write_snapshot( const snapshot_writer_ptr& snapshot );
|
||||
sha256 calculate_integrity_hash()const;
|
||||
void write_snapshot( const snapshot_writer_ptr& snapshot )const;
|
||||
|
||||
bool sender_avoids_whitelist_blacklist_enforcement( account_name sender )const;
|
||||
void check_actor_list( const flat_set<account_name>& actors )const;
|
||||
@@ -261,7 +265,7 @@ namespace eosio { namespace chain {
|
||||
void check_action_list( account_name code, action_name action )const;
|
||||
void check_key_list( const public_key_type& key )const;
|
||||
bool is_building_block()const;
|
||||
bool is_speculative_block()const;
|
||||
bool is_producing_block()const;
|
||||
|
||||
bool is_ram_billing_in_notify_allowed()const;
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@ namespace eosio { namespace chain {
|
||||
* database tracks the longest chain and the last irreversible block number. All
|
||||
* blocks older than the last irreversible block are freed after emitting the
|
||||
* irreversible signal.
|
||||
*
|
||||
* An internal mutex is used to provide thread-safety.
|
||||
*/
|
||||
class fork_database {
|
||||
public:
|
||||
@@ -57,9 +55,9 @@ namespace eosio { namespace chain {
|
||||
|
||||
void remove( const block_id_type& id );
|
||||
|
||||
block_state_ptr root()const;
|
||||
block_state_ptr head()const;
|
||||
block_state_ptr pending_head()const;
|
||||
const block_state_ptr& root()const;
|
||||
const block_state_ptr& head()const;
|
||||
block_state_ptr pending_head()const;
|
||||
|
||||
/**
|
||||
* Returns the sequence of block states resulting from trimming the branch from the
|
||||
|
||||
@@ -396,9 +396,6 @@ private:
|
||||
std::function<deep_mind_handler*()> _get_deep_mind_logger;
|
||||
};
|
||||
|
||||
std::optional<builtin_protocol_feature> read_builtin_protocol_feature( const fc::path& p );
|
||||
protocol_feature_set initialize_protocol_features( const fc::path& p, bool populate_missing_builtins = true );
|
||||
|
||||
} } // namespace eosio::chain
|
||||
|
||||
FC_REFLECT(eosio::chain::protocol_feature_subjective_restrictions,
|
||||
|
||||
@@ -1,148 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <eosio/chain/name.hpp>
|
||||
#include <fc/exception/exception.hpp>
|
||||
#include <fc/log/logger_config.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/thread_pool.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
namespace eosio { namespace chain {
|
||||
|
||||
inline constexpr uint64_t make_name_v( std::string_view str ) {
|
||||
return string_to_uint64_t(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for thread pool of boost asio io_context run.
|
||||
* Wrapper class for boost asio thread pool and io_context run.
|
||||
* Also names threads so that tools like htop can see thread name.
|
||||
* Example: named_thread_pool<eosio::chain::make_name_v("chain")> thread_pool;
|
||||
* @param NamePrefix is eosio::name appended with -## of thread.
|
||||
* A short name_prefix (6 chars or under) is recommended as console_appender uses 9 chars
|
||||
* for the thread name.
|
||||
*/
|
||||
template<uint64_t NamePrefix>
|
||||
class named_thread_pool {
|
||||
public:
|
||||
using on_except_t = std::function<void(const fc::exception& e)>;
|
||||
// name_prefix is name appended with -## of thread.
|
||||
// short name_prefix (6 chars or under) is recommended as console_appender uses 9 chars for thread name
|
||||
named_thread_pool( std::string name_prefix, size_t num_threads );
|
||||
|
||||
named_thread_pool()
|
||||
: _name_prefix(NamePrefix)
|
||||
, _ioc()
|
||||
{}
|
||||
// calls stop()
|
||||
~named_thread_pool();
|
||||
|
||||
~named_thread_pool(){
|
||||
stop();
|
||||
}
|
||||
boost::asio::io_context& get_executor() { return _ioc; }
|
||||
|
||||
auto& get_executor() { return _ioc; }
|
||||
|
||||
/// Spawn threads, can be re-started after stop().
|
||||
/// Assumes start()/stop() called from the same thread or externally protected.
|
||||
/// @param num_threads is number of threads spawned
|
||||
/// @param on_except is the function to call if io_context throws an exception, is called from thread pool thread.
|
||||
/// if an empty function then logs and rethrows exception on thread which will terminate.
|
||||
/// @throw assert_exception if already started and not stopped.
|
||||
void start( size_t num_threads, on_except_t on_except ) {
|
||||
FC_ASSERT( !_ioc_work, "Thread pool already started" );
|
||||
_ioc_work.emplace( boost::asio::make_work_guard( _ioc ) );
|
||||
_ioc.restart();
|
||||
_thread_pool.reserve( num_threads );
|
||||
for( size_t i = 0; i < num_threads; ++i ) {
|
||||
_thread_pool.emplace_back( std::thread( &named_thread_pool::run_thread, this, i, on_except ) );
|
||||
}
|
||||
}
|
||||
|
||||
/// destroy work guard, stop io_context, join thread_pool
|
||||
void stop() {
|
||||
_ioc_work.reset();
|
||||
_ioc.stop();
|
||||
for( auto& t : _thread_pool ) {
|
||||
t.join();
|
||||
}
|
||||
_thread_pool.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
void run_thread( size_t i, const on_except_t& on_except ) {
|
||||
// copying the name_prefix and i into lambda makes it available to be seen on the stack in the debugger
|
||||
std::string tn = _name_prefix.to_string() + "-" + std::to_string( i );
|
||||
try {
|
||||
fc::set_os_thread_name( tn );
|
||||
_ioc.run();
|
||||
} catch( const fc::exception& e ) {
|
||||
if( on_except ) {
|
||||
on_except( e );
|
||||
} else {
|
||||
elog( "Exiting thread ${t} on exception: ${e}", ("t", tn)("e", e.to_detail_string()) );
|
||||
throw;
|
||||
}
|
||||
} catch( const std::exception& e ) {
|
||||
fc::std_exception_wrapper se( FC_LOG_MESSAGE( warn, "${what}: ", ("what", e.what()) ),
|
||||
std::current_exception(), BOOST_CORE_TYPEID( e ).name(), e.what() );
|
||||
if( on_except ) {
|
||||
on_except( se );
|
||||
} else {
|
||||
elog( "Exiting thread ${t} on exception: ${e}", ("t", tn)("e", se.to_detail_string()) );
|
||||
throw;
|
||||
}
|
||||
} catch( ... ) {
|
||||
if( on_except ) {
|
||||
fc::unhandled_exception ue( FC_LOG_MESSAGE( warn, "unknown exception" ), std::current_exception() );
|
||||
on_except( ue );
|
||||
} else {
|
||||
elog( "Exiting thread ${t} on unknown exception", ("t", tn) );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
// destroy work guard, stop io_context, join thread_pool, and stop thread_pool
|
||||
void stop();
|
||||
|
||||
private:
|
||||
using ioc_work_t = boost::asio::executor_work_guard<boost::asio::io_context::executor_type>;
|
||||
|
||||
eosio::chain::name _name_prefix;
|
||||
boost::asio::thread_pool _thread_pool;
|
||||
boost::asio::io_context _ioc;
|
||||
std::vector<std::thread> _thread_pool;
|
||||
std::optional<ioc_work_t> _ioc_work;
|
||||
};
|
||||
|
||||
template<uint64_t NamePrefix>
|
||||
class named_thread {
|
||||
public:
|
||||
/// @param name_prefix is name appended with -1 for thread name.
|
||||
/// A short name_prefix (6 chars or under) is recommended as console_appender uses 9 chars
|
||||
/// for the thread name.
|
||||
named_thread() = default;
|
||||
|
||||
/// stop before destroying strand
|
||||
~named_thread() { stop(); }
|
||||
|
||||
auto& get_executor() { return _strand; }
|
||||
|
||||
/// Spawn thread, can be re-started after stop().
|
||||
/// Assumes start()/stop() called from the same thread or externally protected.
|
||||
/// @param on_except is the function to call if io_context throws an exception, is called from thread pool thread.
|
||||
/// if an empty function then logs and rethrows exception on thread which will terminate.
|
||||
/// @throw assert_exception if already started and not stopped.
|
||||
void start( typename named_thread_pool<NamePrefix>::on_except_t on_except ) { _pool.start( 1, std::move( on_except ) ); }
|
||||
|
||||
/// destroy work guard, stop io_context, join thread_pool
|
||||
void stop() { _pool.stop(); }
|
||||
|
||||
private:
|
||||
named_thread_pool<NamePrefix> _pool;
|
||||
decltype(boost::asio::make_strand(_pool.get_executor())) _strand{ boost::asio::make_strand( _pool.get_executor() ) };
|
||||
};
|
||||
|
||||
// async on io_context and return future
|
||||
// async on thread_pool and return future
|
||||
template<typename F>
|
||||
auto post_async_task( boost::asio::io_context& ioc, F&& f ) {
|
||||
auto async_thread_pool( boost::asio::io_context& thread_pool, F&& f ) {
|
||||
auto task = std::make_shared<std::packaged_task<decltype( f() )()>>( std::forward<F>( f ) );
|
||||
boost::asio::post( ioc, [task]() { (*task)(); } );
|
||||
boost::asio::post( thread_pool, [task]() { (*task)(); } );
|
||||
return task->get_future();
|
||||
}
|
||||
|
||||
|
||||
@@ -39,13 +39,14 @@ namespace eosio { namespace chain {
|
||||
const packed_transaction& t,
|
||||
transaction_checktime_timer&& timer,
|
||||
fc::time_point start = fc::time_point::now(),
|
||||
transaction_metadata::trx_type type = transaction_metadata::trx_type::input);
|
||||
bool read_only=false);
|
||||
~transaction_context();
|
||||
|
||||
void init_for_implicit_trx( uint64_t initial_net_usage = 0 );
|
||||
|
||||
void init_for_input_trx( uint64_t packed_trx_unprunable_size,
|
||||
uint64_t packed_trx_prunable_size );
|
||||
uint64_t packed_trx_prunable_size,
|
||||
bool skip_recording);
|
||||
|
||||
void init_for_deferred_trx( fc::time_point published );
|
||||
|
||||
@@ -83,8 +84,6 @@ namespace eosio { namespace chain {
|
||||
|
||||
void validate_referenced_accounts( const transaction& trx, bool enforce_actor_whitelist_blacklist )const;
|
||||
|
||||
bool is_dry_run()const { return trx_type == transaction_metadata::trx_type::dry_run; };
|
||||
|
||||
private:
|
||||
|
||||
friend struct controller_impl;
|
||||
@@ -118,8 +117,6 @@ namespace eosio { namespace chain {
|
||||
|
||||
void disallow_transaction_extensions( const char* error_msg )const;
|
||||
|
||||
std::string get_tx_cpu_usage_exceeded_reason_msg(fc::microseconds& limit) const;
|
||||
|
||||
/// Fields:
|
||||
public:
|
||||
|
||||
@@ -152,9 +149,9 @@ namespace eosio { namespace chain {
|
||||
|
||||
transaction_checktime_timer transaction_timer;
|
||||
|
||||
const bool is_read_only;
|
||||
private:
|
||||
bool is_initialized = false;
|
||||
transaction_metadata::trx_type trx_type;
|
||||
|
||||
uint64_t net_limit = 0;
|
||||
bool net_limit_due_to_block = true;
|
||||
@@ -173,16 +170,6 @@ namespace eosio { namespace chain {
|
||||
int64_t billing_timer_exception_code = block_cpu_usage_exceeded::code_value;
|
||||
fc::time_point pseudo_start;
|
||||
fc::microseconds billed_time;
|
||||
|
||||
enum class tx_cpu_usage_exceeded_reason {
|
||||
account_cpu_limit, // includes subjective billing
|
||||
on_chain_consensus_max_transaction_cpu_usage,
|
||||
user_specified_trx_max_cpu_usage_ms,
|
||||
node_configured_max_transaction_time,
|
||||
speculative_executed_adjusted_max_transaction_time // prev_billed_cpu_time_us > 0
|
||||
};
|
||||
tx_cpu_usage_exceeded_reason tx_cpu_usage_reason = tx_cpu_usage_exceeded_reason::account_cpu_limit;
|
||||
fc::microseconds tx_cpu_usage_amount;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
@@ -24,16 +24,18 @@ class transaction_metadata {
|
||||
input,
|
||||
implicit,
|
||||
scheduled,
|
||||
dry_run
|
||||
read_only
|
||||
};
|
||||
|
||||
private:
|
||||
const packed_transaction_ptr _packed_trx;
|
||||
const fc::microseconds _sig_cpu_usage;
|
||||
const flat_set<public_key_type> _recovered_pub_keys;
|
||||
const trx_type _trx_type;
|
||||
|
||||
public:
|
||||
const bool implicit;
|
||||
const bool scheduled;
|
||||
const bool read_only;
|
||||
bool accepted = false; // not thread safe
|
||||
uint32_t billed_cpu_time_us = 0; // not thread safe
|
||||
|
||||
@@ -50,11 +52,13 @@ class transaction_metadata {
|
||||
// creation of tranaction_metadata restricted to start_recover_keys and create_no_recover_keys below, public for make_shared
|
||||
explicit transaction_metadata( const private_type& pt, packed_transaction_ptr ptrx,
|
||||
fc::microseconds sig_cpu_usage, flat_set<public_key_type> recovered_pub_keys,
|
||||
trx_type t = trx_type::input )
|
||||
bool _implicit = false, bool _scheduled = false, bool _read_only = false)
|
||||
: _packed_trx( std::move( ptrx ) )
|
||||
, _sig_cpu_usage( sig_cpu_usage )
|
||||
, _recovered_pub_keys( std::move( recovered_pub_keys ) )
|
||||
, _trx_type( t ) {
|
||||
, implicit( _implicit )
|
||||
, scheduled( _scheduled )
|
||||
, read_only( _read_only) {
|
||||
}
|
||||
|
||||
transaction_metadata() = delete;
|
||||
@@ -69,10 +73,6 @@ class transaction_metadata {
|
||||
fc::microseconds signature_cpu_usage()const { return _sig_cpu_usage; }
|
||||
const flat_set<public_key_type>& recovered_keys()const { return _recovered_pub_keys; }
|
||||
size_t get_estimated_size() const;
|
||||
trx_type get_trx_type() const { return _trx_type; };
|
||||
bool implicit() const { return _trx_type == trx_type::implicit; };
|
||||
bool scheduled() const { return _trx_type == trx_type::scheduled; };
|
||||
bool is_dry_run() const { return _trx_type == trx_type::dry_run; };
|
||||
|
||||
/// Thread safe.
|
||||
/// @returns transaction_metadata_ptr or exception via future
|
||||
@@ -85,7 +85,7 @@ class transaction_metadata {
|
||||
static transaction_metadata_ptr
|
||||
create_no_recover_keys( packed_transaction_ptr trx, trx_type t ) {
|
||||
return std::make_shared<transaction_metadata>( private_type(), std::move(trx),
|
||||
fc::microseconds(), flat_set<public_key_type>(), t );
|
||||
fc::microseconds(), flat_set<public_key_type>(), t == trx_type::implicit, t == trx_type::scheduled, t==trx_type::read_only );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -21,10 +21,11 @@ using namespace boost::multi_index;
|
||||
|
||||
enum class trx_enum_type {
|
||||
unknown = 0,
|
||||
forked = 1,
|
||||
aborted = 2,
|
||||
incoming_api = 3,
|
||||
incoming_p2p = 4 // incoming_end() needs to be updated if this changes
|
||||
persisted = 1,
|
||||
forked = 2,
|
||||
aborted = 3,
|
||||
incoming_persisted = 4,
|
||||
incoming = 5 // incoming_end() needs to be updated if this changes
|
||||
};
|
||||
|
||||
using next_func_t = std::function<void(const std::variant<fc::exception_ptr, transaction_trace_ptr>&)>;
|
||||
@@ -45,7 +46,8 @@ struct unapplied_transaction {
|
||||
};
|
||||
|
||||
/**
|
||||
* Track unapplied transactions for incoming, forked blocks, and aborted blocks.
|
||||
* Track unapplied transactions for persisted, forked blocks, and aborted blocks.
|
||||
* Persisted are first so that they can be applied in each block until expired.
|
||||
*/
|
||||
class unapplied_transaction_queue {
|
||||
private:
|
||||
@@ -94,15 +96,15 @@ public:
|
||||
return itr->trx_meta;
|
||||
}
|
||||
|
||||
template <typename Yield, typename Callback>
|
||||
bool clear_expired( const time_point& pending_block_time, Yield&& yield, Callback&& callback ) {
|
||||
template <typename Func>
|
||||
bool clear_expired( const time_point& pending_block_time, const time_point& deadline, Func&& callback ) {
|
||||
auto& persisted_by_expiry = queue.get<by_expiry>();
|
||||
while( !persisted_by_expiry.empty() ) {
|
||||
const auto& itr = persisted_by_expiry.begin();
|
||||
if( itr->expiration() > pending_block_time ) {
|
||||
break;
|
||||
}
|
||||
if( yield() ) {
|
||||
if( deadline <= fc::time_point::now() ) {
|
||||
return false;
|
||||
}
|
||||
callback( itr->trx_meta->packed_trx(), itr->trx_type );
|
||||
@@ -157,11 +159,26 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void add_incoming( const transaction_metadata_ptr& trx, bool api_trx, bool return_failure_trace, next_func_t next ) {
|
||||
void add_persisted( const transaction_metadata_ptr& trx ) {
|
||||
auto itr = queue.get<by_trx_id>().find( trx->id() );
|
||||
if( itr == queue.get<by_trx_id>().end() ) {
|
||||
auto insert_itr = queue.insert( { trx, trx_enum_type::persisted } );
|
||||
if( insert_itr.second ) added( insert_itr.first );
|
||||
} else if( itr->trx_type != trx_enum_type::persisted ) {
|
||||
if (itr->trx_type == trx_enum_type::incoming || itr->trx_type == trx_enum_type::incoming_persisted)
|
||||
--incoming_count;
|
||||
queue.get<by_trx_id>().modify( itr, [](auto& un){
|
||||
un.trx_type = trx_enum_type::persisted;
|
||||
un.next = nullptr; // persisted already have ack'ed initial trace
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
void add_incoming( const transaction_metadata_ptr& trx, bool persist_until_expired, bool return_failure_trace, next_func_t next ) {
|
||||
auto itr = queue.get<by_trx_id>().find( trx->id() );
|
||||
if( itr == queue.get<by_trx_id>().end() ) {
|
||||
auto insert_itr = queue.insert(
|
||||
{ trx, api_trx ? trx_enum_type::incoming_api : trx_enum_type::incoming_p2p, return_failure_trace, std::move( next ) } );
|
||||
{ trx, persist_until_expired ? trx_enum_type::incoming_persisted : trx_enum_type::incoming, return_failure_trace, std::move( next ) } );
|
||||
if( insert_itr.second ) added( insert_itr.first );
|
||||
} else {
|
||||
if( itr->trx_meta == trx ) return; // same trx meta pointer
|
||||
@@ -177,11 +194,14 @@ public:
|
||||
iterator begin() { return queue.get<by_type>().begin(); }
|
||||
iterator end() { return queue.get<by_type>().end(); }
|
||||
|
||||
// forked, aborted
|
||||
// persisted, forked, aborted
|
||||
iterator unapplied_begin() { return queue.get<by_type>().begin(); }
|
||||
iterator unapplied_end() { return queue.get<by_type>().upper_bound( trx_enum_type::aborted ); }
|
||||
|
||||
iterator incoming_begin() { return queue.get<by_type>().lower_bound( trx_enum_type::incoming_api ); }
|
||||
iterator persisted_begin() { return queue.get<by_type>().lower_bound( trx_enum_type::persisted ); }
|
||||
iterator persisted_end() { return queue.get<by_type>().upper_bound( trx_enum_type::persisted ); }
|
||||
|
||||
iterator incoming_begin() { return queue.get<by_type>().lower_bound( trx_enum_type::incoming_persisted ); }
|
||||
iterator incoming_end() { return queue.get<by_type>().end(); } // if changed to upper_bound, verify usage performance
|
||||
|
||||
iterator lower_bound( const transaction_id_type& id ) {
|
||||
@@ -200,7 +220,7 @@ private:
|
||||
template<typename Itr>
|
||||
void added( Itr itr ) {
|
||||
auto size = calc_size( itr->trx_meta );
|
||||
if( itr->trx_type == trx_enum_type::incoming_p2p || itr->trx_type == trx_enum_type::incoming_api ) {
|
||||
if( itr->trx_type == trx_enum_type::incoming || itr->trx_type == trx_enum_type::incoming_persisted ) {
|
||||
++incoming_count;
|
||||
EOS_ASSERT( size_in_bytes + size < max_transaction_queue_size, tx_resource_exhaustion,
|
||||
"Transaction ${id}, size ${s} bytes would exceed configured "
|
||||
@@ -213,7 +233,7 @@ private:
|
||||
|
||||
template<typename Itr>
|
||||
void removed( Itr itr ) {
|
||||
if( itr->trx_type == trx_enum_type::incoming_p2p || itr->trx_type == trx_enum_type::incoming_api ) {
|
||||
if( itr->trx_type == trx_enum_type::incoming || itr->trx_type == trx_enum_type::incoming_persisted ) {
|
||||
--incoming_count;
|
||||
}
|
||||
size_in_bytes -= calc_size( itr->trx_meta );
|
||||
|
||||
@@ -332,7 +332,7 @@ namespace eosio { namespace chain { namespace wasm_validations {
|
||||
public:
|
||||
wasm_binary_validation( const eosio::chain::controller& control, IR::Module& mod ) : _module( &mod ) {
|
||||
// initialize validators here
|
||||
nested_validator::init(!control.is_speculative_block());
|
||||
nested_validator::init(!control.is_producing_block());
|
||||
}
|
||||
|
||||
void validate() {
|
||||
|
||||
@@ -63,9 +63,6 @@ namespace eosio { namespace chain {
|
||||
//Immediately exits currently running wasm. UB is called when no wasm running
|
||||
void exit();
|
||||
|
||||
//Returns true if the code is cached
|
||||
bool is_code_cached(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) const;
|
||||
|
||||
// If substitute_apply is set, then apply calls it before doing anything else. If substitute_apply returns true,
|
||||
// then apply returns immediately.
|
||||
std::function<bool(
|
||||
|
||||
@@ -94,11 +94,6 @@ namespace eosio { namespace chain {
|
||||
});
|
||||
}
|
||||
|
||||
bool is_code_cached(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) const {
|
||||
wasm_cache_index::iterator it = wasm_instantiation_cache.find( boost::make_tuple(code_hash, vm_type, vm_version) );
|
||||
return it != wasm_instantiation_cache.end();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> parse_initial_memory(const Module& module) {
|
||||
std::vector<uint8_t> mem_image;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace eosio { namespace chain {
|
||||
|
||||
|
||||
@@ -349,13 +349,7 @@ auto fn(A... a) {
|
||||
: "cc");
|
||||
}
|
||||
using native_args = vm::flatten_parameters_t<AUTO_PARAM_WORKAROUND(F)>;
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-value"
|
||||
// If a is unpopulated, this reports "statement has no effect [-Werror=unused-value]"
|
||||
eosio::vm::native_value stack[] = { a... };
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
constexpr int cb_ctx_ptr_offset = OFFSET_OF_CONTROL_BLOCK_MEMBER(ctx);
|
||||
apply_context* ctx;
|
||||
asm("mov %%gs:%c[applyContextOffset], %[cPtr]\n"
|
||||
@@ -391,7 +385,7 @@ void register_eosvm_oc(Name n) {
|
||||
if(n == BOOST_HANA_STRING("env.eosio_exit")) return;
|
||||
constexpr auto fn = create_function<F, Preconditions, injected>();
|
||||
constexpr auto index = find_intrinsic_index(n.c_str());
|
||||
[[maybe_unused]] intrinsic the_intrinsic(
|
||||
intrinsic the_intrinsic(
|
||||
n.c_str(),
|
||||
wasm_function_type_provider<std::remove_pointer_t<decltype(fn)>>::type(),
|
||||
reinterpret_cast<void*>(fn),
|
||||
|
||||
Regular → Executable
Regular → Executable
@@ -4,7 +4,6 @@
|
||||
#include <eosio/chain/deep_mind.hpp>
|
||||
|
||||
#include <fc/scoped_exit.hpp>
|
||||
#include <fc/io/json.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/assign/list_of.hpp>
|
||||
@@ -779,202 +778,4 @@ Enables new `get_block_num` intrinsic which returns the current block number.
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<builtin_protocol_feature> read_builtin_protocol_feature( const fc::path& p ) {
|
||||
try {
|
||||
return fc::json::from_file<builtin_protocol_feature>( p );
|
||||
} catch( const fc::exception& e ) {
|
||||
wlog( "problem encountered while reading '${path}':\n${details}",
|
||||
("path", p.generic_string())("details",e.to_detail_string()) );
|
||||
} catch( ... ) {
|
||||
dlog( "unknown problem encountered while reading '${path}'",
|
||||
("path", p.generic_string()) );
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
protocol_feature_set initialize_protocol_features( const fc::path& p, bool populate_missing_builtins ) {
|
||||
using boost::filesystem::directory_iterator;
|
||||
|
||||
protocol_feature_set pfs;
|
||||
|
||||
bool directory_exists = true;
|
||||
|
||||
if( fc::exists( p ) ) {
|
||||
EOS_ASSERT( fc::is_directory( p ), plugin_exception,
|
||||
"Path to protocol-features is not a directory: ${path}",
|
||||
("path", p.generic_string())
|
||||
);
|
||||
} else {
|
||||
if( populate_missing_builtins )
|
||||
boost::filesystem::create_directories( p );
|
||||
else
|
||||
directory_exists = false;
|
||||
}
|
||||
|
||||
auto log_recognized_protocol_feature = []( const builtin_protocol_feature& f, const digest_type& feature_digest ) {
|
||||
if( f.subjective_restrictions.enabled ) {
|
||||
if( f.subjective_restrictions.preactivation_required ) {
|
||||
if( f.subjective_restrictions.earliest_allowed_activation_time == time_point{} ) {
|
||||
ilog( "Support for builtin protocol feature '${codename}' (with digest of '${digest}') is enabled with preactivation required",
|
||||
("codename", builtin_protocol_feature_codename(f.get_codename()))
|
||||
("digest", feature_digest)
|
||||
);
|
||||
} else {
|
||||
ilog( "Support for builtin protocol feature '${codename}' (with digest of '${digest}') is enabled with preactivation required and with an earliest allowed activation time of ${earliest_time}",
|
||||
("codename", builtin_protocol_feature_codename(f.get_codename()))
|
||||
("digest", feature_digest)
|
||||
("earliest_time", f.subjective_restrictions.earliest_allowed_activation_time)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if( f.subjective_restrictions.earliest_allowed_activation_time == time_point{} ) {
|
||||
ilog( "Support for builtin protocol feature '${codename}' (with digest of '${digest}') is enabled without activation restrictions",
|
||||
("codename", builtin_protocol_feature_codename(f.get_codename()))
|
||||
("digest", feature_digest)
|
||||
);
|
||||
} else {
|
||||
ilog( "Support for builtin protocol feature '${codename}' (with digest of '${digest}') is enabled without preactivation required but with an earliest allowed activation time of ${earliest_time}",
|
||||
("codename", builtin_protocol_feature_codename(f.get_codename()))
|
||||
("digest", feature_digest)
|
||||
("earliest_time", f.subjective_restrictions.earliest_allowed_activation_time)
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ilog( "Recognized builtin protocol feature '${codename}' (with digest of '${digest}') but support for it is not enabled",
|
||||
("codename", builtin_protocol_feature_codename(f.get_codename()))
|
||||
("digest", feature_digest)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
map<builtin_protocol_feature_t, fc::path> found_builtin_protocol_features;
|
||||
map<digest_type, std::pair<builtin_protocol_feature, bool> > builtin_protocol_features_to_add;
|
||||
// The bool in the pair is set to true if the builtin protocol feature has already been visited to add
|
||||
map< builtin_protocol_feature_t, std::optional<digest_type> > visited_builtins;
|
||||
|
||||
// Read all builtin protocol features
|
||||
if( directory_exists ) {
|
||||
for( directory_iterator enditr, itr{p}; itr != enditr; ++itr ) {
|
||||
auto file_path = itr->path();
|
||||
if( !fc::is_regular_file( file_path ) || file_path.extension().generic_string().compare( ".json" ) != 0 )
|
||||
continue;
|
||||
|
||||
auto f = read_builtin_protocol_feature( file_path );
|
||||
|
||||
if( !f ) continue;
|
||||
|
||||
auto res = found_builtin_protocol_features.emplace( f->get_codename(), file_path );
|
||||
|
||||
EOS_ASSERT( res.second, plugin_exception,
|
||||
"Builtin protocol feature '${codename}' was already included from a previous_file",
|
||||
("codename", builtin_protocol_feature_codename(f->get_codename()))
|
||||
("current_file", file_path.generic_string())
|
||||
("previous_file", res.first->second.generic_string())
|
||||
);
|
||||
|
||||
const auto feature_digest = f->digest();
|
||||
|
||||
builtin_protocol_features_to_add.emplace( std::piecewise_construct,
|
||||
std::forward_as_tuple( feature_digest ),
|
||||
std::forward_as_tuple( *f, false ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Add builtin protocol features to the protocol feature manager in the right order (to satisfy dependencies)
|
||||
using itr_type = map<digest_type, std::pair<builtin_protocol_feature, bool>>::iterator;
|
||||
std::function<void(const itr_type&)> add_protocol_feature =
|
||||
[&pfs, &builtin_protocol_features_to_add, &visited_builtins, &log_recognized_protocol_feature, &add_protocol_feature]( const itr_type& itr ) -> void {
|
||||
if( itr->second.second ) {
|
||||
return;
|
||||
} else {
|
||||
itr->second.second = true;
|
||||
visited_builtins.emplace( itr->second.first.get_codename(), itr->first );
|
||||
}
|
||||
|
||||
for( const auto& d : itr->second.first.dependencies ) {
|
||||
auto itr2 = builtin_protocol_features_to_add.find( d );
|
||||
if( itr2 != builtin_protocol_features_to_add.end() ) {
|
||||
add_protocol_feature( itr2 );
|
||||
}
|
||||
}
|
||||
|
||||
pfs.add_feature( itr->second.first );
|
||||
|
||||
log_recognized_protocol_feature( itr->second.first, itr->first );
|
||||
};
|
||||
|
||||
for( auto itr = builtin_protocol_features_to_add.begin(); itr != builtin_protocol_features_to_add.end(); ++itr ) {
|
||||
add_protocol_feature( itr );
|
||||
}
|
||||
|
||||
auto output_protocol_feature = [&p]( const builtin_protocol_feature& f, const digest_type& feature_digest ) {
|
||||
string filename( "BUILTIN-" );
|
||||
filename += builtin_protocol_feature_codename( f.get_codename() );
|
||||
filename += ".json";
|
||||
|
||||
auto file_path = p / filename;
|
||||
|
||||
EOS_ASSERT( !fc::exists( file_path ), plugin_exception,
|
||||
"Could not save builtin protocol feature with codename '${codename}' because a file at the following path already exists: ${path}",
|
||||
("codename", builtin_protocol_feature_codename( f.get_codename() ))
|
||||
("path", file_path.generic_string())
|
||||
);
|
||||
|
||||
if( fc::json::save_to_file( f, file_path ) ) {
|
||||
ilog( "Saved default specification for builtin protocol feature '${codename}' (with digest of '${digest}') to: ${path}",
|
||||
("codename", builtin_protocol_feature_codename(f.get_codename()))
|
||||
("digest", feature_digest)
|
||||
("path", file_path.generic_string())
|
||||
);
|
||||
} else {
|
||||
elog( "Error occurred while writing default specification for builtin protocol feature '${codename}' (with digest of '${digest}') to: ${path}",
|
||||
("codename", builtin_protocol_feature_codename(f.get_codename()))
|
||||
("digest", feature_digest)
|
||||
("path", file_path.generic_string())
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
std::function<digest_type(builtin_protocol_feature_t)> add_missing_builtins =
|
||||
[&pfs, &visited_builtins, &output_protocol_feature, &log_recognized_protocol_feature, &add_missing_builtins, populate_missing_builtins]
|
||||
( builtin_protocol_feature_t codename ) -> digest_type {
|
||||
auto res = visited_builtins.emplace( codename, std::optional<digest_type>() );
|
||||
if( !res.second ) {
|
||||
EOS_ASSERT( res.first->second, protocol_feature_exception,
|
||||
"invariant failure: cycle found in builtin protocol feature dependencies"
|
||||
);
|
||||
return *res.first->second;
|
||||
}
|
||||
|
||||
auto f = protocol_feature_set::make_default_builtin_protocol_feature( codename,
|
||||
[&add_missing_builtins]( builtin_protocol_feature_t d ) {
|
||||
return add_missing_builtins( d );
|
||||
} );
|
||||
|
||||
if( !populate_missing_builtins )
|
||||
f.subjective_restrictions.enabled = false;
|
||||
|
||||
const auto& pf = pfs.add_feature( f );
|
||||
res.first->second = pf.feature_digest;
|
||||
|
||||
log_recognized_protocol_feature( f, pf.feature_digest );
|
||||
|
||||
if( populate_missing_builtins )
|
||||
output_protocol_feature( f, pf.feature_digest );
|
||||
|
||||
return pf.feature_digest;
|
||||
};
|
||||
|
||||
for( const auto& p : builtin_protocol_feature_codenames ) {
|
||||
auto itr = found_builtin_protocol_features.find( p.first );
|
||||
if( itr != found_builtin_protocol_features.end() ) continue;
|
||||
|
||||
add_missing_builtins( p.first );
|
||||
}
|
||||
|
||||
return pfs;
|
||||
}
|
||||
|
||||
|
||||
} } // eosio::chain
|
||||
|
||||
@@ -166,9 +166,8 @@ void resource_limits_manager::add_transaction_usage(const flat_set<account_name>
|
||||
|
||||
EOS_ASSERT( cpu_used_in_window <= max_user_use_in_window,
|
||||
tx_cpu_usage_exceeded,
|
||||
"authorizing account '${n}' has insufficient objective cpu resources for this transaction,"
|
||||
" used in window ${cpu_used_in_window}us, allowed in window ${max_user_use_in_window}us",
|
||||
("n", a)
|
||||
"authorizing account '${n}' has insufficient cpu resources for this transaction",
|
||||
("n", name(a))
|
||||
("cpu_used_in_window",cpu_used_in_window)
|
||||
("max_user_use_in_window",max_user_use_in_window) );
|
||||
}
|
||||
@@ -186,9 +185,8 @@ void resource_limits_manager::add_transaction_usage(const flat_set<account_name>
|
||||
|
||||
EOS_ASSERT( net_used_in_window <= max_user_use_in_window,
|
||||
tx_net_usage_exceeded,
|
||||
"authorizing account '${n}' has insufficient net resources for this transaction,"
|
||||
" used in window ${net_used_in_window}, allowed in window ${max_user_use_in_window}",
|
||||
("n", a)
|
||||
"authorizing account '${n}' has insufficient net resources for this transaction",
|
||||
("n", name(a))
|
||||
("net_used_in_window",net_used_in_window)
|
||||
("max_user_use_in_window",max_user_use_in_window) );
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <eosio/chain/thread_utils.hpp>
|
||||
#include <fc/log/logger_config.hpp>
|
||||
|
||||
namespace eosio { namespace chain {
|
||||
|
||||
|
||||
//
|
||||
// named_thread_pool
|
||||
//
|
||||
named_thread_pool::named_thread_pool( std::string name_prefix, size_t num_threads )
|
||||
: _thread_pool( num_threads )
|
||||
, _ioc( num_threads )
|
||||
{
|
||||
_ioc_work.emplace( boost::asio::make_work_guard( _ioc ) );
|
||||
for( size_t i = 0; i < num_threads; ++i ) {
|
||||
boost::asio::post( _thread_pool, [&ioc = _ioc, name_prefix, i]() {
|
||||
std::string tn = name_prefix + "-" + std::to_string( i );
|
||||
fc::set_os_thread_name( tn );
|
||||
ioc.run();
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
named_thread_pool::~named_thread_pool() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void named_thread_pool::stop() {
|
||||
_ioc_work.reset();
|
||||
_ioc.stop();
|
||||
_thread_pool.join();
|
||||
_thread_pool.stop();
|
||||
}
|
||||
|
||||
|
||||
} } // eosio::chain
|
||||
@@ -48,14 +48,14 @@ namespace eosio { namespace chain {
|
||||
const packed_transaction& t,
|
||||
transaction_checktime_timer&& tmr,
|
||||
fc::time_point s,
|
||||
transaction_metadata::trx_type type)
|
||||
bool read_only)
|
||||
:control(c)
|
||||
,packed_trx(t)
|
||||
,undo_session()
|
||||
,trace(std::make_shared<transaction_trace>())
|
||||
,start(s)
|
||||
,transaction_timer(std::move(tmr))
|
||||
,trx_type(type)
|
||||
,is_read_only(read_only)
|
||||
,net_usage(trace->net_usage)
|
||||
,pseudo_start(s)
|
||||
{
|
||||
@@ -82,7 +82,7 @@ namespace eosio { namespace chain {
|
||||
}
|
||||
|
||||
void transaction_context::disallow_transaction_extensions( const char* error_msg )const {
|
||||
if( control.is_speculative_block() ) {
|
||||
if( control.is_producing_block() ) {
|
||||
EOS_THROW( subjective_block_production_exception, error_msg );
|
||||
} else {
|
||||
EOS_THROW( disallowed_transaction_extensions_bad_block_exception, error_msg );
|
||||
@@ -114,7 +114,6 @@ namespace eosio { namespace chain {
|
||||
if( cfg.max_transaction_cpu_usage <= objective_duration_limit.count() ) {
|
||||
objective_duration_limit = fc::microseconds(cfg.max_transaction_cpu_usage);
|
||||
billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
|
||||
tx_cpu_usage_reason = tx_cpu_usage_exceeded_reason::on_chain_consensus_max_transaction_cpu_usage;
|
||||
_deadline = start + objective_duration_limit;
|
||||
}
|
||||
|
||||
@@ -132,7 +131,6 @@ namespace eosio { namespace chain {
|
||||
if( trx_specified_cpu_usage_limit <= objective_duration_limit ) {
|
||||
objective_duration_limit = trx_specified_cpu_usage_limit;
|
||||
billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
|
||||
tx_cpu_usage_reason = tx_cpu_usage_exceeded_reason::user_specified_trx_max_cpu_usage_ms;
|
||||
_deadline = start + objective_duration_limit;
|
||||
}
|
||||
}
|
||||
@@ -183,8 +181,6 @@ namespace eosio { namespace chain {
|
||||
// Possibly limit deadline to subjective max_transaction_time
|
||||
if( max_transaction_time_subjective != fc::microseconds::maximum() && (start + max_transaction_time_subjective) <= _deadline ) {
|
||||
_deadline = start + max_transaction_time_subjective;
|
||||
tx_cpu_usage_reason = billed_cpu_time_us > 0 ? tx_cpu_usage_exceeded_reason::speculative_executed_adjusted_max_transaction_time :
|
||||
tx_cpu_usage_exceeded_reason::node_configured_max_transaction_time;
|
||||
billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
|
||||
}
|
||||
|
||||
@@ -194,22 +190,6 @@ namespace eosio { namespace chain {
|
||||
billing_timer_exception_code = deadline_exception::code_value;
|
||||
}
|
||||
|
||||
if( !explicit_billed_cpu_time ) {
|
||||
int64_t validate_account_cpu_limit = account_cpu_limit - subjective_cpu_bill_us + leeway.count(); // Add leeway to allow powerup
|
||||
// Possibly limit deadline to account subjective cpu left
|
||||
if( subjective_cpu_bill_us > 0 && (start + fc::microseconds(validate_account_cpu_limit) < _deadline) ) {
|
||||
_deadline = start + fc::microseconds(validate_account_cpu_limit);
|
||||
billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
|
||||
tx_cpu_usage_reason = tx_cpu_usage_exceeded_reason::account_cpu_limit;
|
||||
}
|
||||
|
||||
// Fail early if amount of the previous speculative execution is within 10% of remaining account cpu available
|
||||
if( validate_account_cpu_limit > 0 )
|
||||
validate_account_cpu_limit -= EOS_PERCENT( validate_account_cpu_limit, 10 * config::percent_1 );
|
||||
if( validate_account_cpu_limit < 0 ) validate_account_cpu_limit = 0;
|
||||
validate_account_cpu_usage_estimate( billed_cpu_time_us, validate_account_cpu_limit, subjective_cpu_bill_us );
|
||||
}
|
||||
|
||||
// Explicit billed_cpu_time_us should be used, block_deadline will be maximum unless in test code
|
||||
if( explicit_billed_cpu_time ) {
|
||||
_deadline = block_deadline;
|
||||
@@ -218,6 +198,15 @@ namespace eosio { namespace chain {
|
||||
deadline_exception_code = billing_timer_exception_code;
|
||||
}
|
||||
|
||||
if( !explicit_billed_cpu_time ) {
|
||||
// Fail early if amount of the previous speculative execution is within 10% of remaining account cpu available
|
||||
int64_t validate_account_cpu_limit = account_cpu_limit - subjective_cpu_bill_us + leeway.count(); // Add leeway to allow powerup
|
||||
if( validate_account_cpu_limit > 0 )
|
||||
validate_account_cpu_limit -= EOS_PERCENT( validate_account_cpu_limit, 10 * config::percent_1 );
|
||||
if( validate_account_cpu_limit < 0 ) validate_account_cpu_limit = 0;
|
||||
validate_account_cpu_usage_estimate( billed_cpu_time_us, validate_account_cpu_limit, subjective_cpu_bill_us );
|
||||
}
|
||||
|
||||
eager_net_limit = (eager_net_limit/8)*8; // Round down to nearest multiple of word size (8 bytes) so check_net_usage can be efficient
|
||||
|
||||
if( initial_net_usage > 0 )
|
||||
@@ -245,7 +234,8 @@ namespace eosio { namespace chain {
|
||||
}
|
||||
|
||||
void transaction_context::init_for_input_trx( uint64_t packed_trx_unprunable_size,
|
||||
uint64_t packed_trx_prunable_size )
|
||||
uint64_t packed_trx_prunable_size,
|
||||
bool skip_recording )
|
||||
{
|
||||
const transaction& trx = packed_trx.get_transaction();
|
||||
if( trx.transaction_extensions.size() > 0 ) {
|
||||
@@ -279,10 +269,11 @@ namespace eosio { namespace chain {
|
||||
if (!control.skip_trx_checks()) {
|
||||
control.validate_expiration(trx);
|
||||
control.validate_tapos(trx);
|
||||
validate_referenced_accounts( trx, enforce_whiteblacklist && control.is_speculative_block() );
|
||||
validate_referenced_accounts( trx, enforce_whiteblacklist && control.is_producing_block() );
|
||||
}
|
||||
init( initial_net_usage);
|
||||
record_transaction( packed_trx.id(), trx.expiration ); /// checks for dupes
|
||||
if (!skip_recording)
|
||||
record_transaction( packed_trx.id(), trx.expiration ); /// checks for dupes
|
||||
}
|
||||
|
||||
void transaction_context::init_for_deferred_trx( fc::time_point p )
|
||||
@@ -367,7 +358,6 @@ namespace eosio { namespace chain {
|
||||
// NOTE: objective_duration_limit may possibly not be objective anymore due to cpu greylisting, but it should still be no greater than the truly objective objective_duration_limit
|
||||
objective_duration_limit = fc::microseconds(account_cpu_limit);
|
||||
billing_timer_exception_code = tx_cpu_usage_exceeded::code_value;
|
||||
tx_cpu_usage_reason = tx_cpu_usage_exceeded_reason::account_cpu_limit;
|
||||
}
|
||||
|
||||
net_usage = ((net_usage + 7)/8)*8; // Round up to nearest multiple of word size (8 bytes)
|
||||
@@ -414,27 +404,6 @@ namespace eosio { namespace chain {
|
||||
}
|
||||
}
|
||||
|
||||
std::string transaction_context::get_tx_cpu_usage_exceeded_reason_msg(fc::microseconds& limit) const {
|
||||
switch( tx_cpu_usage_reason ) {
|
||||
case tx_cpu_usage_exceeded_reason::account_cpu_limit:
|
||||
limit = objective_duration_limit;
|
||||
return " reached account cpu limit ${limit}us";
|
||||
case tx_cpu_usage_exceeded_reason::on_chain_consensus_max_transaction_cpu_usage:
|
||||
limit = objective_duration_limit;
|
||||
return " reached on chain max_transaction_cpu_usage ${limit}us";
|
||||
case tx_cpu_usage_exceeded_reason::user_specified_trx_max_cpu_usage_ms:
|
||||
limit = objective_duration_limit;
|
||||
return " reached trx specified max_cpu_usage_ms ${limit}us";
|
||||
case tx_cpu_usage_exceeded_reason::node_configured_max_transaction_time:
|
||||
limit = max_transaction_time_subjective;
|
||||
return " reached node configured max-transaction-time ${limit}us";
|
||||
case tx_cpu_usage_exceeded_reason::speculative_executed_adjusted_max_transaction_time:
|
||||
limit = max_transaction_time_subjective;
|
||||
return " reached speculative executed adjusted trx max time ${limit}us";
|
||||
}
|
||||
return "unknown tx_cpu_usage_exceeded";
|
||||
}
|
||||
|
||||
void transaction_context::checktime()const {
|
||||
if(BOOST_LIKELY(transaction_timer.expired == false))
|
||||
return;
|
||||
@@ -452,15 +421,15 @@ namespace eosio { namespace chain {
|
||||
if (subjective_cpu_bill_us > 0) {
|
||||
assert_msg += " with a subjective cpu of (${subjective} us)";
|
||||
}
|
||||
fc::microseconds limit;
|
||||
assert_msg += get_tx_cpu_usage_exceeded_reason_msg(limit);
|
||||
if (cpu_limit_due_to_greylist) {
|
||||
assert_msg = "greylisted " + assert_msg;
|
||||
EOS_THROW( greylist_cpu_usage_exceeded, assert_msg,
|
||||
("billing_timer", now - pseudo_start)("subjective", subjective_cpu_bill_us)("limit", limit) );
|
||||
EOS_THROW( greylist_cpu_usage_exceeded,
|
||||
assert_msg,
|
||||
("now", now)("deadline", _deadline)("start", start)("billing_timer", now - pseudo_start)( "subjective", subjective_cpu_bill_us) );
|
||||
} else {
|
||||
EOS_THROW( tx_cpu_usage_exceeded, assert_msg,
|
||||
("billing_timer", now - pseudo_start)("subjective", subjective_cpu_bill_us)("limit", limit) );
|
||||
EOS_THROW( tx_cpu_usage_exceeded,
|
||||
assert_msg,
|
||||
("now", now)("deadline", _deadline)("start", start)("billing_timer", now - pseudo_start)("subjective", subjective_cpu_bill_us) );
|
||||
}
|
||||
} else if( deadline_exception_code == leeway_deadline_exception::code_value ) {
|
||||
EOS_THROW( leeway_deadline_exception,
|
||||
@@ -523,28 +492,28 @@ namespace eosio { namespace chain {
|
||||
("billed", billed_us)( "billable", objective_duration_limit.count() )
|
||||
);
|
||||
} else {
|
||||
auto graylisted = cpu_limit_due_to_greylist && cpu_limited_by_account;
|
||||
// exceeds trx.max_cpu_usage_ms or cfg.max_transaction_cpu_usage if objective_duration_limit is greater
|
||||
auto account_limit = graylisted ? account_cpu_limit : (cpu_limited_by_account ? account_cpu_limit : objective_duration_limit.count());
|
||||
|
||||
if( billed_us > account_limit ) {
|
||||
fc::microseconds tx_limit;
|
||||
std::string assert_msg;
|
||||
assert_msg.reserve(1024);
|
||||
assert_msg += "billed CPU time (${billed} us) is greater than the maximum";
|
||||
auto assert_msg = [&](bool graylisted, bool subjective) {
|
||||
std::string assert_msg = "billed CPU time (${billed} us) is greater than the maximum";
|
||||
assert_msg += graylisted ? " greylisted" : "";
|
||||
assert_msg += " billable CPU time for the transaction (${billable} us)";
|
||||
assert_msg += subjective_billed_us > 0 ? " with a subjective cpu of (${subjective} us)" : "";
|
||||
assert_msg += get_tx_cpu_usage_exceeded_reason_msg( tx_limit );
|
||||
|
||||
if( graylisted ) {
|
||||
FC_THROW_EXCEPTION( greylist_cpu_usage_exceeded, std::move(assert_msg),
|
||||
("billed", billed_us)("billable", account_limit)("subjective", subjective_billed_us)("limit", tx_limit) );
|
||||
} else {
|
||||
FC_THROW_EXCEPTION( tx_cpu_usage_exceeded, std::move(assert_msg),
|
||||
("billed", billed_us)("billable", account_limit)("subjective", subjective_billed_us)("limit", tx_limit) );
|
||||
}
|
||||
}
|
||||
assert_msg += " billable CPU time for the transaction (${billable} us)";
|
||||
assert_msg += subjective ? " with a subjective cpu of (${subjective} us)" : "";
|
||||
return assert_msg;
|
||||
};
|
||||
auto graylisted = cpu_limit_due_to_greylist && cpu_limited_by_account;
|
||||
auto subjective = subjective_billed_us > 0;
|
||||
// exceeds trx.max_cpu_usage_ms or cfg.max_transaction_cpu_usage if objective_duration_limit is greater
|
||||
auto limit = graylisted ? account_cpu_limit : (cpu_limited_by_account ? account_cpu_limit : objective_duration_limit.count());
|
||||
|
||||
if (graylisted)
|
||||
EOS_ASSERT( billed_us <= limit,
|
||||
greylist_cpu_usage_exceeded,
|
||||
assert_msg(graylisted, subjective),
|
||||
("billed", billed_us)("billable", limit)("subjective", subjective_billed_us));
|
||||
else
|
||||
EOS_ASSERT( billed_us <= limit,
|
||||
tx_cpu_usage_exceeded,
|
||||
assert_msg(graylisted, subjective),
|
||||
("billed", billed_us)("billable", limit)("subjective", subjective_billed_us));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -561,27 +530,28 @@ namespace eosio { namespace chain {
|
||||
("billed", prev_billed_us)( "billable", objective_duration_limit.count() )
|
||||
);
|
||||
} else {
|
||||
auto graylisted = cpu_limit_due_to_greylist && cpu_limited_by_account;
|
||||
// exceeds trx.max_cpu_usage_ms or cfg.max_transaction_cpu_usage if objective_duration_limit is greater
|
||||
auto account_limit = graylisted ? account_cpu_limit : (cpu_limited_by_account ? account_cpu_limit : objective_duration_limit.count());
|
||||
|
||||
if( prev_billed_us >= account_limit ) {
|
||||
std::string assert_msg;
|
||||
assert_msg.reserve(1024);
|
||||
assert_msg += "estimated CPU time (${billed} us) is not less than the maximum";
|
||||
auto assert_msg = [&](bool graylisted, bool subjective) {
|
||||
std::string assert_msg = "estimated CPU time (${billed} us) is not less than the maximum";
|
||||
assert_msg += graylisted ? " greylisted" : "";
|
||||
assert_msg += " billable CPU time for the transaction (${billable} us)";
|
||||
assert_msg += subjective_billed_us > 0 ? " with a subjective cpu of (${subjective} us)" : "";
|
||||
assert_msg += " reached account cpu limit ${limit}us";
|
||||
|
||||
if( graylisted ) {
|
||||
FC_THROW_EXCEPTION( greylist_cpu_usage_exceeded, std::move(assert_msg),
|
||||
("billed", prev_billed_us)("billable", account_limit)("subjective", subjective_billed_us)("limit", account_limit) );
|
||||
} else {
|
||||
FC_THROW_EXCEPTION( tx_cpu_usage_exceeded, std::move(assert_msg),
|
||||
("billed", prev_billed_us)("billable", account_limit)("subjective", subjective_billed_us)("limit", account_limit) );
|
||||
}
|
||||
}
|
||||
assert_msg += " billable CPU time for the transaction (${billable} us)";
|
||||
assert_msg += subjective ? " with a subjective cpu of (${subjective} us)" : "";
|
||||
return assert_msg;
|
||||
};
|
||||
auto graylisted = cpu_limit_due_to_greylist && cpu_limited_by_account;
|
||||
auto subjective = subjective_billed_us > 0;
|
||||
// exceeds trx.max_cpu_usage_ms or cfg.max_transaction_cpu_usage if objective_duration_limit is greater
|
||||
auto limit = graylisted ? account_cpu_limit : (cpu_limited_by_account ? account_cpu_limit : objective_duration_limit.count());
|
||||
|
||||
if (graylisted)
|
||||
EOS_ASSERT( prev_billed_us < limit,
|
||||
greylist_cpu_usage_exceeded,
|
||||
assert_msg(graylisted, subjective),
|
||||
("billed", prev_billed_us)("billable", limit)("subjective", subjective_billed_us));
|
||||
else
|
||||
EOS_ASSERT( prev_billed_us < limit,
|
||||
tx_cpu_usage_exceeded,
|
||||
assert_msg(graylisted, subjective),
|
||||
("billed", prev_billed_us)("billable", limit)("subjective", subjective_billed_us));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -617,7 +587,7 @@ namespace eosio { namespace chain {
|
||||
uint32_t specified_greylist_limit = control.get_greylist_limit();
|
||||
for( const auto& a : bill_to_accounts ) {
|
||||
uint32_t greylist_limit = config::maximum_elastic_resource_multiplier;
|
||||
if( !force_elastic_limits && control.is_speculative_block() ) {
|
||||
if( !force_elastic_limits && control.is_producing_block() ) {
|
||||
if( control.is_resource_greylisted(a) ) {
|
||||
greylist_limit = 1;
|
||||
} else {
|
||||
@@ -636,7 +606,7 @@ namespace eosio { namespace chain {
|
||||
}
|
||||
}
|
||||
|
||||
EOS_ASSERT( (!force_elastic_limits && control.is_speculative_block()) || (!greylisted_cpu && !greylisted_net),
|
||||
EOS_ASSERT( (!force_elastic_limits && control.is_producing_block()) || (!greylisted_cpu && !greylisted_net),
|
||||
transaction_exception, "greylisted when not producing block" );
|
||||
|
||||
return std::make_tuple(account_net_limit, account_cpu_limit, greylisted_net, greylisted_cpu);
|
||||
@@ -801,7 +771,7 @@ namespace eosio { namespace chain {
|
||||
actors.insert( auth.actor );
|
||||
}
|
||||
}
|
||||
EOS_ASSERT( one_auth || is_dry_run(), tx_no_auths, "transaction must have at least one authorization" );
|
||||
EOS_ASSERT( one_auth || is_read_only, tx_no_auths, "transaction must have at least one authorization" );
|
||||
|
||||
if( enforce_actor_whitelist_blacklist ) {
|
||||
control.check_actor_list( actors );
|
||||
|
||||
@@ -11,14 +11,15 @@ recover_keys_future transaction_metadata::start_recover_keys( packed_transaction
|
||||
trx_type t,
|
||||
uint32_t max_variable_sig_size )
|
||||
{
|
||||
return post_async_task( thread_pool, [trx{std::move(trx)}, chain_id, time_limit, t, max_variable_sig_size]() mutable {
|
||||
return async_thread_pool( thread_pool, [trx{std::move(trx)}, chain_id, time_limit, t, max_variable_sig_size]() mutable {
|
||||
fc::time_point deadline = time_limit == fc::microseconds::maximum() ?
|
||||
fc::time_point::maximum() : fc::time_point::now() + time_limit;
|
||||
check_variable_sig_size( trx, max_variable_sig_size );
|
||||
const signed_transaction& trn = trx->get_signed_transaction();
|
||||
flat_set<public_key_type> recovered_pub_keys;
|
||||
fc::microseconds cpu_usage = trn.get_signature_keys( chain_id, deadline, recovered_pub_keys );
|
||||
return std::make_shared<transaction_metadata>( private_type(), std::move( trx ), cpu_usage, std::move( recovered_pub_keys ), t );
|
||||
return std::make_shared<transaction_metadata>( private_type(), std::move( trx ), cpu_usage, std::move( recovered_pub_keys ),
|
||||
t==trx_type::implicit, t==trx_type::scheduled, t==trx_type::read_only);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -108,10 +108,6 @@ namespace eosio { namespace chain {
|
||||
my->runtime_interface->immediately_exit_currently_running_module();
|
||||
}
|
||||
|
||||
bool wasm_interface::is_code_cached(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) const {
|
||||
return my->is_code_cached(code_hash, vm_type, vm_version);
|
||||
}
|
||||
|
||||
wasm_instantiated_module_interface::~wasm_instantiated_module_interface() {}
|
||||
wasm_runtime_interface::~wasm_runtime_interface() {}
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
#include <eosio/chain/protocol_state_object.hpp>
|
||||
#include <eosio/chain/transaction_context.hpp>
|
||||
#include <eosio/chain/apply_context.hpp>
|
||||
#include <fc/crypto/alt_bn128.hpp>
|
||||
#include <fc/crypto/modular_arithmetic.hpp>
|
||||
#include <fc/crypto/blake2.hpp>
|
||||
#include <fc/crypto/sha3.hpp>
|
||||
#include <fc/crypto/k1_recover.hpp>
|
||||
#include <bn256/bn256.h>
|
||||
|
||||
namespace {
|
||||
uint32_t ceil_log2(uint32_t n)
|
||||
@@ -36,7 +36,7 @@ namespace eosio { namespace chain { namespace webassembly {
|
||||
EOS_ASSERT(p.which() < context.db.get<protocol_state_object>().num_supported_key_types, unactivated_key_type,
|
||||
"Unactivated key type used when creating assert_recover_key");
|
||||
|
||||
if(context.control.is_speculative_block())
|
||||
if(context.control.is_producing_block())
|
||||
EOS_ASSERT(s.variable_size() <= context.control.configured_subjective_signature_length_limit(),
|
||||
sig_variable_size_limit_exception, "signature variable length component size greater than subjective maximum");
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace eosio { namespace chain { namespace webassembly {
|
||||
EOS_ASSERT(s.which() < context.db.get<protocol_state_object>().num_supported_key_types, unactivated_signature_type,
|
||||
"Unactivated signature type used during recover_key");
|
||||
|
||||
if(context.control.is_speculative_block())
|
||||
if(context.control.is_producing_block())
|
||||
EOS_ASSERT(s.variable_size() <= context.control.configured_subjective_signature_length_limit(),
|
||||
sig_variable_size_limit_exception, "signature variable length component size greater than subjective maximum");
|
||||
|
||||
@@ -117,37 +117,62 @@ namespace eosio { namespace chain { namespace webassembly {
|
||||
}
|
||||
|
||||
int32_t interface::alt_bn128_add(span<const char> op1, span<const char> op2, span<char> result ) const {
|
||||
if (op1.size() != 64 || op2.size() != 64 || result.size() < 64 ||
|
||||
bn256::g1_add({(const uint8_t*)op1.data(), 64}, {(const uint8_t*)op2.data(), 64}, { (uint8_t*)result.data(), 64}) == -1)
|
||||
bytes bop1(op1.data(), op1.data() + op1.size());
|
||||
bytes bop2(op2.data(), op2.data() + op2.size());
|
||||
|
||||
auto maybe_err = fc::alt_bn128_add(bop1, bop2);
|
||||
if(std::holds_alternative<fc::alt_bn128_error>(maybe_err)) {
|
||||
return return_code::failure;
|
||||
}
|
||||
|
||||
const auto& res = std::get<bytes>(maybe_err);
|
||||
|
||||
if( result.size() < res.size() )
|
||||
return return_code::failure;
|
||||
|
||||
std::memcpy( result.data(), res.data(), res.size() );
|
||||
return return_code::success;
|
||||
}
|
||||
|
||||
int32_t interface::alt_bn128_mul(span<const char> g1_point, span<const char> scalar, span<char> result) const {
|
||||
if (g1_point.size() != 64 || scalar.size() != 32 || result.size() < 64 ||
|
||||
bn256::g1_scalar_mul({(const uint8_t*)g1_point.data(), 64}, {(const uint8_t*)scalar.data(), 32}, { (uint8_t*)result.data(), 64}) == -1)
|
||||
bytes bg1_point(g1_point.data(), g1_point.data() + g1_point.size());
|
||||
bytes bscalar(scalar.data(), scalar.data() + scalar.size());
|
||||
|
||||
auto maybe_err = fc::alt_bn128_mul(bg1_point, bscalar);
|
||||
if(std::holds_alternative<fc::alt_bn128_error>(maybe_err)) {
|
||||
return return_code::failure;
|
||||
}
|
||||
|
||||
const auto& res = std::get<bytes>(maybe_err);
|
||||
|
||||
if( result.size() < res.size() )
|
||||
return return_code::failure;
|
||||
|
||||
std::memcpy( result.data(), res.data(), res.size() );
|
||||
return return_code::success;
|
||||
}
|
||||
|
||||
int32_t interface::alt_bn128_pair(span<const char> g1_g2_pairs) const {
|
||||
bytes bg1_g2_pairs(g1_g2_pairs.data(), g1_g2_pairs.data() + g1_g2_pairs.size());
|
||||
|
||||
auto checktime = [this]() { context.trx_context.checktime(); };
|
||||
auto res = bn256::pairing_check({(const uint8_t*)g1_g2_pairs.data(), g1_g2_pairs.size()} , checktime);
|
||||
if (res == -1)
|
||||
auto res = fc::alt_bn128_pair(bg1_g2_pairs, checktime);
|
||||
if(std::holds_alternative<fc::alt_bn128_error>(res)) {
|
||||
return return_code::failure;
|
||||
else
|
||||
return res? 0 : 1;
|
||||
}
|
||||
|
||||
return !std::get<bool>(res);
|
||||
}
|
||||
|
||||
int32_t interface::mod_exp(span<const char> base,
|
||||
span<const char> exp,
|
||||
span<const char> modulus,
|
||||
span<char> out) const {
|
||||
if (context.control.is_speculative_block()) {
|
||||
if (context.control.is_producing_block()) {
|
||||
unsigned int base_modulus_size = std::max(base.size(), modulus.size());
|
||||
|
||||
if (base_modulus_size < exp.size()) {
|
||||
EOS_THROW(subjective_block_production_exception,
|
||||
EOS_THROW(subjective_block_production_exception,
|
||||
"mod_exp restriction: exponent bit size cannot exceed bit size of either base or modulus");
|
||||
}
|
||||
|
||||
@@ -156,7 +181,7 @@ namespace eosio { namespace chain { namespace webassembly {
|
||||
uint64_t bit_calc = 5 * ceil_log2(exp.size()) + 8 * ceil_log2(base_modulus_size);
|
||||
|
||||
if (bit_calc_limit < bit_calc) {
|
||||
EOS_THROW(subjective_block_production_exception,
|
||||
EOS_THROW(subjective_block_production_exception,
|
||||
"mod_exp restriction: bit size too large for input arguments");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ void launch_compile_monitor(int nodeos_fd) {
|
||||
sigaddset(&set, SIGQUIT);
|
||||
sigprocmask(SIG_BLOCK, &set, nullptr);
|
||||
|
||||
struct sigaction sa{};
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = SIG_DFL;
|
||||
sa.sa_flags = SA_NOCLDWAIT;
|
||||
sigaction(SIGCHLD, &sa, nullptr);
|
||||
|
||||
@@ -89,7 +89,7 @@ bool write_message_with_fds(int fd_to_send_to, const eosvmoc_message& message, c
|
||||
.iov_len = sz
|
||||
};
|
||||
union {
|
||||
char buf[CMSG_SPACE(max_num_fds * sizeof(int))] = {};
|
||||
char buf[CMSG_SPACE(max_num_fds * sizeof(int))];
|
||||
struct cmsghdr align;
|
||||
} u;
|
||||
|
||||
@@ -108,7 +108,6 @@ bool write_message_with_fds(int fd_to_send_to, const eosvmoc_message& message, c
|
||||
memcpy(p, &thisfd, sizeof(thisfd));
|
||||
p += sizeof(thisfd);
|
||||
}
|
||||
msg.msg_controllen = cmsg->cmsg_len;
|
||||
}
|
||||
|
||||
int wrote;
|
||||
|
||||
+1
-1
Submodule libraries/chainbase updated: 1f058fff94...62d3f99059
@@ -1,7 +1,2 @@
|
||||
add_library(leap-cli11 INTERFACE)
|
||||
|
||||
set(CLI11_TESTING OFF)
|
||||
add_subdirectory(cli11)
|
||||
mark_as_advanced(CLI_CXX_STD CLI_EXAMPLES CLI_SINGLE_FILE CLI_SINGLE_FILE_TESTS CLI_TESTING)
|
||||
target_link_libraries(leap-cli11 INTERFACE CLI11::CLI11)
|
||||
|
||||
target_include_directories(leap-cli11 INTERFACE include)
|
||||
Submodule libraries/cli11/cli11 deleted from 90b0d6720d
File diff suppressed because it is too large
Load Diff
+1
-1
Submodule libraries/eos-vm updated: 1592261e96...7db4b33fa9
@@ -1,5 +1,11 @@
|
||||
add_subdirectory( secp256k1 )
|
||||
add_subdirectory( libraries/bn256/src )
|
||||
|
||||
set( WITH_PROCPS OFF CACHE BOOL "" FORCE)
|
||||
set( CURVE "ALT_BN128" CACHE STRING "" FORCE)
|
||||
set( USE_ASM OFF CACHE BOOL "" FORCE)
|
||||
set( IS_LIBFF_PARENT OFF CACHE BOOL "" FORCE)
|
||||
set( FF_INSTALL_COMPONENT "${FC_INSTALL_COMPONENT}")
|
||||
add_subdirectory( libraries/ff )
|
||||
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
|
||||
@@ -24,16 +30,19 @@ set( fc_sources
|
||||
src/io/console.cpp
|
||||
src/filesystem.cpp
|
||||
src/interprocess/file_mapping.cpp
|
||||
src/interprocess/mmap_struct.cpp
|
||||
src/log/log_message.cpp
|
||||
src/log/logger.cpp
|
||||
src/log/appender.cpp
|
||||
src/log/console_appender.cpp
|
||||
src/log/console_appender.cpp
|
||||
src/log/dmlog_appender.cpp
|
||||
src/log/logger_config.cpp
|
||||
src/crypto/_digest_common.cpp
|
||||
src/crypto/aes.cpp
|
||||
src/crypto/crc.cpp
|
||||
src/crypto/city.cpp
|
||||
# src/crypto/base32.cpp
|
||||
src/crypto/base36.cpp
|
||||
src/crypto/base58.cpp
|
||||
src/crypto/base64.cpp
|
||||
src/crypto/bigint.cpp
|
||||
@@ -44,6 +53,8 @@ set( fc_sources
|
||||
src/crypto/sha256.cpp
|
||||
src/crypto/sha224.cpp
|
||||
src/crypto/sha512.cpp
|
||||
src/crypto/dh.cpp
|
||||
src/crypto/blowfish.cpp
|
||||
src/crypto/elliptic_common.cpp
|
||||
src/crypto/elliptic_impl_priv.cpp
|
||||
src/crypto/elliptic_secp256k1.cpp
|
||||
@@ -53,16 +64,20 @@ set( fc_sources
|
||||
src/crypto/public_key.cpp
|
||||
src/crypto/private_key.cpp
|
||||
src/crypto/signature.cpp
|
||||
src/crypto/alt_bn128.cpp
|
||||
src/crypto/modular_arithmetic.cpp
|
||||
src/crypto/blake2.cpp
|
||||
src/crypto/k1_recover.cpp
|
||||
src/network/ip.cpp
|
||||
src/network/platform_root_ca.cpp
|
||||
src/network/resolve.cpp
|
||||
src/network/udp_socket.cpp
|
||||
src/network/url.cpp
|
||||
src/network/http/http_client.cpp
|
||||
src/compress/smaz.cpp
|
||||
src/compress/zlib.cpp
|
||||
src/log/gelf_appender.cpp
|
||||
src/log/zipkin.cpp
|
||||
)
|
||||
|
||||
file( GLOB_RECURSE fc_headers ${CMAKE_CURRENT_SOURCE_DIR} *.hpp *.h )
|
||||
@@ -102,22 +117,6 @@ find_package(Boost 1.66 REQUIRED COMPONENTS
|
||||
unit_test_framework
|
||||
iostreams)
|
||||
|
||||
find_path(GMP_INCLUDE_DIR NAMES gmp.h)
|
||||
find_library(GMP_LIBRARY gmp)
|
||||
if(NOT GMP_LIBRARY MATCHES ${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
message( FATAL_ERROR "GMP shared library not found" )
|
||||
endif()
|
||||
set(gmp_library_type SHARED)
|
||||
message(STATUS "GMP: ${GMP_LIBRARY}, ${GMP_INCLUDE_DIR}")
|
||||
add_library(GMP::gmp ${gmp_library_type} IMPORTED)
|
||||
set_target_properties(
|
||||
GMP::gmp PROPERTIES
|
||||
IMPORTED_LOCATION ${GMP_LIBRARY}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${GMP_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(fc PUBLIC GMP::gmp)
|
||||
|
||||
# fc picks up a dependency on zlib via Boost::iostreams, however in some versions of cmake/boost (depending on if CMake config
|
||||
# files are used) the Boost::iostreams target does not have a dependency on zlib itself so add it explicitly
|
||||
find_package(ZLIB REQUIRED)
|
||||
@@ -139,8 +138,9 @@ if(APPLE)
|
||||
find_library(security_framework Security)
|
||||
find_library(corefoundation_framework CoreFoundation)
|
||||
endif()
|
||||
target_link_libraries( fc PUBLIC Boost::date_time Boost::filesystem Boost::chrono Boost::iostreams Threads::Threads
|
||||
OpenSSL::Crypto OpenSSL::SSL ZLIB::ZLIB ${PLATFORM_SPECIFIC_LIBS} ${CMAKE_DL_LIBS} secp256k1 ${security_framework} ${corefoundation_framework})
|
||||
target_link_libraries( fc PUBLIC ff
|
||||
Boost::date_time Boost::filesystem Boost::chrono Boost::iostreams Threads::Threads
|
||||
OpenSSL::Crypto OpenSSL::SSL ZLIB::ZLIB ${PLATFORM_SPECIFIC_LIBS} ${CMAKE_DL_LIBS} secp256k1 ${security_framework} ${corefoundation_framework} )
|
||||
|
||||
# Critically, this ensures that OpenSSL 1.1 & 3.0 both have a variant of BN_zero() with void return value. But it also allows access
|
||||
# to some obsoleted AES functions in 3.0 too, since 3.0's API_COMPAT is effectively 3.0 by default
|
||||
@@ -152,3 +152,4 @@ install(TARGETS fc
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR} COMPONENT dev EXCLUDE_FROM_ALL
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR} COMPONENT dev EXCLUDE_FROM_ALL)
|
||||
install(DIRECTORY include/fc DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR} COMPONENT dev EXCLUDE_FROM_ALL)
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include <fc/api.hpp>
|
||||
#include <fc/thread/thread.hpp>
|
||||
|
||||
namespace fc {
|
||||
|
||||
namespace detail {
|
||||
struct actor_member {
|
||||
#if 1 // BOOST_NO_VARIADIC_TEMPLATES
|
||||
#define RPC_MEMBER_FUNCTOR(z,n,IS_CONST) \
|
||||
template<typename R, typename C, typename P BOOST_PP_ENUM_TRAILING_PARAMS( n, typename A)> \
|
||||
static std::function<fc::future<R>( BOOST_PP_ENUM_PARAMS(n,A) ) > \
|
||||
functor( P p, R (C::*mem_func)(BOOST_PP_ENUM_PARAMS(n,A)) IS_CONST, fc::thread* c = 0) { \
|
||||
return [=](BOOST_PP_ENUM_BINARY_PARAMS(n,A,a))->fc::future<R>{ \
|
||||
return c->async( [=](){ return (p->*mem_func)(BOOST_PP_ENUM_PARAMS(n,a)); } ); }; \
|
||||
}
|
||||
BOOST_PP_REPEAT( 8, RPC_MEMBER_FUNCTOR, const )
|
||||
BOOST_PP_REPEAT( 8, RPC_MEMBER_FUNCTOR, BOOST_PP_EMPTY() )
|
||||
#undef RPC_MEMBER_FUNCTOR
|
||||
|
||||
#else // g++ has a bug that prevents lambdas and varidic templates from working together (G++ Bug 41933)
|
||||
|
||||
template<typename R, typename C, typename P, typename... Args>
|
||||
static std::function<fc::future<R>(Args...)> functor( P&& p, R (C::*mem_func)(Args...), fc::thread* c ) {
|
||||
return [=](Args... args)->fc::future<R>{ c->async( [=]()->R { return p->*mem_func( fc::forward<Args>(args)... ); } ) };
|
||||
}
|
||||
template<typename R, typename C, typename P, typename... Args>
|
||||
static std::function<fc::future<R>(Args...)> functor( P&& p, R (C::*mem_func)(Args...)const, fc::thread* c ){
|
||||
return [=](Args... args)->fc::future<R>{ c->async( [=]()->R { return p->*mem_func( fc::forward<Args>(args)... ); } ) };
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename ThisPtr>
|
||||
struct actor_vtable_visitor {
|
||||
template<typename U>
|
||||
actor_vtable_visitor( fc::thread* t, U&& u ):_thread(t),_this( fc::forward<U>(u) ){}
|
||||
|
||||
template<typename Function, typename MemberPtr>
|
||||
void operator()( const char* name, Function& memb, MemberPtr m )const {
|
||||
memb = actor_member::functor( _this, m, _thread );
|
||||
}
|
||||
fc::thread* _thread;
|
||||
ThisPtr _this;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts all method calls to another thread and
|
||||
* returns a future.
|
||||
*/
|
||||
template<typename Interface>
|
||||
class actor : public api<Interface, detail::actor_member> {
|
||||
public:
|
||||
actor(){}
|
||||
|
||||
template<typename InterfaceType>
|
||||
actor( InterfaceType* p, fc::thread* t = &fc::thread::current() )
|
||||
{
|
||||
this->_vtable.reset(new detail::vtable<Interface,detail::actor_member>() );
|
||||
this->_vtable->template visit<InterfaceType>( detail::actor_vtable_visitor<InterfaceType*>(t, p) );
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fc
|
||||
@@ -0,0 +1,621 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
*********************************************************************
|
||||
* *
|
||||
* Open Bloom Filter *
|
||||
* *
|
||||
* Author: Arash Partow - 2000 *
|
||||
* URL: http://www.partow.net *
|
||||
* URL: http://www.partow.net/programming/hashfunctions/index.html *
|
||||
* *
|
||||
* Copyright notice: *
|
||||
* Free use of the Open Bloom Filter Library is permitted under the *
|
||||
* guidelines and in accordance with the most current version of the *
|
||||
* Common Public License. *
|
||||
* http://www.opensource.org/licenses/cpl1.0.php *
|
||||
* *
|
||||
*********************************************************************
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
|
||||
namespace fc {
|
||||
|
||||
|
||||
static const std::size_t bits_per_char = 0x08; // 8 bits in 1 char(unsigned)
|
||||
static const unsigned char bit_mask[bits_per_char] = {
|
||||
0x01, //00000001
|
||||
0x02, //00000010
|
||||
0x04, //00000100
|
||||
0x08, //00001000
|
||||
0x10, //00010000
|
||||
0x20, //00100000
|
||||
0x40, //01000000
|
||||
0x80 //10000000
|
||||
};
|
||||
|
||||
class bloom_parameters
|
||||
{
|
||||
public:
|
||||
|
||||
bloom_parameters()
|
||||
: minimum_size(1),
|
||||
maximum_size(std::numeric_limits<unsigned long long int>::max()),
|
||||
minimum_number_of_hashes(1),
|
||||
maximum_number_of_hashes(std::numeric_limits<unsigned int>::max()),
|
||||
projected_element_count(10000),
|
||||
false_positive_probability(1.0 / projected_element_count),
|
||||
random_seed(0xA5A5A5A55A5A5A5AULL)
|
||||
{}
|
||||
|
||||
virtual ~bloom_parameters()
|
||||
{}
|
||||
|
||||
inline bool operator!()
|
||||
{
|
||||
return (minimum_size > maximum_size) ||
|
||||
(minimum_number_of_hashes > maximum_number_of_hashes) ||
|
||||
(minimum_number_of_hashes < 1) ||
|
||||
(0 == maximum_number_of_hashes) ||
|
||||
(0 == projected_element_count) ||
|
||||
(false_positive_probability < 0.0) ||
|
||||
(std::numeric_limits<double>::infinity() == std::abs(false_positive_probability)) ||
|
||||
(0 == random_seed) ||
|
||||
(0xFFFFFFFFFFFFFFFFULL == random_seed);
|
||||
}
|
||||
|
||||
//Allowed min/max size of the bloom filter in bits
|
||||
unsigned long long int minimum_size;
|
||||
unsigned long long int maximum_size;
|
||||
|
||||
//Allowed min/max number of hash functions
|
||||
unsigned int minimum_number_of_hashes;
|
||||
unsigned int maximum_number_of_hashes;
|
||||
|
||||
//The approximate number of elements to be inserted
|
||||
//into the bloom filter, should be within one order
|
||||
//of magnitude. The default is 10000.
|
||||
unsigned long long int projected_element_count;
|
||||
|
||||
//The approximate false positive probability expected
|
||||
//from the bloom filter. The default is the reciprocal
|
||||
//of the projected_element_count.
|
||||
double false_positive_probability;
|
||||
|
||||
unsigned long long int random_seed;
|
||||
|
||||
struct optimal_parameters_t
|
||||
{
|
||||
optimal_parameters_t()
|
||||
: number_of_hashes(0),
|
||||
table_size(0)
|
||||
{}
|
||||
|
||||
unsigned int number_of_hashes;
|
||||
unsigned long long int table_size;
|
||||
};
|
||||
|
||||
optimal_parameters_t optimal_parameters;
|
||||
|
||||
virtual bool compute_optimal_parameters()
|
||||
{
|
||||
/*
|
||||
Note:
|
||||
The following will attempt to find the number of hash functions
|
||||
and minimum amount of storage bits required to construct a bloom
|
||||
filter consistent with the user defined false positive probability
|
||||
and estimated element insertion count.
|
||||
*/
|
||||
|
||||
if (!(*this))
|
||||
return false;
|
||||
|
||||
double min_m = std::numeric_limits<double>::infinity();
|
||||
double min_k = 0.0;
|
||||
double curr_m = 0.0;
|
||||
double k = 1.0;
|
||||
|
||||
while (k < 1000.0)
|
||||
{
|
||||
double numerator = (- k * projected_element_count);
|
||||
double denominator = std::log(1.0 - std::pow(false_positive_probability, 1.0 / k));
|
||||
curr_m = numerator / denominator;
|
||||
if (curr_m < min_m)
|
||||
{
|
||||
min_m = curr_m;
|
||||
min_k = k;
|
||||
}
|
||||
k += 1.0;
|
||||
}
|
||||
|
||||
optimal_parameters_t& optp = optimal_parameters;
|
||||
|
||||
optp.number_of_hashes = static_cast<unsigned int>(min_k);
|
||||
optp.table_size = static_cast<unsigned long long int>(min_m);
|
||||
optp.table_size += (((optp.table_size % bits_per_char) != 0) ? (bits_per_char - (optp.table_size % bits_per_char)) : 0);
|
||||
|
||||
if (optp.number_of_hashes < minimum_number_of_hashes)
|
||||
optp.number_of_hashes = minimum_number_of_hashes;
|
||||
else if (optp.number_of_hashes > maximum_number_of_hashes)
|
||||
optp.number_of_hashes = maximum_number_of_hashes;
|
||||
|
||||
if (optp.table_size < minimum_size)
|
||||
optp.table_size = minimum_size;
|
||||
else if (optp.table_size > maximum_size)
|
||||
optp.table_size = maximum_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class bloom_filter
|
||||
{
|
||||
protected:
|
||||
|
||||
typedef unsigned int bloom_type;
|
||||
typedef unsigned char cell_type;
|
||||
|
||||
public:
|
||||
|
||||
bloom_filter()
|
||||
: salt_count_(0),
|
||||
table_size_(0),
|
||||
raw_table_size_(0),
|
||||
projected_element_count_(0),
|
||||
inserted_element_count_(0),
|
||||
random_seed_(0),
|
||||
desired_false_positive_probability_(0.0)
|
||||
{}
|
||||
|
||||
bloom_filter(const bloom_parameters& p)
|
||||
: projected_element_count_(p.projected_element_count),
|
||||
inserted_element_count_(0),
|
||||
random_seed_((p.random_seed * 0xA5A5A5A5) + 1),
|
||||
desired_false_positive_probability_(p.false_positive_probability)
|
||||
{
|
||||
salt_count_ = p.optimal_parameters.number_of_hashes;
|
||||
table_size_ = p.optimal_parameters.table_size;
|
||||
generate_unique_salt();
|
||||
raw_table_size_ = table_size_ / bits_per_char;
|
||||
|
||||
bit_table_.resize( static_cast<std::size_t>(raw_table_size_) );
|
||||
//bit_table_ = new cell_type[static_cast<std::size_t>(raw_table_size_)];
|
||||
std::fill_n(bit_table_.data(),raw_table_size_,0x00);
|
||||
}
|
||||
|
||||
bloom_filter(const bloom_filter& filter)
|
||||
{
|
||||
this->operator=(filter);
|
||||
}
|
||||
|
||||
inline bool operator == (const bloom_filter& f) const
|
||||
{
|
||||
if (this != &f)
|
||||
{
|
||||
return
|
||||
(salt_count_ == f.salt_count_) &&
|
||||
(table_size_ == f.table_size_) &&
|
||||
(raw_table_size_ == f.raw_table_size_) &&
|
||||
(projected_element_count_ == f.projected_element_count_) &&
|
||||
(inserted_element_count_ == f.inserted_element_count_) &&
|
||||
(random_seed_ == f.random_seed_) &&
|
||||
(desired_false_positive_probability_ == f.desired_false_positive_probability_) &&
|
||||
(salt_ == f.salt_) &&
|
||||
std::equal(f.bit_table_.data(),f.bit_table_.data() + raw_table_size_,bit_table_.data());
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool operator != (const bloom_filter& f) const
|
||||
{
|
||||
return !operator==(f);
|
||||
}
|
||||
|
||||
inline bloom_filter& operator = (const bloom_filter& f)
|
||||
{
|
||||
if (this != &f)
|
||||
{
|
||||
salt_count_ = f.salt_count_;
|
||||
table_size_ = f.table_size_;
|
||||
raw_table_size_ = f.raw_table_size_;
|
||||
projected_element_count_ = f.projected_element_count_;
|
||||
inserted_element_count_ = f.inserted_element_count_;
|
||||
random_seed_ = f.random_seed_;
|
||||
desired_false_positive_probability_ = f.desired_false_positive_probability_;
|
||||
bit_table_.resize( raw_table_size_ );
|
||||
std::copy(f.bit_table_.data(),f.bit_table_.data() + raw_table_size_,bit_table_.data());
|
||||
salt_ = f.salt_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ~bloom_filter()
|
||||
{
|
||||
}
|
||||
|
||||
inline bool operator!() const
|
||||
{
|
||||
return (0 == table_size_);
|
||||
}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
std::fill_n(bit_table_.data(),raw_table_size_,0x00);
|
||||
inserted_element_count_ = 0;
|
||||
}
|
||||
|
||||
inline void insert(const unsigned char* key_begin, const std::size_t& length)
|
||||
{
|
||||
std::size_t bit_index = 0;
|
||||
std::size_t bit = 0;
|
||||
for (std::size_t i = 0; i < salt_.size(); ++i)
|
||||
{
|
||||
compute_indices(hash_ap(key_begin,length,salt_[i]),bit_index,bit);
|
||||
bit_table_[bit_index / bits_per_char] |= bit_mask[bit];
|
||||
}
|
||||
++inserted_element_count_;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void insert(const T& t)
|
||||
{
|
||||
// Note: T must be a C++ POD type.
|
||||
insert(reinterpret_cast<const unsigned char*>(&t),sizeof(T));
|
||||
}
|
||||
|
||||
inline void insert(const std::string& key)
|
||||
{
|
||||
insert(reinterpret_cast<const unsigned char*>(key.c_str()),key.size());
|
||||
}
|
||||
|
||||
inline void insert(const char* data, const std::size_t& length)
|
||||
{
|
||||
insert(reinterpret_cast<const unsigned char*>(data),length);
|
||||
}
|
||||
|
||||
template<typename InputIterator>
|
||||
inline void insert(const InputIterator begin, const InputIterator end)
|
||||
{
|
||||
InputIterator itr = begin;
|
||||
while (end != itr)
|
||||
{
|
||||
insert(*(itr++));
|
||||
}
|
||||
}
|
||||
|
||||
inline virtual bool contains(const unsigned char* key_begin, const std::size_t length) const
|
||||
{
|
||||
std::size_t bit_index = 0;
|
||||
std::size_t bit = 0;
|
||||
for (std::size_t i = 0; i < salt_.size(); ++i)
|
||||
{
|
||||
compute_indices(hash_ap(key_begin,length,salt_[i]),bit_index,bit);
|
||||
if ((bit_table_[bit_index / bits_per_char] & bit_mask[bit]) != bit_mask[bit])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool contains(const T& t) const
|
||||
{
|
||||
return contains(reinterpret_cast<const unsigned char*>(&t),static_cast<std::size_t>(sizeof(T)));
|
||||
}
|
||||
|
||||
inline bool contains(const std::string& key) const
|
||||
{
|
||||
return contains(reinterpret_cast<const unsigned char*>(key.c_str()),key.size());
|
||||
}
|
||||
|
||||
inline bool contains(const char* data, const std::size_t& length) const
|
||||
{
|
||||
return contains(reinterpret_cast<const unsigned char*>(data),length);
|
||||
}
|
||||
|
||||
template<typename InputIterator>
|
||||
inline InputIterator contains_all(const InputIterator begin, const InputIterator end) const
|
||||
{
|
||||
InputIterator itr = begin;
|
||||
while (end != itr)
|
||||
{
|
||||
if (!contains(*itr))
|
||||
{
|
||||
return itr;
|
||||
}
|
||||
++itr;
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
template<typename InputIterator>
|
||||
inline InputIterator contains_none(const InputIterator begin, const InputIterator end) const
|
||||
{
|
||||
InputIterator itr = begin;
|
||||
while (end != itr)
|
||||
{
|
||||
if (contains(*itr))
|
||||
{
|
||||
return itr;
|
||||
}
|
||||
++itr;
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
inline virtual unsigned long long int size() const
|
||||
{
|
||||
return table_size_;
|
||||
}
|
||||
|
||||
inline std::size_t element_count() const
|
||||
{
|
||||
return inserted_element_count_;
|
||||
}
|
||||
|
||||
inline double effective_fpp() const
|
||||
{
|
||||
/*
|
||||
Note:
|
||||
The effective false positive probability is calculated using the
|
||||
designated table size and hash function count in conjunction with
|
||||
the current number of inserted elements - not the user defined
|
||||
predicated/expected number of inserted elements.
|
||||
*/
|
||||
return std::pow(1.0 - std::exp(-1.0 * salt_.size() * inserted_element_count_ / size()), 1.0 * salt_.size());
|
||||
}
|
||||
|
||||
inline bloom_filter& operator &= (const bloom_filter& f)
|
||||
{
|
||||
/* intersection */
|
||||
if (
|
||||
(salt_count_ == f.salt_count_) &&
|
||||
(table_size_ == f.table_size_) &&
|
||||
(random_seed_ == f.random_seed_)
|
||||
)
|
||||
{
|
||||
for (std::size_t i = 0; i < raw_table_size_; ++i)
|
||||
{
|
||||
bit_table_[i] &= f.bit_table_[i];
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bloom_filter& operator |= (const bloom_filter& f)
|
||||
{
|
||||
/* union */
|
||||
if (
|
||||
(salt_count_ == f.salt_count_) &&
|
||||
(table_size_ == f.table_size_) &&
|
||||
(random_seed_ == f.random_seed_)
|
||||
)
|
||||
{
|
||||
for (std::size_t i = 0; i < raw_table_size_; ++i)
|
||||
{
|
||||
bit_table_[i] |= f.bit_table_[i];
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bloom_filter& operator ^= (const bloom_filter& f)
|
||||
{
|
||||
/* difference */
|
||||
if (
|
||||
(salt_count_ == f.salt_count_) &&
|
||||
(table_size_ == f.table_size_) &&
|
||||
(random_seed_ == f.random_seed_)
|
||||
)
|
||||
{
|
||||
for (std::size_t i = 0; i < raw_table_size_; ++i)
|
||||
{
|
||||
bit_table_[i] ^= f.bit_table_[i];
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const cell_type* table() const
|
||||
{
|
||||
return bit_table_.data();
|
||||
}
|
||||
|
||||
inline std::size_t hash_count()
|
||||
{
|
||||
return salt_.size();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
inline virtual void compute_indices(const bloom_type& hash, std::size_t& bit_index, std::size_t& bit) const
|
||||
{
|
||||
bit_index = hash % table_size_;
|
||||
bit = bit_index % bits_per_char;
|
||||
}
|
||||
|
||||
void generate_unique_salt()
|
||||
{
|
||||
/*
|
||||
Note:
|
||||
A distinct hash function need not be implementation-wise
|
||||
distinct. In the current implementation "seeding" a common
|
||||
hash function with different values seems to be adequate.
|
||||
*/
|
||||
const unsigned int predef_salt_count = 128;
|
||||
static const bloom_type predef_salt[predef_salt_count] =
|
||||
{
|
||||
0xAAAAAAAA, 0x55555555, 0x33333333, 0xCCCCCCCC,
|
||||
0x66666666, 0x99999999, 0xB5B5B5B5, 0x4B4B4B4B,
|
||||
0xAA55AA55, 0x55335533, 0x33CC33CC, 0xCC66CC66,
|
||||
0x66996699, 0x99B599B5, 0xB54BB54B, 0x4BAA4BAA,
|
||||
0xAA33AA33, 0x55CC55CC, 0x33663366, 0xCC99CC99,
|
||||
0x66B566B5, 0x994B994B, 0xB5AAB5AA, 0xAAAAAA33,
|
||||
0x555555CC, 0x33333366, 0xCCCCCC99, 0x666666B5,
|
||||
0x9999994B, 0xB5B5B5AA, 0xFFFFFFFF, 0xFFFF0000,
|
||||
0xB823D5EB, 0xC1191CDF, 0xF623AEB3, 0xDB58499F,
|
||||
0xC8D42E70, 0xB173F616, 0xA91A5967, 0xDA427D63,
|
||||
0xB1E8A2EA, 0xF6C0D155, 0x4909FEA3, 0xA68CC6A7,
|
||||
0xC395E782, 0xA26057EB, 0x0CD5DA28, 0x467C5492,
|
||||
0xF15E6982, 0x61C6FAD3, 0x9615E352, 0x6E9E355A,
|
||||
0x689B563E, 0x0C9831A8, 0x6753C18B, 0xA622689B,
|
||||
0x8CA63C47, 0x42CC2884, 0x8E89919B, 0x6EDBD7D3,
|
||||
0x15B6796C, 0x1D6FDFE4, 0x63FF9092, 0xE7401432,
|
||||
0xEFFE9412, 0xAEAEDF79, 0x9F245A31, 0x83C136FC,
|
||||
0xC3DA4A8C, 0xA5112C8C, 0x5271F491, 0x9A948DAB,
|
||||
0xCEE59A8D, 0xB5F525AB, 0x59D13217, 0x24E7C331,
|
||||
0x697C2103, 0x84B0A460, 0x86156DA9, 0xAEF2AC68,
|
||||
0x23243DA5, 0x3F649643, 0x5FA495A8, 0x67710DF8,
|
||||
0x9A6C499E, 0xDCFB0227, 0x46A43433, 0x1832B07A,
|
||||
0xC46AFF3C, 0xB9C8FFF0, 0xC9500467, 0x34431BDF,
|
||||
0xB652432B, 0xE367F12B, 0x427F4C1B, 0x224C006E,
|
||||
0x2E7E5A89, 0x96F99AA5, 0x0BEB452A, 0x2FD87C39,
|
||||
0x74B2E1FB, 0x222EFD24, 0xF357F60C, 0x440FCB1E,
|
||||
0x8BBE030F, 0x6704DC29, 0x1144D12F, 0x948B1355,
|
||||
0x6D8FD7E9, 0x1C11A014, 0xADD1592F, 0xFB3C712E,
|
||||
0xFC77642F, 0xF9C4CE8C, 0x31312FB9, 0x08B0DD79,
|
||||
0x318FA6E7, 0xC040D23D, 0xC0589AA7, 0x0CA5C075,
|
||||
0xF874B172, 0x0CF914D5, 0x784D3280, 0x4E8CFEBC,
|
||||
0xC569F575, 0xCDB2A091, 0x2CC016B4, 0x5C5F4421
|
||||
};
|
||||
|
||||
if (salt_count_ <= predef_salt_count)
|
||||
{
|
||||
std::copy(predef_salt,
|
||||
predef_salt + salt_count_,
|
||||
std::back_inserter(salt_));
|
||||
for (unsigned int i = 0; i < salt_.size(); ++i)
|
||||
{
|
||||
/*
|
||||
Note:
|
||||
This is done to integrate the user defined random seed,
|
||||
so as to allow for the generation of unique bloom filter
|
||||
instances.
|
||||
*/
|
||||
salt_[i] = salt_[i] * salt_[(i + 3) % salt_.size()] + static_cast<bloom_type>(random_seed_);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::copy(predef_salt,predef_salt + predef_salt_count,std::back_inserter(salt_));
|
||||
srand(static_cast<unsigned int>(random_seed_));
|
||||
while (salt_.size() < salt_count_)
|
||||
{
|
||||
bloom_type current_salt = static_cast<bloom_type>(rand()) * static_cast<bloom_type>(rand());
|
||||
if (0 == current_salt) continue;
|
||||
if (salt_.end() == std::find(salt_.begin(), salt_.end(), current_salt))
|
||||
{
|
||||
salt_.push_back(current_salt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bloom_type hash_ap(const unsigned char* begin, std::size_t remaining_length, bloom_type hash) const
|
||||
{
|
||||
const unsigned char* itr = begin;
|
||||
unsigned int loop = 0;
|
||||
while (remaining_length >= 8)
|
||||
{
|
||||
const unsigned int& i1 = *(reinterpret_cast<const unsigned int*>(itr)); itr += sizeof(unsigned int);
|
||||
const unsigned int& i2 = *(reinterpret_cast<const unsigned int*>(itr)); itr += sizeof(unsigned int);
|
||||
hash ^= (hash << 7) ^ i1 * (hash >> 3) ^
|
||||
(~((hash << 11) + (i2 ^ (hash >> 5))));
|
||||
remaining_length -= 8;
|
||||
}
|
||||
if (remaining_length)
|
||||
{
|
||||
if (remaining_length >= 4)
|
||||
{
|
||||
const unsigned int& i = *(reinterpret_cast<const unsigned int*>(itr));
|
||||
if (loop & 0x01)
|
||||
hash ^= (hash << 7) ^ i * (hash >> 3);
|
||||
else
|
||||
hash ^= (~((hash << 11) + (i ^ (hash >> 5))));
|
||||
++loop;
|
||||
remaining_length -= 4;
|
||||
itr += sizeof(unsigned int);
|
||||
}
|
||||
if (remaining_length >= 2)
|
||||
{
|
||||
const unsigned short& i = *(reinterpret_cast<const unsigned short*>(itr));
|
||||
if (loop & 0x01)
|
||||
hash ^= (hash << 7) ^ i * (hash >> 3);
|
||||
else
|
||||
hash ^= (~((hash << 11) + (i ^ (hash >> 5))));
|
||||
++loop;
|
||||
remaining_length -= 2;
|
||||
itr += sizeof(unsigned short);
|
||||
}
|
||||
if (remaining_length)
|
||||
{
|
||||
hash += ((*itr) ^ (hash * 0xA5A5A5A5)) + loop;
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<bloom_type> salt_;
|
||||
std::vector<unsigned char> bit_table_;
|
||||
unsigned int salt_count_;
|
||||
unsigned long long int table_size_;
|
||||
unsigned long long int raw_table_size_;
|
||||
unsigned long long int projected_element_count_;
|
||||
unsigned int inserted_element_count_;
|
||||
unsigned long long int random_seed_;
|
||||
double desired_false_positive_probability_;
|
||||
};
|
||||
|
||||
inline bloom_filter operator & (const bloom_filter& a, const bloom_filter& b)
|
||||
{
|
||||
bloom_filter result = a;
|
||||
result &= b;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bloom_filter operator | (const bloom_filter& a, const bloom_filter& b)
|
||||
{
|
||||
bloom_filter result = a;
|
||||
result |= b;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bloom_filter operator ^ (const bloom_filter& a, const bloom_filter& b)
|
||||
{
|
||||
bloom_filter result = a;
|
||||
result ^= b;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
} // namespace fc
|
||||
|
||||
|
||||
FC_REFLECT( fc::bloom_filter, (salt_)(bit_table_)(salt_count_)(table_size_)(raw_table_size_)(projected_element_count_)(inserted_element_count_)(random_seed_)(desired_false_positive_probability_) )
|
||||
FC_REFLECT( fc::bloom_parameters::optimal_parameters_t, (number_of_hashes)(table_size) )
|
||||
FC_REFLECT( fc::bloom_parameters, (minimum_size)(maximum_size)(minimum_number_of_hashes)(maximum_number_of_hashes)(projected_element_count)(false_positive_probability)(random_seed)(optimal_parameters) )
|
||||
|
||||
/*
|
||||
Note 1:
|
||||
If it can be guaranteed that bits_per_char will be of the form 2^n then
|
||||
the following optimization can be used:
|
||||
|
||||
hash_table[bit_index >> n] |= bit_mask[bit_index & (bits_per_char - 1)];
|
||||
|
||||
Note 2:
|
||||
For performance reasons where possible when allocating memory it should
|
||||
be aligned (aligned_alloc) according to the architecture being used.
|
||||
*/
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace fc {
|
||||
|
||||
std::string smaz_compress( const std::string& in );
|
||||
std::string smaz_decompress( const std::string& compressed );
|
||||
|
||||
} // namespace fc
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <fc/utility.hpp>
|
||||
|
||||
namespace fc {
|
||||
using bytes = std::vector<char>;
|
||||
|
||||
enum class alt_bn128_error : int32_t {
|
||||
operand_component_invalid,
|
||||
operand_not_in_curve,
|
||||
pairing_list_size_error,
|
||||
operand_outside_g2,
|
||||
input_len_error,
|
||||
invalid_scalar_size
|
||||
};
|
||||
|
||||
std::variant<alt_bn128_error, bytes> alt_bn128_add(const bytes& op1, const bytes& op2);
|
||||
std::variant<alt_bn128_error, bytes> alt_bn128_mul(const bytes& g1_point, const bytes& scalar);
|
||||
std::variant<alt_bn128_error, bool> alt_bn128_pair(const bytes& g1_g2_pairs, const yield_function_t& yield);
|
||||
|
||||
} // fc
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include <fc/vector.hpp>
|
||||
#include <fc/string.hpp>
|
||||
|
||||
namespace fc
|
||||
{
|
||||
std::vector<char> from_base32( const fc::string& b32 );
|
||||
fc::string to_base32( const std::vector<char>& vec );
|
||||
fc::string to_base32( const char* data, size_t len );
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include <fc/vector.hpp>
|
||||
#include <fc/string.hpp>
|
||||
|
||||
namespace fc
|
||||
{
|
||||
std::vector<char> from_base36( const fc::string& b36 );
|
||||
fc::string to_base36( const std::vector<char>& vec );
|
||||
fc::string to_base36( const char* data, size_t len );
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
// Blowfish.h Header File
|
||||
//
|
||||
// BLOWFISH ENCRYPTION ALGORITHM
|
||||
//
|
||||
// encryption and decryption of Byte Strings using the Blowfish encryption Algorithm.
|
||||
// Blowfish is a block cipher that encrypts data in 8-byte blocks. The algorithm consists
|
||||
// of two parts: a key-expansion part and a data-ancryption part. Key expansion converts a
|
||||
// variable key of at least 1 and at most 56 bytes into several subkey arrays totaling
|
||||
// 4168 bytes. Blowfish has 16 rounds. Each round consists of a key-dependent permutation,
|
||||
// and a key and data-dependent substitution. All operations are XORs and additions on 32-bit words.
|
||||
// The only additional operations are four indexed array data lookups per round.
|
||||
// Blowfish uses a large number of subkeys. These keys must be precomputed before any data
|
||||
// encryption or decryption. The P-array consists of 18 32-bit subkeys: P0, P1,...,P17.
|
||||
// There are also four 32-bit S-boxes with 256 entries each: S0,0, S0,1,...,S0,255;
|
||||
// S1,0, S1,1,...,S1,255; S2,0, S2,1,...,S2,255; S3,0, S3,1,...,S3,255;
|
||||
//
|
||||
// The Electronic Code Book (ECB), Cipher Block Chaining (CBC) and Cipher Feedback modes
|
||||
// are used:
|
||||
//
|
||||
// In ECB mode if the same block is encrypted twice with the same key, the resulting
|
||||
// ciphertext blocks are the same.
|
||||
//
|
||||
// In CBC Mode a ciphertext block is obtained by first xoring the
|
||||
// plaintext block with the previous ciphertext block, and encrypting the resulting value.
|
||||
//
|
||||
// In CFB mode a ciphertext block is obtained by encrypting the previous ciphertext block
|
||||
// and xoring the resulting value with the plaintext
|
||||
//
|
||||
// The previous ciphertext block is usually stored in an Initialization Vector (IV).
|
||||
// An Initialization Vector of zero is commonly used for the first block, though other
|
||||
// arrangements are also in use.
|
||||
|
||||
/*
|
||||
http://www.counterpane.com/vectors.txt
|
||||
Test vectors by Eric Young. These tests all assume Blowfish with 16
|
||||
rounds.
|
||||
|
||||
All data is shown as a hex string with 012345 loading as
|
||||
data[0]=0x01;
|
||||
data[1]=0x23;
|
||||
data[2]=0x45;
|
||||
ecb test data (taken from the DES validation tests)
|
||||
|
||||
key bytes clear bytes cipher bytes
|
||||
0000000000000000 0000000000000000 4EF997456198DD78
|
||||
FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF 51866FD5B85ECB8A
|
||||
3000000000000000 1000000000000001 7D856F9A613063F2 ???
|
||||
1111111111111111 1111111111111111 2466DD878B963C9D
|
||||
0123456789ABCDEF 1111111111111111 61F9C3802281B096
|
||||
1111111111111111 0123456789ABCDEF 7D0CC630AFDA1EC7
|
||||
0000000000000000 0000000000000000 4EF997456198DD78
|
||||
FEDCBA9876543210 0123456789ABCDEF 0ACEAB0FC6A0A28D
|
||||
7CA110454A1A6E57 01A1D6D039776742 59C68245EB05282B
|
||||
0131D9619DC1376E 5CD54CA83DEF57DA B1B8CC0B250F09A0
|
||||
07A1133E4A0B2686 0248D43806F67172 1730E5778BEA1DA4
|
||||
3849674C2602319E 51454B582DDF440A A25E7856CF2651EB
|
||||
04B915BA43FEB5B6 42FD443059577FA2 353882B109CE8F1A
|
||||
0113B970FD34F2CE 059B5E0851CF143A 48F4D0884C379918
|
||||
0170F175468FB5E6 0756D8E0774761D2 432193B78951FC98
|
||||
43297FAD38E373FE 762514B829BF486A 13F04154D69D1AE5
|
||||
07A7137045DA2A16 3BDD119049372802 2EEDDA93FFD39C79
|
||||
04689104C2FD3B2F 26955F6835AF609A D887E0393C2DA6E3
|
||||
37D06BB516CB7546 164D5E404F275232 5F99D04F5B163969
|
||||
1F08260D1AC2465E 6B056E18759F5CCA 4A057A3B24D3977B
|
||||
584023641ABA6176 004BD6EF09176062 452031C1E4FADA8E
|
||||
025816164629B007 480D39006EE762F2 7555AE39F59B87BD
|
||||
49793EBC79B3258F 437540C8698F3CFA 53C55F9CB49FC019
|
||||
4FB05E1515AB73A7 072D43A077075292 7A8E7BFA937E89A3
|
||||
49E95D6D4CA229BF 02FE55778117F12A CF9C5D7A4986ADB5
|
||||
018310DC409B26D6 1D9D5C5018F728C2 D1ABB290658BC778
|
||||
1C587F1C13924FEF 305532286D6F295A 55CB3774D13EF201
|
||||
0101010101010101 0123456789ABCDEF FA34EC4847B268B2
|
||||
1F1F1F1F0E0E0E0E 0123456789ABCDEF A790795108EA3CAE
|
||||
E0FEE0FEF1FEF1FE 0123456789ABCDEF C39E072D9FAC631D
|
||||
0000000000000000 FFFFFFFFFFFFFFFF 014933E0CDAFF6E4
|
||||
FFFFFFFFFFFFFFFF 0000000000000000 F21E9A77B71C49BC
|
||||
0123456789ABCDEF 0000000000000000 245946885754369A
|
||||
FEDCBA9876543210 FFFFFFFFFFFFFFFF 6B5C5A9C5D9E0A5A
|
||||
|
||||
set_key test data
|
||||
data[8]= FEDCBA9876543210
|
||||
c=F9AD597C49DB005E k[ 1]=F0
|
||||
c=E91D21C1D961A6D6 k[ 2]=F0E1
|
||||
c=E9C2B70A1BC65CF3 k[ 3]=F0E1D2
|
||||
c=BE1E639408640F05 k[ 4]=F0E1D2C3
|
||||
c=B39E44481BDB1E6E k[ 5]=F0E1D2C3B4
|
||||
c=9457AA83B1928C0D k[ 6]=F0E1D2C3B4A5
|
||||
c=8BB77032F960629D k[ 7]=F0E1D2C3B4A596
|
||||
c=E87A244E2CC85E82 k[ 8]=F0E1D2C3B4A59687
|
||||
c=15750E7A4F4EC577 k[ 9]=F0E1D2C3B4A5968778
|
||||
c=122BA70B3AB64AE0 k[10]=F0E1D2C3B4A596877869
|
||||
c=3A833C9AFFC537F6 k[11]=F0E1D2C3B4A5968778695A
|
||||
c=9409DA87A90F6BF2 k[12]=F0E1D2C3B4A5968778695A4B
|
||||
c=884F80625060B8B4 k[13]=F0E1D2C3B4A5968778695A4B3C
|
||||
c=1F85031C19E11968 k[14]=F0E1D2C3B4A5968778695A4B3C2D
|
||||
c=79D9373A714CA34F k[15]=F0E1D2C3B4A5968778695A4B3C2D1E ???
|
||||
c=93142887EE3BE15C k[16]=F0E1D2C3B4A5968778695A4B3C2D1E0F
|
||||
c=03429E838CE2D14B k[17]=F0E1D2C3B4A5968778695A4B3C2D1E0F00
|
||||
c=A4299E27469FF67B k[18]=F0E1D2C3B4A5968778695A4B3C2D1E0F0011
|
||||
c=AFD5AED1C1BC96A8 k[19]=F0E1D2C3B4A5968778695A4B3C2D1E0F001122
|
||||
c=10851C0E3858DA9F k[20]=F0E1D2C3B4A5968778695A4B3C2D1E0F00112233
|
||||
c=E6F51ED79B9DB21F k[21]=F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344
|
||||
c=64A6E14AFD36B46F k[22]=F0E1D2C3B4A5968778695A4B3C2D1E0F001122334455
|
||||
c=80C7D7D45A5479AD k[23]=F0E1D2C3B4A5968778695A4B3C2D1E0F00112233445566
|
||||
c=05044B62FA52D080 k[24]=F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344556677
|
||||
|
||||
chaining mode test data
|
||||
key[16] = 0123456789ABCDEFF0E1D2C3B4A59687
|
||||
iv[8] = FEDCBA9876543210
|
||||
data[29] = "7654321 Now is the time for " (includes trailing '\0')
|
||||
data[29] = 37363534333231204E6F77206973207468652074696D6520666F722000
|
||||
cbc cipher text
|
||||
cipher[32]= 6B77B4D63006DEE605B156E27403979358DEB9E7154616D959F1652BD5FF92CC
|
||||
cfb64 cipher text cipher[29]=
|
||||
E73214A2822139CAF26ECF6D2EB9E76E3DA3DE04D1517200519D57A6C3
|
||||
ofb64 cipher text cipher[29]=
|
||||
E73214A2822139CA62B343CC5B65587310DD908D0C241B2263C2CF80DA
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
namespace fc {
|
||||
|
||||
//Block Structure
|
||||
struct sblock
|
||||
{
|
||||
//Constructors
|
||||
sblock(unsigned int l=0, unsigned int r=0) : m_uil(l), m_uir(r) {}
|
||||
//Copy Constructor
|
||||
sblock(const sblock& roBlock) : m_uil(roBlock.m_uil), m_uir(roBlock.m_uir) {}
|
||||
sblock& operator^=(sblock& b) { m_uil ^= b.m_uil; m_uir ^= b.m_uir; return *this; }
|
||||
unsigned int m_uil, m_uir;
|
||||
};
|
||||
|
||||
class blowfish
|
||||
{
|
||||
public:
|
||||
enum { ECB=0, CBC=1, CFB=2 };
|
||||
|
||||
//Constructor - Initialize the P and S boxes for a given Key
|
||||
blowfish();
|
||||
void start(unsigned char* ucKey, uint64_t n, const sblock& roChain = sblock(0UL,0UL));
|
||||
|
||||
//Resetting the chaining block
|
||||
void reset_chain() { m_oChain = m_oChain0; }
|
||||
|
||||
// encrypt/decrypt Buffer in Place
|
||||
void encrypt(unsigned char* buf, uint64_t n, int iMode=CFB);
|
||||
void decrypt(unsigned char* buf, uint64_t n, int iMode=CFB);
|
||||
|
||||
// encrypt/decrypt from Input Buffer to Output Buffer
|
||||
void encrypt(const unsigned char* in, unsigned char* out, uint64_t n, int iMode=CFB);
|
||||
void decrypt(const unsigned char* in, unsigned char* out, uint64_t n, int iMode=CFB);
|
||||
|
||||
//Private Functions
|
||||
private:
|
||||
unsigned int F(unsigned int ui);
|
||||
void encrypt(sblock&);
|
||||
void decrypt(sblock&);
|
||||
|
||||
private:
|
||||
//The Initialization Vector, by default {0, 0}
|
||||
sblock m_oChain0;
|
||||
sblock m_oChain;
|
||||
unsigned int m_auiP[18];
|
||||
unsigned int m_auiS[4][256];
|
||||
static const unsigned int scm_auiInitP[18];
|
||||
static const unsigned int scm_auiInitS[4][256];
|
||||
};
|
||||
|
||||
|
||||
} // namespace fc
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include <fc/crypto/openssl.hpp>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace fc {
|
||||
|
||||
struct diffie_hellman {
|
||||
diffie_hellman():valid(0),g(5){}
|
||||
bool generate_params( int s, uint8_t g );
|
||||
bool generate_pub_key();
|
||||
bool compute_shared_key( const char* buf, uint32_t s );
|
||||
bool compute_shared_key( const std::vector<char>& pubk);
|
||||
bool validate();
|
||||
|
||||
std::vector<char> p;
|
||||
std::vector<char> pub_key;
|
||||
std::vector<char> priv_key;
|
||||
std::vector<char> shared_key;
|
||||
bool valid;
|
||||
uint8_t g;
|
||||
};
|
||||
|
||||
} // namespace fc
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <fc/crypto/sha256.hpp>
|
||||
#include <fc/vector.hpp>
|
||||
|
||||
namespace fc { namespace equihash {
|
||||
|
||||
struct proof
|
||||
{
|
||||
uint32_t n;
|
||||
uint32_t k;
|
||||
sha256 seed;
|
||||
std::vector< uint32_t > inputs;
|
||||
|
||||
bool is_valid() const;
|
||||
|
||||
static proof hash( uint32_t n, uint32_t k, sha256 seed );
|
||||
};
|
||||
|
||||
} } // fc
|
||||
|
||||
FC_REFLECT( fc::equihash::proof, (n)(k)(seed)(inputs) )
|
||||
@@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <fc/crypto/sha1.hpp>
|
||||
#include <fc/crypto/sha256.hpp>
|
||||
#include <fc/io/raw_fwd.hpp>
|
||||
#include <fc/array.hpp>
|
||||
|
||||
namespace fc {
|
||||
namespace detail { class pke_impl; }
|
||||
|
||||
class private_key;
|
||||
class public_key;
|
||||
void generate_key_pair( public_key&, private_key& );
|
||||
|
||||
typedef std::vector<char> bytes;
|
||||
typedef bytes signature;
|
||||
|
||||
class public_key
|
||||
{
|
||||
public:
|
||||
public_key();
|
||||
explicit public_key( const bytes& d );
|
||||
public_key( const public_key& k );
|
||||
public_key( public_key&& k );
|
||||
~public_key();
|
||||
|
||||
operator bool()const;
|
||||
|
||||
public_key& operator=(const public_key& p );
|
||||
public_key& operator=(public_key&& p );
|
||||
|
||||
bool verify( const sha1& digest, const array<char,2048/8>& sig )const;
|
||||
bool verify( const sha1& digest, const signature& sig )const;
|
||||
bool verify( const sha256& digest, const signature& sig )const;
|
||||
bytes encrypt( const char* data, size_t len )const;
|
||||
bytes encrypt( const bytes& )const;
|
||||
bytes decrypt( const bytes& )const;
|
||||
|
||||
bytes serialize()const;
|
||||
friend void generate_key_pair( public_key&, private_key& );
|
||||
private:
|
||||
std::shared_ptr<detail::pke_impl> my;
|
||||
};
|
||||
|
||||
class private_key
|
||||
{
|
||||
public:
|
||||
private_key();
|
||||
explicit private_key( const bytes& d );
|
||||
private_key( const private_key& k );
|
||||
private_key( private_key&& k );
|
||||
~private_key();
|
||||
|
||||
operator bool()const;
|
||||
|
||||
private_key& operator=(const private_key& p );
|
||||
private_key& operator=(private_key&& p );
|
||||
|
||||
void sign( const sha1& digest, array<char,2048/8>& sig )const;
|
||||
signature sign( const sha1& digest )const;
|
||||
signature sign( const sha256& digest )const;
|
||||
|
||||
bytes decrypt( const char* bytes, size_t len )const;
|
||||
bytes decrypt( const bytes& )const;
|
||||
bytes encrypt( const bytes& )const;
|
||||
|
||||
bytes serialize()const;
|
||||
friend void generate_key_pair( public_key&, private_key& );
|
||||
|
||||
private:
|
||||
std::shared_ptr<detail::pke_impl> my;
|
||||
};
|
||||
bool operator==( const private_key& a, const private_key& b );
|
||||
|
||||
namespace raw
|
||||
{
|
||||
template<typename Stream>
|
||||
void unpack( Stream& s, fc::public_key& pk)
|
||||
{
|
||||
bytes ser;
|
||||
fc::raw::unpack(s,ser);
|
||||
pk = fc::public_key( ser );
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void pack( Stream& s, const fc::public_key& pk)
|
||||
{
|
||||
fc::raw::pack( s, pk.serialize() );
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void unpack( Stream& s, fc::private_key& pk)
|
||||
{
|
||||
bytes ser;
|
||||
fc::raw::unpack(s,ser);
|
||||
pk = fc::private_key( ser );
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void pack( Stream& s, const fc::private_key& pk)
|
||||
{
|
||||
fc::raw::pack( s, pk.serialize() );
|
||||
}
|
||||
}
|
||||
class variant;
|
||||
void to_variant( const public_key& bi, variant& v );
|
||||
void from_variant( const variant& v, public_key& bi );
|
||||
void to_variant( const private_key& bi, variant& v );
|
||||
void from_variant( const variant& v, private_key& bi );
|
||||
|
||||
} // fc
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include <fc/interprocess/file_mapping.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace fc
|
||||
{
|
||||
class path;
|
||||
namespace detail
|
||||
{
|
||||
/**
|
||||
* Base class used to hide common implementation details.
|
||||
*/
|
||||
class mmap_struct_base
|
||||
{
|
||||
public:
|
||||
size_t size()const;
|
||||
void flush();
|
||||
|
||||
protected:
|
||||
void open( const fc::path& file, size_t s, bool create );
|
||||
std::unique_ptr<fc::file_mapping> _file_mapping;
|
||||
std::unique_ptr<fc::mapped_region> _mapped_region;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @class mmap_struct
|
||||
* @brief A struct that has been mapped from a file.
|
||||
*
|
||||
* @note T must be POD
|
||||
*/
|
||||
template<typename T>
|
||||
class mmap_struct : public detail::mmap_struct_base
|
||||
{
|
||||
public:
|
||||
mmap_struct():_mapped_struct(nullptr){}
|
||||
/**
|
||||
* Create the file if it does not exist or is of the wrong size if create is true, then maps
|
||||
* the file to memory.
|
||||
*
|
||||
* @throw an exception if the file does not exist or is the wrong size and create is false
|
||||
*/
|
||||
void open( const fc::path& file, bool create = false )
|
||||
{
|
||||
detail::mmap_struct_base::open( file, sizeof(T), create );
|
||||
_mapped_struct = (T*)_mapped_region->get_address();
|
||||
}
|
||||
|
||||
T* operator->() { return _mapped_struct; }
|
||||
const T* operator->()const { return _mapped_struct; }
|
||||
|
||||
T& operator*() { return *_mapped_struct; }
|
||||
const T& operator*()const { return *_mapped_struct; }
|
||||
|
||||
private:
|
||||
T* _mapped_struct;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/iostreams/categories.hpp>
|
||||
namespace fc {
|
||||
|
||||
namespace bio = boost::iostreams;
|
||||
|
||||
template <typename STREAM, typename Category>
|
||||
struct device_adaptor {
|
||||
STREAM& strm;
|
||||
|
||||
typedef char char_type;
|
||||
typedef Category category;
|
||||
size_t write(const char* data, size_t n) { return strm.write(data, n), n; }
|
||||
size_t read(char* data, size_t n) { return strm.read(data, n), n; }
|
||||
};
|
||||
|
||||
template <typename STREAM>
|
||||
device_adaptor<STREAM, bio::sink_tag> to_sink(STREAM& strm) {
|
||||
return device_adaptor<STREAM, bio::sink_tag>{strm};
|
||||
}
|
||||
|
||||
template <typename STREAM>
|
||||
device_adaptor<STREAM, bio::source_tag> to_source(STREAM& strm) {
|
||||
return device_adaptor<STREAM, bio::source_tag>{strm};
|
||||
}
|
||||
|
||||
template <typename STREAM>
|
||||
device_adaptor<STREAM, bio::source_tag> to_seekable(STREAM& strm) {
|
||||
return device_adaptor<STREAM, bio::seekable_device_tag>{strm};
|
||||
}
|
||||
} // namespace fc
|
||||
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
#include <fc/io/iostream.hpp>
|
||||
#include <fc/exception/exception.hpp>
|
||||
|
||||
namespace fc
|
||||
{
|
||||
/**
|
||||
* Records the size, but discards the data.
|
||||
*/
|
||||
class size_stream : public virtual fc::ostream
|
||||
{
|
||||
public:
|
||||
size_stream( size_t s = 0):_size(s){}
|
||||
|
||||
size_t size()const { return _size; }
|
||||
size_t seek( size_t pos ) { return _size = pos; }
|
||||
|
||||
virtual size_t writesome( const char* /*ignored buf*/, size_t len )
|
||||
{
|
||||
_size += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
virtual void close(){}
|
||||
virtual void flush(){}
|
||||
|
||||
private:
|
||||
size_t _size;
|
||||
};
|
||||
|
||||
|
||||
class iobuffer : public virtual fc::iostream
|
||||
{
|
||||
public:
|
||||
iobuffer( size_t s )
|
||||
:_data(s){}
|
||||
|
||||
size_t size()const { return _data.size(); }
|
||||
size_t pos()const { return _pos; }
|
||||
size_t seek( size_t pos )
|
||||
{
|
||||
return _pos = std::min<size_t>(_data.size(),pos);
|
||||
}
|
||||
|
||||
virtual size_t readsome( char* buf, size_t len )
|
||||
{
|
||||
auto avail = std::min<size_t>( _data.size()-_pos, len );
|
||||
if( avail == 0 ) throw fc::eof_exception();
|
||||
memcpy( buf, _data.data()+_pos, avail );
|
||||
_pos += avail;
|
||||
return avail;
|
||||
}
|
||||
/**
|
||||
* This method may block until at least 1 character is
|
||||
* available.
|
||||
*/
|
||||
char peek()const
|
||||
{
|
||||
if( _pos == _data.size() ) throw fc::eof_exception();
|
||||
return _data[_pos];
|
||||
}
|
||||
|
||||
virtual size_t writesome( const char* buf, size_t len )
|
||||
{
|
||||
auto avail = std::max<size_t>( _data.size(), _pos + len );
|
||||
_data.resize(avail);
|
||||
memcpy( _data.data()+_pos, buf, len );
|
||||
_pos += avail;
|
||||
return avail;
|
||||
}
|
||||
char* data() { return _data.data(); }
|
||||
|
||||
virtual void close(){}
|
||||
virtual void flush(){}
|
||||
|
||||
private:
|
||||
std::vector<char> _data;
|
||||
size_t _pos;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <fc/io/raw.hpp>
|
||||
#include <fc/interprocess/file_mapping.hpp>
|
||||
#include <fc/filesystem.hpp>
|
||||
#include <fc/exception/exception.hpp>
|
||||
|
||||
namespace fc
|
||||
{
|
||||
namespace raw
|
||||
{
|
||||
template<typename T>
|
||||
void unpack_file( const fc::path& filename, T& obj )
|
||||
{
|
||||
try {
|
||||
fc::file_mapping fmap( filename.generic_string().c_str(), fc::read_only);
|
||||
fc::mapped_region mapr( fmap, fc::read_only, 0, fc::file_size(filename) );
|
||||
auto cs = (const char*)mapr.get_address();
|
||||
|
||||
fc::datastream<const char*> ds( cs, mapr.get_size() );
|
||||
fc::raw::unpack(ds,obj);
|
||||
} FC_RETHROW_EXCEPTIONS( info, "unpacking file ${file}", ("file",filename) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <fc/log/zipkin.hpp>
|
||||
#include <optional>
|
||||
|
||||
/// @param TRACE_STR const char* identifier for trace
|
||||
/// @return implementation defined type RAII object that submits trace on exit of scope
|
||||
#define fc_create_trace( TRACE_STR ) \
|
||||
::fc::zipkin_config::is_enabled() ? \
|
||||
::fc::optional_trace{ ::std::optional<::fc::zipkin_trace>( ::std::in_place, (TRACE_STR) ) } \
|
||||
: ::fc::optional_trace{};
|
||||
|
||||
/// @param TRACE_STR const char* identifier for trace
|
||||
/// @param TRACE_ID fc::sha256 id to use
|
||||
/// @return implementation defined type RAII object that submits trace on exit of scope
|
||||
#define fc_create_trace_with_id( TRACE_STR, TRACE_ID ) \
|
||||
::fc::zipkin_config::is_enabled() ? \
|
||||
::fc::optional_trace{ ::std::optional<::fc::zipkin_trace>( ::std::in_place, ::fc::zipkin_span::to_id(TRACE_ID), (TRACE_STR) ) } \
|
||||
: ::fc::optional_trace{};
|
||||
|
||||
/// @param TRACE_VARNAME variable returned from fc_create_trace
|
||||
/// @param SPAN_STR const char* indentifier
|
||||
/// @return implementation defined type RAII object that submits span on exit of scope
|
||||
#define fc_create_span( TRACE_VARNAME, SPAN_STR ) \
|
||||
( (TRACE_VARNAME) && ::fc::zipkin_config::is_enabled() ) ? \
|
||||
(TRACE_VARNAME).opt->create_span( (SPAN_STR) ) \
|
||||
: ::std::optional<::fc::zipkin_span>{};
|
||||
|
||||
/// @param TRACE_TOKEN variable returned from trace.get_token()
|
||||
/// @param SPAN_STR const char* indentifier
|
||||
/// @return implementation defined type RAII object that submits span on exit of scope
|
||||
#define fc_create_span_from_token( TRACE_TOKEN, SPAN_STR ) \
|
||||
( (TRACE_TOKEN) && ::fc::zipkin_config::is_enabled() ) ? \
|
||||
::fc::zipkin_trace::create_span_from_token( (TRACE_TOKEN), (SPAN_STR) ) \
|
||||
: ::std::optional<::fc::zipkin_span>{};
|
||||
|
||||
/// @param SPAN_VARNAME variable returned from fc_create_span
|
||||
/// @param TAG_KEY_STR string key
|
||||
/// @param TAG_VALUE string value
|
||||
#define fc_add_tag( SPAN_VARNAME, TAG_KEY_STR, TAG_VALUE_STR) \
|
||||
FC_MULTILINE_MACRO_BEGIN \
|
||||
if( (SPAN_VARNAME) && ::fc::zipkin_config::is_enabled() ) \
|
||||
(SPAN_VARNAME)->add_tag( (TAG_KEY_STR), (TAG_VALUE_STR) ); \
|
||||
FC_MULTILINE_MACRO_END
|
||||
@@ -0,0 +1,197 @@
|
||||
#pragma once
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace fc {
|
||||
/// Active Object that sends zipkin messages in JSON format
|
||||
/// https://zipkin.io/zipkin-api/
|
||||
///
|
||||
/// Span contains following data
|
||||
/// uint64_t traceId - unique id for trace, all children spans shared same id
|
||||
/// std::string name - logical operation, should have low cardinality
|
||||
/// uint64_t parentId - The parent span id, or absent if root span
|
||||
/// uint64_t id - unique id for this span
|
||||
/// int64_t timestamp - epoch microseconds of start of span
|
||||
/// int64_t duration - microseconds of span
|
||||
///
|
||||
/// Enable zipkin by calling zipkin_config::init() from main thread on startup.
|
||||
/// Use macros defined in trace.hpp.
|
||||
|
||||
class zipkin;
|
||||
|
||||
class sha256;
|
||||
|
||||
class zipkin_config {
|
||||
public:
|
||||
/// Thread safe only if init() called from main thread before spawning of any threads
|
||||
static bool is_enabled() { return get_zipkin_() != nullptr; }
|
||||
|
||||
/// Not thread safe, call from main thread before spawning any threads that might use zipkin.
|
||||
/// @param url the url endpoint of zipkin server. e.g. http://127.0.0.1:9411/api/v2/spans
|
||||
/// @param service_name the service name to include in each zipkin span
|
||||
/// @param timeout_us the timeout in microseconds for each http call (9 consecutive failures and zipkin is disabled)
|
||||
static void init( const std::string& url, const std::string& service_name, uint32_t timeout_us );
|
||||
|
||||
/// Thread safe only if init() called from main thread before spawning of any threads
|
||||
/// @throw assert_exception if called before init()
|
||||
static zipkin& get_zipkin();
|
||||
|
||||
/// Thread safe only if init() called from main thread before spawning of any threads
|
||||
/// @throw assert_exception if called before init()
|
||||
static void shutdown();
|
||||
|
||||
/// Starts with a random id and increments on each call, will not return 0
|
||||
static uint64_t get_next_unique_id();
|
||||
|
||||
private:
|
||||
/// Provide access to initialized zipkin endpoint
|
||||
/// Thread safe as long as init() called correctly
|
||||
/// @return nullptr if init() not called
|
||||
static zipkin* get_zipkin_() { return get().zip.get(); };
|
||||
|
||||
static zipkin_config& get();
|
||||
|
||||
private:
|
||||
std::unique_ptr<zipkin> zip;
|
||||
};
|
||||
|
||||
struct zipkin_span {
|
||||
explicit zipkin_span( std::string name, uint64_t parent_id = 0 )
|
||||
: data( std::move( name ), parent_id ) {}
|
||||
|
||||
explicit zipkin_span( uint64_t id, std::string name, uint64_t parent_id = 0 )
|
||||
: data( id, std::move( name ), parent_id ) {}
|
||||
|
||||
zipkin_span( const zipkin_span& ) = delete;
|
||||
zipkin_span& operator=( const zipkin_span& ) = delete;
|
||||
zipkin_span& operator=( zipkin_span&& ) = delete;
|
||||
|
||||
zipkin_span( zipkin_span&& rhs ) noexcept
|
||||
: data( std::move( rhs.data ) ) {
|
||||
rhs.data.id = 0;
|
||||
}
|
||||
|
||||
~zipkin_span();
|
||||
|
||||
void add_tag( const std::string& key, const std::string& var ) {
|
||||
// zipkin tags are required to be strings
|
||||
data.tags( key, var );
|
||||
}
|
||||
|
||||
void add_tag( const std::string& key, const char* var ) {
|
||||
// zipkin tags are required to be strings
|
||||
data.tags( key, var );
|
||||
}
|
||||
|
||||
void add_tag( const std::string& key, bool v ) {
|
||||
// zipkin tags are required to be strings
|
||||
data.tags( key, v ? "true" : "false" );
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_arithmetic_v<std::remove_reference_t<T>>, void>
|
||||
add_tag( const std::string& key, T&& var ) {
|
||||
data.tags( key, std::to_string( std::forward<T>( var ) ) );
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::enable_if_t<!std::is_arithmetic_v<std::remove_reference_t<T>>, void>
|
||||
add_tag( const std::string& key, T&& var ) {
|
||||
data.tags( key, (std::string) var );
|
||||
}
|
||||
|
||||
struct token {
|
||||
friend zipkin_span;
|
||||
friend struct zipkin_trace;
|
||||
friend struct optional_trace;
|
||||
constexpr explicit operator bool() const noexcept { return id != 0; }
|
||||
private:
|
||||
explicit token( uint64_t id )
|
||||
: id( id ) {}
|
||||
uint64_t id;
|
||||
};
|
||||
|
||||
token get_token() const { return token{data.id}; };
|
||||
|
||||
static uint64_t to_id( const fc::sha256& id );
|
||||
|
||||
template<typename T>
|
||||
static uint64_t to_id( const T& id ) {
|
||||
static_assert( std::is_same_v<decltype( id.data() ), const uint64_t*>, "expected uint64_t" );
|
||||
return id.data()[3];
|
||||
}
|
||||
|
||||
struct span_data {
|
||||
explicit span_data( std::string name, uint64_t parent_id = 0 )
|
||||
: id( zipkin_config::get_next_unique_id() ), parent_id( parent_id ),
|
||||
start( time_point::now() ), name( std::move( name ) ) {}
|
||||
|
||||
explicit span_data( uint64_t id, std::string name, uint64_t parent_id = 0 )
|
||||
: id( id ), parent_id( parent_id ), start( time_point::now() ), name( std::move( name ) ) {}
|
||||
|
||||
span_data( const span_data& ) = delete;
|
||||
span_data& operator=( const span_data& ) = delete;
|
||||
span_data& operator=( span_data&& ) = delete;
|
||||
span_data( span_data&& rhs ) = default;
|
||||
|
||||
uint64_t id;
|
||||
// zipkin traceId and parentId are same (when parent_id set) since only allowing trace with span children.
|
||||
// Not currently supporting spans with children, only trace with children spans.
|
||||
const uint64_t parent_id;
|
||||
const fc::time_point start;
|
||||
fc::time_point stop;
|
||||
std::string name;
|
||||
fc::mutable_variant_object tags;
|
||||
};
|
||||
|
||||
span_data data;
|
||||
};
|
||||
|
||||
struct zipkin_trace : public zipkin_span {
|
||||
using zipkin_span::zipkin_span;
|
||||
|
||||
[[nodiscard]] std::optional<zipkin_span> create_span( std::string name ) const {
|
||||
return zipkin_span{std::move( name ), get_token().id};
|
||||
}
|
||||
|
||||
[[nodiscard]] static std::optional<zipkin_span>
|
||||
create_span_from_token( zipkin_span::token token, std::string name ) {
|
||||
return zipkin_span{std::move( name ), token.id};
|
||||
}
|
||||
};
|
||||
|
||||
struct optional_trace {
|
||||
std::optional<zipkin_trace> opt;
|
||||
|
||||
constexpr explicit operator bool() const noexcept { return opt.has_value(); }
|
||||
|
||||
[[nodiscard]] zipkin_span::token get_token() const {
|
||||
return opt ? opt->get_token() : zipkin_span::token( 0 );
|
||||
}
|
||||
};
|
||||
|
||||
class zipkin {
|
||||
public:
|
||||
zipkin( const std::string& url, const std::string& service_name, uint32_t timeout_us );
|
||||
|
||||
/// finishes logging all queued up spans
|
||||
~zipkin() = default;
|
||||
|
||||
/// Starts with a random id and increments on each call, will not return 0
|
||||
uint64_t get_next_unique_id();
|
||||
|
||||
// finish logging all queued up spans, not restartable
|
||||
void shutdown();
|
||||
|
||||
// Logs zipkin json via http on separate thread
|
||||
void log( zipkin_span::span_data&& span );
|
||||
|
||||
private:
|
||||
class impl;
|
||||
std::unique_ptr<impl> my;
|
||||
};
|
||||
|
||||
} // namespace fc
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include <fc/tuple.hpp>
|
||||
#include <fc/function.hpp>
|
||||
|
||||
namespace fc {
|
||||
template<typename R>
|
||||
std::function<R, fc::tuple<> > make_fused( const std::function<R>& f ) {
|
||||
return [=]( fc::tuple<> ){ return f(); };
|
||||
}
|
||||
template<typename R,typename A>
|
||||
std::function<R(fc::tuple<A>) > make_fused( const std::function<R(A)>& f ) {
|
||||
return [f]( fc::tuple<A> t){ return f(t.a); };
|
||||
}
|
||||
template<typename R,typename A,typename B>
|
||||
std::function<R(fc::tuple<A,B>) > make_fused( const std::function<R(A,B)>& f ) {
|
||||
return [f]( fc::tuple<A,B> t){ return f(t.a,t.b); };
|
||||
}
|
||||
template<typename R,typename A,typename B,typename C>
|
||||
std::function<R(fc::tuple<A,B,C>) > make_fused( const std::function<R(A,B,C)>& f ) {
|
||||
return [f]( fc::tuple<A,B,C> t){ return f(t.a,t.b,t.c); };
|
||||
}
|
||||
template<typename R,typename A,typename B,typename C,typename D>
|
||||
std::function<R(fc::tuple<A,B,C,D>) > make_fused( const std::function<R(A,B,C,D)>& f ) {
|
||||
return [f]( fc::tuple<A,B,C> t){ return f(t.a,t.b,t.c,t.d); };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright defined in LICENSE.txt
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
|
||||
namespace fc {
|
||||
|
||||
//Add the platform's trusted root CAs to the ssl context
|
||||
void add_platform_root_cas_to_context(boost::asio::ssl::context& ctx);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
namespace fc
|
||||
{
|
||||
class noncopyable
|
||||
{
|
||||
public:
|
||||
noncopyable(){}
|
||||
private:
|
||||
noncopyable( const noncopyable& ) = delete;
|
||||
noncopyable& operator=( const noncopyable& ) = delete;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,516 @@
|
||||
#pragma once
|
||||
#include <fc/variant.hpp>
|
||||
#include <fc/api.hpp>
|
||||
#include <fc/any.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <fc/signals.hpp>
|
||||
|
||||
namespace fc {
|
||||
class api_connection;
|
||||
|
||||
namespace detail {
|
||||
template<typename Signature>
|
||||
class callback_functor
|
||||
{
|
||||
public:
|
||||
typedef typename std::function<Signature>::result_type result_type;
|
||||
|
||||
callback_functor( std::weak_ptr< fc::api_connection > con, uint64_t id )
|
||||
:_callback_id(id),_api_connection(con){}
|
||||
|
||||
template<typename... Args>
|
||||
result_type operator()( Args... args )const;
|
||||
|
||||
private:
|
||||
uint64_t _callback_id;
|
||||
std::weak_ptr< fc::api_connection > _api_connection;
|
||||
};
|
||||
|
||||
template<typename R, typename Arg0, typename ... Args>
|
||||
std::function<R(Args...)> bind_first_arg( const std::function<R(Arg0,Args...)>& f, Arg0 a0 )
|
||||
{
|
||||
return [=]( Args... args ) { return f( a0, args... ); };
|
||||
}
|
||||
template<typename R>
|
||||
R call_generic( const std::function<R()>& f, variants::const_iterator a0, variants::const_iterator e )
|
||||
{
|
||||
return f();
|
||||
}
|
||||
|
||||
template<typename R, typename Arg0, typename ... Args>
|
||||
R call_generic( const std::function<R(Arg0,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )
|
||||
{
|
||||
FC_ASSERT( a0 != e );
|
||||
return call_generic<R,Args...>( bind_first_arg<R,Arg0,Args...>( f, a0->as< typename std::decay<Arg0>::type >() ), a0+1, e );
|
||||
}
|
||||
|
||||
template<typename R, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<R(Args...)>& f )
|
||||
{
|
||||
return [=]( const variants& args ) {
|
||||
return variant( call_generic( f, args.begin(), args.end() ) );
|
||||
};
|
||||
}
|
||||
|
||||
template<typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<void(Args...)>& f )
|
||||
{
|
||||
return [=]( const variants& args ) {
|
||||
call_generic( f, args.begin(), args.end() );
|
||||
return variant();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* If api<T> is returned from a remote method, the API is eagerly bound to api<T> of
|
||||
* the correct type in api_visitor::from_variant(). This binding [1] needs a reference
|
||||
* to the api_connection, which is made available to from_variant() as a parameter.
|
||||
*
|
||||
* However, in the case of a remote method which returns api_base which can subsequently
|
||||
* be cast by the caller with as<T>, we need to keep track of the connection because
|
||||
* the binding is done later (when the client code actually calls as<T>).
|
||||
*
|
||||
* [1] The binding actually happens in get_remote_api().
|
||||
*/
|
||||
class any_api : public api_base
|
||||
{
|
||||
public:
|
||||
any_api( api_id_type api_id, const std::shared_ptr<fc::api_connection>& con )
|
||||
: _api_id(api_id), _api_connection(con) {}
|
||||
|
||||
virtual uint64_t get_handle()const override
|
||||
{ return _api_id; }
|
||||
|
||||
virtual api_id_type register_api( api_connection& conn )const override
|
||||
{ FC_ASSERT( false ); return api_id_type(); }
|
||||
|
||||
api_id_type _api_id;
|
||||
std::weak_ptr<fc::api_connection> _api_connection;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class generic_api
|
||||
{
|
||||
public:
|
||||
template<typename Api>
|
||||
generic_api( const Api& a, const std::shared_ptr<fc::api_connection>& c );
|
||||
|
||||
generic_api( const generic_api& cpy ) = delete;
|
||||
|
||||
variant call( const string& name, const variants& args )
|
||||
{
|
||||
auto itr = _by_name.find(name);
|
||||
FC_ASSERT( itr != _by_name.end(), "no method with name '${name}'", ("name",name)("api",_by_name) );
|
||||
return call( itr->second, args );
|
||||
}
|
||||
|
||||
variant call( uint32_t method_id, const variants& args )
|
||||
{
|
||||
FC_ASSERT( method_id < _methods.size() );
|
||||
return _methods[method_id](args);
|
||||
}
|
||||
|
||||
std::weak_ptr< fc::api_connection > get_connection()
|
||||
{
|
||||
return _api_connection;
|
||||
}
|
||||
|
||||
std::vector<std::string> get_method_names()const
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
result.reserve( _by_name.size() );
|
||||
for( auto& m : _by_name ) result.push_back(m.first);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct api_visitor;
|
||||
|
||||
template<typename R, typename Arg0, typename ... Args>
|
||||
std::function<R(Args...)> bind_first_arg( const std::function<R(Arg0,Args...)>& f, Arg0 a0 )const
|
||||
{
|
||||
return [=]( Args... args ) { return f( a0, args... ); };
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
R call_generic( const std::function<R()>& f, variants::const_iterator a0, variants::const_iterator e )const
|
||||
{
|
||||
return f();
|
||||
}
|
||||
|
||||
template<typename R, typename Signature, typename ... Args>
|
||||
R call_generic( const std::function<R(std::function<Signature>,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )
|
||||
{
|
||||
FC_ASSERT( a0 != e, "too few arguments passed to method" );
|
||||
detail::callback_functor<Signature> arg0( get_connection(), a0->as<uint64_t>() );
|
||||
return call_generic<R,Args...>( this->bind_first_arg<R,std::function<Signature>,Args...>( f, std::function<Signature>(arg0) ), a0+1, e );
|
||||
}
|
||||
template<typename R, typename Signature, typename ... Args>
|
||||
R call_generic( const std::function<R(const std::function<Signature>&,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )
|
||||
{
|
||||
FC_ASSERT( a0 != e, "too few arguments passed to method" );
|
||||
detail::callback_functor<Signature> arg0( get_connection(), a0->as<uint64_t>() );
|
||||
return call_generic<R,Args...>( this->bind_first_arg<R,const std::function<Signature>&,Args...>( f, arg0 ), a0+1, e );
|
||||
}
|
||||
|
||||
template<typename R, typename Arg0, typename ... Args>
|
||||
R call_generic( const std::function<R(Arg0,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )
|
||||
{
|
||||
FC_ASSERT( a0 != e, "too few arguments passed to method" );
|
||||
return call_generic<R,Args...>( this->bind_first_arg<R,Arg0,Args...>( f, a0->as< typename std::decay<Arg0>::type >() ), a0+1, e );
|
||||
}
|
||||
|
||||
struct api_visitor
|
||||
{
|
||||
api_visitor( generic_api& a, const std::weak_ptr<fc::api_connection>& s ):_api(a),_api_con(s){ }
|
||||
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<api<Interface,Adaptor>(Args...)>& f )const;
|
||||
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<std::optional<api<Interface,Adaptor>>(Args...)>& f )const;
|
||||
|
||||
template<typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<fc::api_ptr(Args...)>& f )const;
|
||||
|
||||
template<typename R, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<R(Args...)>& f )const;
|
||||
|
||||
template<typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<void(Args...)>& f )const;
|
||||
|
||||
template<typename Result, typename... Args>
|
||||
void operator()( const char* name, std::function<Result(Args...)>& memb )const {
|
||||
_api._methods.emplace_back( to_generic( memb ) );
|
||||
_api._by_name[name] = _api._methods.size() - 1;
|
||||
}
|
||||
|
||||
generic_api& _api;
|
||||
const std::weak_ptr<fc::api_connection>& _api_con;
|
||||
};
|
||||
|
||||
|
||||
std::weak_ptr<fc::api_connection> _api_connection;
|
||||
fc::any _api;
|
||||
std::map< std::string, uint32_t > _by_name;
|
||||
std::vector< std::function<variant(const variants&)> > _methods;
|
||||
}; // class generic_api
|
||||
|
||||
|
||||
|
||||
class api_connection : public std::enable_shared_from_this<fc::api_connection>
|
||||
{
|
||||
public:
|
||||
api_connection(){}
|
||||
virtual ~api_connection(){};
|
||||
|
||||
|
||||
template<typename T>
|
||||
api<T> get_remote_api( api_id_type api_id = 0 )
|
||||
{
|
||||
api<T> result;
|
||||
result->visit( api_visitor( api_id, this->shared_from_this() ) );
|
||||
return result;
|
||||
}
|
||||
|
||||
/** makes calls to the remote server */
|
||||
virtual variant send_call( api_id_type api_id, string method_name, variants args = variants() ) = 0;
|
||||
virtual variant send_callback( uint64_t callback_id, variants args = variants() ) = 0;
|
||||
virtual void send_notice( uint64_t callback_id, variants args = variants() ) = 0;
|
||||
|
||||
variant receive_call( api_id_type api_id, const string& method_name, const variants& args = variants() )const
|
||||
{
|
||||
FC_ASSERT( _local_apis.size() > api_id );
|
||||
return _local_apis[api_id]->call( method_name, args );
|
||||
}
|
||||
variant receive_callback( uint64_t callback_id, const variants& args = variants() )const
|
||||
{
|
||||
FC_ASSERT( _local_callbacks.size() > callback_id );
|
||||
return _local_callbacks[callback_id]( args );
|
||||
}
|
||||
void receive_notice( uint64_t callback_id, const variants& args = variants() )const
|
||||
{
|
||||
FC_ASSERT( _local_callbacks.size() > callback_id );
|
||||
_local_callbacks[callback_id]( args );
|
||||
}
|
||||
|
||||
template<typename Interface>
|
||||
api_id_type register_api( const Interface& a )
|
||||
{
|
||||
auto handle = a.get_handle();
|
||||
auto itr = _handle_to_id.find(handle);
|
||||
if( itr != _handle_to_id.end() ) return itr->second;
|
||||
|
||||
_local_apis.push_back( std::unique_ptr<generic_api>( new generic_api(a, shared_from_this() ) ) );
|
||||
_handle_to_id[handle] = _local_apis.size() - 1;
|
||||
return _local_apis.size() - 1;
|
||||
}
|
||||
|
||||
template<typename Signature>
|
||||
uint64_t register_callback( const std::function<Signature>& cb )
|
||||
{
|
||||
_local_callbacks.push_back( detail::to_generic( cb ) );
|
||||
return _local_callbacks.size() - 1;
|
||||
}
|
||||
|
||||
std::vector<std::string> get_method_names( api_id_type local_api_id = 0 )const { return _local_apis[local_api_id]->get_method_names(); }
|
||||
|
||||
fc::signal<void()> closed;
|
||||
private:
|
||||
std::vector< std::unique_ptr<generic_api> > _local_apis;
|
||||
std::map< uint64_t, api_id_type > _handle_to_id;
|
||||
std::vector< std::function<variant(const variants&)> > _local_callbacks;
|
||||
|
||||
|
||||
struct api_visitor
|
||||
{
|
||||
uint32_t _api_id;
|
||||
std::shared_ptr<fc::api_connection> _connection;
|
||||
|
||||
api_visitor( uint32_t api_id, std::shared_ptr<fc::api_connection> con )
|
||||
:_api_id(api_id),_connection(std::move(con))
|
||||
{
|
||||
}
|
||||
|
||||
api_visitor() = delete;
|
||||
|
||||
template<typename Result>
|
||||
static Result from_variant( const variant& v, Result*, const std::shared_ptr<fc::api_connection>& )
|
||||
{
|
||||
return v.as<Result>();
|
||||
}
|
||||
|
||||
template<typename ResultInterface>
|
||||
static fc::api<ResultInterface> from_variant( const variant& v,
|
||||
fc::api<ResultInterface>* /*used for template deduction*/,
|
||||
const std::shared_ptr<fc::api_connection>& con
|
||||
)
|
||||
{
|
||||
return con->get_remote_api<ResultInterface>( v.as_uint64() );
|
||||
}
|
||||
|
||||
static fc::api_ptr from_variant(
|
||||
const variant& v,
|
||||
fc::api_ptr* /* used for template deduction */,
|
||||
const std::shared_ptr<fc::api_connection>& con
|
||||
)
|
||||
{
|
||||
return fc::api_ptr( new detail::any_api( v.as_uint64(), con ) );
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static fc::variant convert_callbacks( const std::shared_ptr<fc::api_connection>&, const T& v )
|
||||
{
|
||||
return fc::variant(v);
|
||||
}
|
||||
|
||||
template<typename Signature>
|
||||
static fc::variant convert_callbacks( const std::shared_ptr<fc::api_connection>& con, const std::function<Signature>& v )
|
||||
{
|
||||
return con->register_callback( v );
|
||||
}
|
||||
|
||||
template<typename Result, typename... Args>
|
||||
void operator()( const char* name, std::function<Result(Args...)>& memb )const
|
||||
{
|
||||
auto con = _connection;
|
||||
auto api_id = _api_id;
|
||||
memb = [con,api_id,name]( Args... args ) {
|
||||
auto var_result = con->send_call( api_id, name, { convert_callbacks(con,args)...} );
|
||||
return from_variant( var_result, (Result*)nullptr, con );
|
||||
};
|
||||
}
|
||||
template<typename... Args>
|
||||
void operator()( const char* name, std::function<void(Args...)>& memb )const
|
||||
{
|
||||
auto con = _connection;
|
||||
auto api_id = _api_id;
|
||||
memb = [con,api_id,name]( Args... args ) {
|
||||
con->send_call( api_id, name, { convert_callbacks(con,args)...} );
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class local_api_connection : public api_connection
|
||||
{
|
||||
public:
|
||||
/** makes calls to the remote server */
|
||||
virtual variant send_call( api_id_type api_id, string method_name, variants args = variants() ) override
|
||||
{
|
||||
FC_ASSERT( _remote_connection );
|
||||
return _remote_connection->receive_call( api_id, method_name, std::move(args) );
|
||||
}
|
||||
virtual variant send_callback( uint64_t callback_id, variants args = variants() ) override
|
||||
{
|
||||
FC_ASSERT( _remote_connection );
|
||||
return _remote_connection->receive_callback( callback_id, args );
|
||||
}
|
||||
virtual void send_notice( uint64_t callback_id, variants args = variants() ) override
|
||||
{
|
||||
FC_ASSERT( _remote_connection );
|
||||
_remote_connection->receive_notice( callback_id, args );
|
||||
}
|
||||
|
||||
|
||||
void set_remote_connection( const std::shared_ptr<fc::api_connection>& rc )
|
||||
{
|
||||
FC_ASSERT( !_remote_connection );
|
||||
FC_ASSERT( rc != this->shared_from_this() );
|
||||
_remote_connection = rc;
|
||||
}
|
||||
const std::shared_ptr<fc::api_connection>& remote_connection()const { return _remote_connection; }
|
||||
|
||||
std::shared_ptr<fc::api_connection> _remote_connection;
|
||||
};
|
||||
|
||||
template<typename Api>
|
||||
generic_api::generic_api( const Api& a, const std::shared_ptr<fc::api_connection>& c )
|
||||
:_api_connection(c),_api(a)
|
||||
{
|
||||
boost::any_cast<const Api&>(a)->visit( api_visitor( *this, c ) );
|
||||
}
|
||||
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> generic_api::api_visitor::to_generic(
|
||||
const std::function<fc::api<Interface,Adaptor>(Args...)>& f )const
|
||||
{
|
||||
auto api_con = _api_con;
|
||||
auto gapi = &_api;
|
||||
return [=]( const variants& args ) {
|
||||
auto con = api_con.lock();
|
||||
FC_ASSERT( con, "not connected" );
|
||||
|
||||
auto api_result = gapi->call_generic( f, args.begin(), args.end() );
|
||||
return con->register_api( api_result );
|
||||
};
|
||||
}
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> generic_api::api_visitor::to_generic(
|
||||
const std::function<std::optional<fc::api<Interface,Adaptor>>(Args...)>& f )const
|
||||
{
|
||||
auto api_con = _api_con;
|
||||
auto gapi = &_api;
|
||||
return [=]( const variants& args )-> fc::variant {
|
||||
auto con = api_con.lock();
|
||||
FC_ASSERT( con, "not connected" );
|
||||
|
||||
auto api_result = gapi->call_generic( f, args.begin(), args.end() );
|
||||
if( api_result )
|
||||
return con->register_api( *api_result );
|
||||
return variant();
|
||||
};
|
||||
}
|
||||
|
||||
template<typename ... Args>
|
||||
std::function<variant(const fc::variants&)> generic_api::api_visitor::to_generic(
|
||||
const std::function<fc::api_ptr(Args...)>& f )const
|
||||
{
|
||||
auto api_con = _api_con;
|
||||
auto gapi = &_api;
|
||||
return [=]( const variants& args ) -> fc::variant {
|
||||
auto con = api_con.lock();
|
||||
FC_ASSERT( con, "not connected" );
|
||||
|
||||
auto api_result = gapi->call_generic( f, args.begin(), args.end() );
|
||||
if( !api_result )
|
||||
return variant();
|
||||
return api_result->register_api( *con );
|
||||
};
|
||||
}
|
||||
|
||||
template<typename R, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> generic_api::api_visitor::to_generic( const std::function<R(Args...)>& f )const
|
||||
{
|
||||
generic_api* gapi = &_api;
|
||||
return [f,gapi]( const variants& args ) {
|
||||
return variant( gapi->call_generic( f, args.begin(), args.end() ) );
|
||||
};
|
||||
}
|
||||
|
||||
template<typename ... Args>
|
||||
std::function<variant(const fc::variants&)> generic_api::api_visitor::to_generic( const std::function<void(Args...)>& f )const
|
||||
{
|
||||
generic_api* gapi = &_api;
|
||||
return [f,gapi]( const variants& args ) {
|
||||
gapi->call_generic( f, args.begin(), args.end() );
|
||||
return variant();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* It is slightly unclean tight coupling to have this method in the api class.
|
||||
* It breaks encapsulation by requiring an api class method to have a pointer
|
||||
* to an api_connection. The reason this is necessary is we have a goal of being
|
||||
* able to call register_api() on an api<T> through its base class api_base. But
|
||||
* register_api() must know the template parameters!
|
||||
*
|
||||
* The only reasonable way to achieve the goal is to implement register_api()
|
||||
* as a method in api<T> (which obviously knows the template parameter T),
|
||||
* then make the implementation accessible through the base class (by making
|
||||
* it a pure virtual method in the base class which is overridden by the subclass's
|
||||
* implementation).
|
||||
*/
|
||||
template< typename Interface, typename Transform >
|
||||
api_id_type api< Interface, Transform >::register_api( api_connection& conn )const
|
||||
{
|
||||
return conn.register_api( *this );
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
api<T> api_base::as()
|
||||
{
|
||||
// TODO: this method should probably be const (if it is not too hard)
|
||||
api<T>* maybe_requested_type = dynamic_cast< api<T>* >(this);
|
||||
if( maybe_requested_type != nullptr )
|
||||
return *maybe_requested_type;
|
||||
|
||||
detail::any_api* maybe_any = dynamic_cast< detail::any_api* >(this);
|
||||
FC_ASSERT( maybe_any != nullptr );
|
||||
std::shared_ptr< api_connection > api_conn = maybe_any->_api_connection.lock();
|
||||
FC_ASSERT( api_conn );
|
||||
return api_conn->get_remote_api<T>( maybe_any->_api_id );
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template<typename Signature>
|
||||
template<typename... Args>
|
||||
typename callback_functor<Signature>::result_type callback_functor<Signature>::operator()( Args... args )const
|
||||
{
|
||||
std::shared_ptr< fc::api_connection > locked = _api_connection.lock();
|
||||
// TODO: make new exception type for this instead of recycling eof_exception
|
||||
if( !locked )
|
||||
throw fc::eof_exception();
|
||||
locked->send_callback( _callback_id, fc::variants{ args... } ).template as< result_type >();
|
||||
}
|
||||
|
||||
|
||||
template<typename... Args>
|
||||
class callback_functor<void(Args...)>
|
||||
{
|
||||
public:
|
||||
typedef void result_type;
|
||||
|
||||
callback_functor( std::weak_ptr< fc::api_connection > con, uint64_t id )
|
||||
:_callback_id(id),_api_connection(con){}
|
||||
|
||||
void operator()( Args... args )const
|
||||
{
|
||||
std::shared_ptr< fc::api_connection > locked = _api_connection.lock();
|
||||
// TODO: make new exception type for this instead of recycling eof_exception
|
||||
if( !locked )
|
||||
throw fc::eof_exception();
|
||||
locked->send_notice( _callback_id, fc::variants{ args... } );
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t _callback_id;
|
||||
std::weak_ptr< fc::api_connection > _api_connection;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
} // fc
|
||||
@@ -0,0 +1,530 @@
|
||||
#pragma once
|
||||
#include <fc/variant.hpp>
|
||||
#include <fc/io/raw.hpp>
|
||||
#include <fc/api.hpp>
|
||||
#include <fc/any.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <fc/signals.hpp>
|
||||
|
||||
namespace fc {
|
||||
class binary_api_connection;
|
||||
|
||||
namespace detail {
|
||||
template<typename Signature>
|
||||
class callback_functor
|
||||
{
|
||||
public:
|
||||
typedef typename std::function<Signature>::result_type result_type;
|
||||
|
||||
callback_functor( std::weak_ptr< fc::binary_api_connection > con, uint64_t id )
|
||||
:_callback_id(id),_binary_api_connection(con){}
|
||||
|
||||
template<typename... Args>
|
||||
result_type operator()( Args... args )const;
|
||||
|
||||
private:
|
||||
uint64_t _callback_id;
|
||||
std::weak_ptr< fc::binary_api_connection > _binary_api_connection;
|
||||
};
|
||||
|
||||
template<typename R, typename Arg0, typename ... Args>
|
||||
std::function<R(Args...)> bind_first_arg( const std::function<R(Arg0,Args...)>& f, Arg0 a0 )
|
||||
{
|
||||
return [=]( Args... args ) { return f( a0, args... ); };
|
||||
}
|
||||
template<typename R>
|
||||
R call_generic( const std::function<R()>& f, variants::const_iterator a0, variants::const_iterator e )
|
||||
{
|
||||
return f();
|
||||
}
|
||||
|
||||
template<typename R, typename Arg0, typename ... Args>
|
||||
R call_generic( const std::function<R(Arg0,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )
|
||||
{
|
||||
FC_ASSERT( a0 != e );
|
||||
return call_generic<R,Args...>( bind_first_arg<R,Arg0,Args...>( f, a0->as< typename std::decay<Arg0>::type >() ), a0+1, e );
|
||||
}
|
||||
|
||||
template<typename R, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<R(Args...)>& f )
|
||||
{
|
||||
return [=]( const variants& args ) {
|
||||
return variant( call_generic( f, args.begin(), args.end() ) );
|
||||
};
|
||||
}
|
||||
|
||||
template<typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<void(Args...)>& f )
|
||||
{
|
||||
return [=]( const variants& args ) {
|
||||
call_generic( f, args.begin(), args.end() );
|
||||
return variant();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* If api<T> is returned from a remote method, the API is eagerly bound to api<T> of
|
||||
* the correct type in api_visitor::from_variant(). This binding [1] needs a reference
|
||||
* to the binary_api_connection, which is made available to from_variant() as a parameter.
|
||||
*
|
||||
* However, in the case of a remote method which returns api_base which can subsequently
|
||||
* be cast by the caller with as<T>, we need to keep track of the connection because
|
||||
* the binding is done later (when the client code actually calls as<T>).
|
||||
*
|
||||
* [1] The binding actually happens in get_remote_api().
|
||||
*/
|
||||
class any_api : public api_base
|
||||
{
|
||||
public:
|
||||
any_api( api_id_type api_id, const std::shared_ptr<fc::binary_api_connection>& con )
|
||||
: _api_id(api_id), _binary_api_connection(con) {}
|
||||
|
||||
virtual uint64_t get_handle()const override
|
||||
{ return _api_id; }
|
||||
|
||||
virtual api_id_type register_api( binary_api_connection& conn )const override
|
||||
{ FC_ASSERT( false ); return api_id_type(); }
|
||||
|
||||
api_id_type _api_id;
|
||||
std::weak_ptr<fc::binary_api_connection> _binary_api_connection;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class generic_api
|
||||
{
|
||||
public:
|
||||
template<typename Api>
|
||||
generic_api( const Api& a, const std::shared_ptr<fc::binary_api_connection>& c );
|
||||
|
||||
generic_api( const generic_api& cpy ) = delete;
|
||||
|
||||
vector<char> call( const string& name, const vector<char>& args )
|
||||
{
|
||||
auto itr = _by_name.find(name);
|
||||
FC_ASSERT( itr != _by_name.end(), "no method with name '${name}'", ("name",name)("api",_by_name) );
|
||||
return call( itr->second, args );
|
||||
}
|
||||
|
||||
vector<char> call( uint32_t method_id, const vector<char>& args )
|
||||
{
|
||||
FC_ASSERT( method_id < _methods.size() );
|
||||
return _methods[method_id](args);
|
||||
}
|
||||
|
||||
std::weak_ptr< fc::binary_api_connection > get_connection()
|
||||
{
|
||||
return _binary_api_connection;
|
||||
}
|
||||
|
||||
std::vector<std::string> get_method_names()const
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
result.reserve( _by_name.size() );
|
||||
for( auto& m : _by_name ) result.push_back(m.first);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct api_visitor;
|
||||
|
||||
template<typename R, typename Arg0, typename ... Args>
|
||||
std::function<R(Args...)> bind_first_arg( const std::function<R(Arg0,Args...)>& f, Arg0 a0 )const
|
||||
{
|
||||
return [=]( Args... args ) { return f( a0, args... ); };
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
R call_generic( const std::function<R()>& f, datastream<const char*>& ds )const
|
||||
{
|
||||
return f();
|
||||
}
|
||||
|
||||
template<typename R, typename Signature, typename ... Args>
|
||||
R call_generic( const std::function<R(std::function<Signature>,Args...)>& f, datastream<const char*>& ds )
|
||||
{
|
||||
uint64_t callback_id = 0;
|
||||
fc::raw::unpack( ds, callback_id );
|
||||
detail::callback_functor<Signature> arg0( get_connection(), callback_id );
|
||||
return call_generic<R,Args...>( this->bind_first_arg<R,std::function<Signature>,Args...>( f, std::function<Signature>(arg0) ), ds );
|
||||
}
|
||||
template<typename R, typename Signature, typename ... Args>
|
||||
R call_generic( const std::function<R(const std::function<Signature>&,Args...)>& f, fc::datastream<const char*>& ds )
|
||||
{
|
||||
uint64_t callback_id = 0;
|
||||
fc::raw::unpack( ds, callback_id );
|
||||
detail::callback_functor<Signature> arg0( get_connection(), callback_id );
|
||||
return call_generic<R,Args...>( this->bind_first_arg<R,const std::function<Signature>&,Args...>( f, arg0 ), ds );
|
||||
}
|
||||
|
||||
template<typename R, typename Arg0, typename ... Args>
|
||||
R call_generic( const std::function<R(Arg0,Args...)>& f, fc::datastream<const char*>& ds )
|
||||
{
|
||||
std::decay<Arg0>::type a0;
|
||||
fc::raw::unpack( ds, a0 );
|
||||
return call_generic<R,Args...>( this->bind_first_arg<R,Arg0,Args...>( f, a0 ), ds );
|
||||
}
|
||||
|
||||
struct api_visitor
|
||||
{
|
||||
api_visitor( generic_api& a, const std::weak_ptr<fc::binary_api_connection>& s ):api(a),_api_con(s){ }
|
||||
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<api<Interface,Adaptor>(Args...)>& f )const;
|
||||
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<std::optional<api<Interface,Adaptor>>(Args...)>& f )const;
|
||||
|
||||
template<typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<fc::api_ptr(Args...)>& f )const;
|
||||
|
||||
template<typename R, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<R(Args...)>& f )const;
|
||||
|
||||
template<typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<void(Args...)>& f )const;
|
||||
|
||||
template<typename Result, typename... Args>
|
||||
void operator()( const char* name, std::function<Result(Args...)>& memb )const {
|
||||
api._methods.emplace_back( to_generic( memb ) );
|
||||
api._by_name[name] = api._methods.size() - 1;
|
||||
}
|
||||
|
||||
generic_api& api;
|
||||
const std::weak_ptr<fc::binary_api_connection>& _api_con;
|
||||
};
|
||||
|
||||
|
||||
std::weak_ptr<fc::binary_api_connection> _binary_api_connection;
|
||||
fc::any _api;
|
||||
std::map< std::string, uint32_t > _by_name;
|
||||
std::vector< std::function<vector<char>(const vector<char>&)> > _methods;
|
||||
}; // class generic_api
|
||||
|
||||
|
||||
|
||||
class binary_api_connection : public std::enable_shared_from_this<fc::binary_api_connection>
|
||||
{
|
||||
public:
|
||||
typedef std::vector<char> params_type;
|
||||
typedef std::vector<char> result_type;
|
||||
|
||||
binary_api_connection(){}
|
||||
virtual ~binary_api_connection(){};
|
||||
|
||||
|
||||
template<typename T>
|
||||
api<T> get_remote_api( api_id_type api_id = 0 )
|
||||
{
|
||||
api<T> result;
|
||||
result->visit( api_visitor( api_id, this->shared_from_this() ) );
|
||||
return result;
|
||||
}
|
||||
|
||||
/** makes calls to the remote server */
|
||||
virtual result_type send_call( api_id_type api_id, string method_name, params_type args = params_type() ) = 0;
|
||||
virtual result_type send_callback( uint64_t callback_id, params_type args = params_type() ) = 0;
|
||||
virtual void send_notice( uint64_t callback_id, params_type args = params_type() ) = 0;
|
||||
|
||||
result_type receive_call( api_id_type api_id, const string& method_name, const params_type& args = params_type() )const
|
||||
{
|
||||
FC_ASSERT( _local_apis.size() > api_id );
|
||||
return _local_apis[api_id]->call( method_name, args );
|
||||
}
|
||||
result_type receive_callback( uint64_t callback_id, const params_type& args = params_type() )const
|
||||
{
|
||||
FC_ASSERT( _local_callbacks.size() > callback_id );
|
||||
return _local_callbacks[callback_id]( args );
|
||||
}
|
||||
void receive_notice( uint64_t callback_id, const params_type& args = params_type() )const
|
||||
{
|
||||
FC_ASSERT( _local_callbacks.size() > callback_id );
|
||||
_local_callbacks[callback_id]( args );
|
||||
}
|
||||
|
||||
template<typename Interface>
|
||||
api_id_type register_api( const Interface& a )
|
||||
{
|
||||
auto handle = a.get_handle();
|
||||
auto itr = _handle_to_id.find(handle);
|
||||
if( itr != _handle_to_id.end() ) return itr->second;
|
||||
|
||||
_local_apis.push_back( std::unique_ptr<generic_api>( new generic_api(a, shared_from_this() ) ) );
|
||||
_handle_to_id[handle] = _local_apis.size() - 1;
|
||||
return _local_apis.size() - 1;
|
||||
}
|
||||
|
||||
template<typename Signature>
|
||||
uint64_t register_callback( const std::function<Signature>& cb )
|
||||
{
|
||||
_local_callbacks.push_back( detail::to_generic( cb ) );
|
||||
return _local_callbacks.size() - 1;
|
||||
}
|
||||
|
||||
std::vector<std::string> get_method_names( api_id_type local_api_id = 0 )const { return _local_apis[local_api_id]->get_method_names(); }
|
||||
|
||||
fc::signal<void()> closed;
|
||||
private:
|
||||
std::vector< std::unique_ptr<generic_api> > _local_apis;
|
||||
std::map< uint64_t, api_id_type > _handle_to_id;
|
||||
std::vector< std::function<variant(const variants&)> > _local_callbacks;
|
||||
|
||||
|
||||
struct api_visitor
|
||||
{
|
||||
uint32_t _api_id;
|
||||
std::shared_ptr<fc::binary_api_connection> _connection;
|
||||
|
||||
api_visitor( uint32_t api_id, std::shared_ptr<fc::binary_api_connection> con )
|
||||
:_api_id(api_id),_connection(std::move(con))
|
||||
{
|
||||
}
|
||||
|
||||
api_visitor() = delete;
|
||||
|
||||
template<typename Result>
|
||||
static Result from_vector( const vector<char>& v, Result*, const std::shared_ptr<fc::binary_api_connection>& )
|
||||
{
|
||||
return fc::raw::unpack<Result>( v );
|
||||
}
|
||||
|
||||
template<typename ResultInterface>
|
||||
static fc::api<ResultInterface> from_vector( const vector<char>& v,
|
||||
fc::api<ResultInterface>* /*used for template deduction*/,
|
||||
const std::shared_ptr<fc::binary_api_connection>& con
|
||||
)
|
||||
{
|
||||
return con->get_remote_api<ResultInterface>( fc::raw::unpack<uint64_t>( v ) );
|
||||
}
|
||||
|
||||
static fc::api_ptr from_vector(
|
||||
const vector<char>& v,
|
||||
fc::api_ptr* /* used for template deduction */,
|
||||
const std::shared_ptr<fc::binary_api_connection>& con
|
||||
)
|
||||
{
|
||||
return fc::api_ptr( new detail::any_api( fc::raw::unpack<uint64_t>(v), con ) );
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static result_type convert_callbacks( const std::shared_ptr<fc::binary_api_connection>&, const T& v )
|
||||
{
|
||||
return fc::raw::pack(v);
|
||||
}
|
||||
|
||||
template<typename Signature>
|
||||
static result_type convert_callbacks( const std::shared_ptr<fc::binary_api_connection>& con, const std::function<Signature>& v )
|
||||
{
|
||||
return con->register_callback( v );
|
||||
}
|
||||
|
||||
template<typename Result, typename... Args>
|
||||
void operator()( const char* name, std::function<Result(Args...)>& memb )const
|
||||
{
|
||||
auto con = _connection;
|
||||
auto api_id = _api_id;
|
||||
memb = [con,api_id,name]( Args... args ) {
|
||||
auto var_result = con->send_call( api_id, name, { convert_callbacks(con,args)...} );
|
||||
return from_vector( var_result, (Result*)nullptr, con );
|
||||
};
|
||||
}
|
||||
template<typename... Args>
|
||||
void operator()( const char* name, std::function<void(Args...)>& memb )const
|
||||
{
|
||||
auto con = _connection;
|
||||
auto api_id = _api_id;
|
||||
memb = [con,api_id,name]( Args... args ) {
|
||||
con->send_call( api_id, name, { convert_callbacks(con,args)...} );
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class local_binary_api_connection : public binary_api_connection
|
||||
{
|
||||
public:
|
||||
/** makes calls to the remote server */
|
||||
virtual result_type send_call( api_id_type api_id, string method_name, params_type args = params_type() ) override
|
||||
{
|
||||
FC_ASSERT( _remote_connection );
|
||||
return _remote_connection->receive_call( api_id, method_name, std::move(args) );
|
||||
}
|
||||
virtual result_type send_callback( uint64_t callback_id, params_type args = params_type() ) override
|
||||
{
|
||||
FC_ASSERT( _remote_connection );
|
||||
return _remote_connection->receive_callback( callback_id, args );
|
||||
}
|
||||
virtual void send_notice( uint64_t callback_id, params_type args = params_type() ) override
|
||||
{
|
||||
FC_ASSERT( _remote_connection );
|
||||
_remote_connection->receive_notice( callback_id, args );
|
||||
}
|
||||
|
||||
|
||||
void set_remote_connection( const std::shared_ptr<fc::binary_api_connection>& rc )
|
||||
{
|
||||
FC_ASSERT( !_remote_connection );
|
||||
FC_ASSERT( rc != this->shared_from_this() );
|
||||
_remote_connection = rc;
|
||||
}
|
||||
const std::shared_ptr<fc::binary_api_connection>& remote_connection()const { return _remote_connection; }
|
||||
|
||||
std::shared_ptr<fc::binary_api_connection> _remote_connection;
|
||||
};
|
||||
|
||||
template<typename Api>
|
||||
generic_api::generic_api( const Api& a, const std::shared_ptr<fc::binary_api_connection>& c )
|
||||
:_binary_api_connection(c),_api(a)
|
||||
{
|
||||
boost::any_cast<const Api&>(a)->visit( api_visitor( *this, c ) );
|
||||
}
|
||||
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<result_type(const params_type&)> generic_api::api_visitor::to_generic(
|
||||
const std::function<fc::api<Interface,Adaptor>(Args...)>& f )const
|
||||
{
|
||||
auto api_con = _api_con;
|
||||
auto gapi = &api;
|
||||
return [=]( const params_type& args ) {
|
||||
auto con = api_con.lock();
|
||||
FC_ASSERT( con, "not connected" );
|
||||
|
||||
fc::raw::datastream<const char*> ds( args.data(), args.size() );
|
||||
auto api_result = gapi->call_generic( f, args );
|
||||
return con->register_api( api_result );
|
||||
};
|
||||
}
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<result_type(const params_type&)> generic_api::api_visitor::to_generic(
|
||||
const std::function<std::optional<fc::api<Interface,Adaptor>>(Args...)>& f )const
|
||||
{
|
||||
auto api_con = _api_con;
|
||||
auto gapi = &api;
|
||||
return [=]( const params_type& args )-> fc::variant {
|
||||
auto con = api_con.lock();
|
||||
FC_ASSERT( con, "not connected" );
|
||||
|
||||
fc::raw::datastream<const char*> ds( args.data(), args.size() );
|
||||
auto api_result = gapi->call_generic( f, ds );
|
||||
if( api_result )
|
||||
return con->register_api( *api_result );
|
||||
return result_type();
|
||||
};
|
||||
}
|
||||
|
||||
template<typename ... Args>
|
||||
std::function<result_type(const params_type&)> generic_api::api_visitor::to_generic(
|
||||
const std::function<fc::api_ptr(Args...)>& f )const
|
||||
{
|
||||
auto api_con = _api_con;
|
||||
auto gapi = &api;
|
||||
return [=]( const variants& args ) -> fc::variant {
|
||||
auto con = api_con.lock();
|
||||
FC_ASSERT( con, "not connected" );
|
||||
|
||||
fc::raw::datastream<const char*> ds( args.data(), args.size() );
|
||||
auto api_result = gapi->call_generic( f, ds );
|
||||
if( !api_result )
|
||||
return result_type();
|
||||
return api_result->register_api( *con );
|
||||
};
|
||||
}
|
||||
|
||||
template<typename R, typename ... Args>
|
||||
std::function<result_type(const params_type&)> generic_api::api_visitor::to_generic( const std::function<R(Args...)>& f )const
|
||||
{
|
||||
generic_api* gapi = &api;
|
||||
return [f,gapi]( const params_type& args ) {
|
||||
fc::raw::datastream<const char*> ds( args.data(), args.size() );
|
||||
return fc::raw::pack(gapi->call_generic( f, ds ));
|
||||
};
|
||||
}
|
||||
|
||||
template<typename ... Args>
|
||||
std::function<result_type(const params_type&)> generic_api::api_visitor::to_generic( const std::function<void(Args...)>& f )const
|
||||
{
|
||||
generic_api* gapi = &api;
|
||||
return [f,gapi]( const params_type& args ) {
|
||||
fc::raw::datastream<const char*> ds( args.data(), args.size() );
|
||||
gapi->call_generic( f, ds );
|
||||
return result_type();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* It is slightly unclean tight coupling to have this method in the api class.
|
||||
* It breaks encapsulation by requiring an api class method to have a pointer
|
||||
* to an binary_api_connection. The reason this is necessary is we have a goal of being
|
||||
* able to call register_api() on an api<T> through its base class api_base. But
|
||||
* register_api() must know the template parameters!
|
||||
*
|
||||
* The only reasonable way to achieve the goal is to implement register_api()
|
||||
* as a method in api<T> (which obviously knows the template parameter T),
|
||||
* then make the implementation accessible through the base class (by making
|
||||
* it a pure virtual method in the base class which is overridden by the subclass's
|
||||
* implementation).
|
||||
*/
|
||||
template< typename Interface, typename Transform >
|
||||
api_id_type api< Interface, Transform >::register_api( binary_api_connection& conn )const
|
||||
{
|
||||
return conn.register_api( *this );
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
api<T> api_base::as()
|
||||
{
|
||||
// TODO: this method should probably be const (if it is not too hard)
|
||||
api<T>* maybe_requested_type = dynamic_cast< api<T>* >(this);
|
||||
if( maybe_requested_type != nullptr )
|
||||
return *maybe_requested_type;
|
||||
|
||||
detail::any_api* maybe_any = dynamic_cast< detail::any_api* >(this);
|
||||
FC_ASSERT( maybe_any != nullptr );
|
||||
std::shared_ptr< binary_api_connection > api_conn = maybe_any->_binary_api_connection.lock();
|
||||
FC_ASSERT( api_conn );
|
||||
return api_conn->get_remote_api<T>( maybe_any->_api_id );
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template<typename Signature>
|
||||
template<typename... Args>
|
||||
typename callback_functor<Signature>::result_type callback_functor<Signature>::operator()( Args... args )const
|
||||
{
|
||||
std::shared_ptr< fc::binary_api_connection > locked = _binary_api_connection.lock();
|
||||
// TODO: make new exception type for this instead of recycling eof_exception
|
||||
if( !locked )
|
||||
throw fc::eof_exception();
|
||||
|
||||
/// TODO------------->>> pack args...
|
||||
locked->send_callback( _callback_id, fc::raw::pack( args... ) ).template as< result_type >();
|
||||
}
|
||||
|
||||
|
||||
template<typename... Args>
|
||||
class callback_functor<void(Args...)>
|
||||
{
|
||||
public:
|
||||
typedef void result_type;
|
||||
|
||||
callback_functor( std::weak_ptr< fc::binary_api_connection > con, uint64_t id )
|
||||
:_callback_id(id),_binary_api_connection(con){}
|
||||
|
||||
void operator()( Args... args )const
|
||||
{
|
||||
std::shared_ptr< fc::binary_api_connection > locked = _binary_api_connection.lock();
|
||||
// TODO: make new exception type for this instead of recycling eof_exception
|
||||
if( !locked )
|
||||
throw fc::eof_exception();
|
||||
locked->send_notice( _callback_id, fc::variants{ args... } );
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t _callback_id;
|
||||
std::weak_ptr< fc::binary_api_connection > _binary_api_connection;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
} // fc
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include <fc/io/stdio.hpp>
|
||||
#include <fc/io/json.hpp>
|
||||
#include <fc/io/buffered_iostream.hpp>
|
||||
#include <fc/io/sstream.hpp>
|
||||
#include <fc/rpc/api_connection.hpp>
|
||||
#include <fc/thread/thread.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace fc { namespace rpc {
|
||||
|
||||
/**
|
||||
* Provides a simple wrapper for RPC calls to a given interface.
|
||||
*/
|
||||
class cli : public api_connection
|
||||
{
|
||||
public:
|
||||
~cli();
|
||||
|
||||
virtual variant send_call( api_id_type api_id, string method_name, variants args = variants() );
|
||||
virtual variant send_callback( uint64_t callback_id, variants args = variants() );
|
||||
virtual void send_notice( uint64_t callback_id, variants args = variants() );
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void wait();
|
||||
void format_result( const string& method, std::function<string(variant,const variants&)> formatter);
|
||||
|
||||
virtual void getline( const fc::string& prompt, fc::string& line );
|
||||
|
||||
void set_prompt( const string& prompt );
|
||||
|
||||
private:
|
||||
void run();
|
||||
|
||||
std::string _prompt = ">>>";
|
||||
std::map<string,std::function<string(variant,const variants&)> > _result_formatters;
|
||||
fc::future<void> _run_complete;
|
||||
};
|
||||
} }
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include <fc/io/json.hpp>
|
||||
#include <fc/network/http/connection.hpp>
|
||||
#include <fc/network/http/server.hpp>
|
||||
#include <fc/reflect/variant.hpp>
|
||||
#include <fc/rpc/api_connection.hpp>
|
||||
#include <fc/rpc/state.hpp>
|
||||
|
||||
namespace fc { namespace rpc {
|
||||
|
||||
class http_api_connection : public api_connection
|
||||
{
|
||||
public:
|
||||
http_api_connection();
|
||||
~http_api_connection();
|
||||
|
||||
virtual variant send_call(
|
||||
api_id_type api_id,
|
||||
string method_name,
|
||||
variants args = variants() ) override;
|
||||
virtual variant send_callback(
|
||||
uint64_t callback_id,
|
||||
variants args = variants() ) override;
|
||||
virtual void send_notice(
|
||||
uint64_t callback_id,
|
||||
variants args = variants() ) override;
|
||||
|
||||
void on_request(
|
||||
const fc::http::request& req,
|
||||
const fc::http::server::response& resp );
|
||||
|
||||
fc::rpc::state _rpc_state;
|
||||
};
|
||||
|
||||
} } // namespace fc::rpc
|
||||
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
#include <fc/io/buffered_iostream.hpp>
|
||||
#include <fc/variant_object.hpp>
|
||||
#include <fc/thread/future.hpp>
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <functional>
|
||||
|
||||
namespace fc { namespace rpc {
|
||||
|
||||
namespace detail { class variant_connection_impl; }
|
||||
|
||||
/**
|
||||
* @brief Implements JSON-RPC 2.0 over a set of io streams
|
||||
*
|
||||
* Each JSON RPC message is expected to be on its own line, violators
|
||||
* will be prosecuted to the fullest extent of the law.
|
||||
*/
|
||||
class variant_connection
|
||||
{
|
||||
public:
|
||||
typedef std::function<variant(const variants&)> method;
|
||||
typedef std::function<variant(const variant_object&)> named_param_method;
|
||||
|
||||
variant_connection( fc::variant_stream::ptr in, fc::variant_stream::ptr out );
|
||||
~variant_connection();
|
||||
|
||||
/**
|
||||
* Starts processing messages from input
|
||||
*/
|
||||
future<void> exec();
|
||||
|
||||
logger get_logger()const;
|
||||
void set_logger( const logger& l );
|
||||
|
||||
/**
|
||||
* @name server interface
|
||||
*
|
||||
* Adding methods to the interface allows the remote side
|
||||
* to call them.
|
||||
*/
|
||||
///@{
|
||||
void add_method( const fc::string& name, method );
|
||||
void add_named_param_method( const fc::string& name, named_param_method );
|
||||
void remove_method( const fc::string& name );
|
||||
//@}
|
||||
|
||||
/**
|
||||
* @name client interface
|
||||
*/
|
||||
///@{
|
||||
void notice( const fc::string& method );
|
||||
void notice( const fc::string& method, const variants& args );
|
||||
void notice( const fc::string& method, const variant_object& named_args );
|
||||
|
||||
/// args will be handled as named params
|
||||
future<fc::variant> async_call( const fc::string& method,
|
||||
const variant_object& args );
|
||||
|
||||
future<fc::variant> async_call( const fc::string& method, mutable_variant_object args );
|
||||
|
||||
/// Sending in an array of variants will be handled as positional arguments
|
||||
future<fc::variant> async_call( const fc::string& method,
|
||||
const variants& args );
|
||||
|
||||
future<fc::variant> async_call( const fc::string& method );
|
||||
|
||||
future<fc::variant> async_call( const fc::string& method,
|
||||
const fc::variant& a1 );
|
||||
|
||||
future<fc::variant> async_call( const fc::string& method,
|
||||
const fc::variant& a1,
|
||||
const fc::variant& a2 );
|
||||
|
||||
future<fc::variant> async_call( const fc::string& method,
|
||||
const fc::variant& a1,
|
||||
const fc::variant& a2,
|
||||
const fc::variant& a3 );
|
||||
|
||||
template<typename Result>
|
||||
Result call( const fc::string& method,
|
||||
const fc::variant& a1,
|
||||
const fc::variant& a2,
|
||||
const fc::variant& a3,
|
||||
microseconds timeout = microseconds::maximum())
|
||||
{
|
||||
return async_call( method, a1, a2, a3 ).wait(timeout).as<Result>();
|
||||
}
|
||||
|
||||
template<typename Result>
|
||||
Result call( const fc::string& method,
|
||||
const fc::variant& a1,
|
||||
const fc::variant& a2,
|
||||
microseconds timeout = microseconds::maximum())
|
||||
{
|
||||
return async_call( method, a1, a2 ).wait(timeout).as<Result>();
|
||||
}
|
||||
|
||||
template<typename Result>
|
||||
Result call( const fc::string& method,
|
||||
const fc::variant& a1,
|
||||
microseconds timeout = microseconds::maximum())
|
||||
{
|
||||
return async_call( method, a1 ).wait(timeout).as<Result>();
|
||||
}
|
||||
|
||||
template<typename Result>
|
||||
Result call( const fc::string& method,
|
||||
variant_object a1,
|
||||
microseconds timeout = microseconds::maximum())
|
||||
{
|
||||
return async_call( method, fc::move(a1) ).wait(timeout).as<Result>();
|
||||
}
|
||||
template<typename Result>
|
||||
Result call( const fc::string& method,
|
||||
mutable_variant_object a1,
|
||||
microseconds timeout = microseconds::maximum())
|
||||
{
|
||||
return async_call( method, variant_object( fc::move(a1) ) ).wait(timeout).as<Result>();
|
||||
}
|
||||
|
||||
|
||||
template<typename Result>
|
||||
Result call( const fc::string& method, microseconds timeout = microseconds::maximum() )
|
||||
{
|
||||
return async_call( method ).wait(timeout).as<Result>();
|
||||
}
|
||||
|
||||
/// Sending in a variant_object will be issued as named parameters
|
||||
variant call( const fc::string& method, const variant_object& named_args );
|
||||
///@}
|
||||
|
||||
private:
|
||||
std::unique_ptr<detail::variant_connection_impl> my;
|
||||
};
|
||||
typedef std::shared_ptr<variant_connection> variant_connection_ptr;
|
||||
|
||||
}} // fc::rpc
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
namespace fc
|
||||
{
|
||||
|
||||
/**
|
||||
* Thread-safe, circular buffer for passing variants
|
||||
* between threads.
|
||||
*/
|
||||
class variant_stream
|
||||
{
|
||||
public:
|
||||
variant_stream( size_t s );
|
||||
~variant_stream();
|
||||
|
||||
/** producer api */
|
||||
int64_t free(); // number of spaces available
|
||||
int64_t claim( int64_t num );
|
||||
int64_t publish( int64_t pos );
|
||||
int64_t wait_free(); // wait for free space
|
||||
|
||||
// producer/consumer api
|
||||
variant& get( int64_t pos );
|
||||
|
||||
/** consumer api */
|
||||
int64_t begin(); // returns the first index ready to be read
|
||||
int64_t end(); // returns the first index that cannot be read
|
||||
int64_t wait(); // wait for variants to be posted
|
||||
|
||||
private:
|
||||
std::vector<fc::variant> _variants;
|
||||
uint64_t _read_pos;
|
||||
uint64_t _write_pos;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include <fc/rpc/api_connection.hpp>
|
||||
#include <fc/rpc/state.hpp>
|
||||
#include <fc/network/http/websocket.hpp>
|
||||
#include <fc/io/json.hpp>
|
||||
#include <fc/reflect/variant.hpp>
|
||||
|
||||
namespace fc { namespace rpc {
|
||||
|
||||
class websocket_api_connection : public api_connection
|
||||
{
|
||||
public:
|
||||
websocket_api_connection( fc::http::websocket_connection& c );
|
||||
~websocket_api_connection();
|
||||
|
||||
virtual variant send_call(
|
||||
api_id_type api_id,
|
||||
string method_name,
|
||||
variants args = variants() ) override;
|
||||
virtual variant send_callback(
|
||||
uint64_t callback_id,
|
||||
variants args = variants() ) override;
|
||||
virtual void send_notice(
|
||||
uint64_t callback_id,
|
||||
variants args = variants() ) override;
|
||||
|
||||
protected:
|
||||
std::string on_message(
|
||||
const std::string& message,
|
||||
bool send_message = true );
|
||||
|
||||
fc::http::websocket_connection& _connection;
|
||||
fc::rpc::state _rpc_state;
|
||||
};
|
||||
|
||||
} } // namespace fc::rpc
|
||||
@@ -0,0 +1,136 @@
|
||||
#pragma once
|
||||
#include <fc/utility.hpp>
|
||||
#include <boost/preprocessor/repeat.hpp>
|
||||
#include <boost/preprocessor/repeat_from_to.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_params.hpp>
|
||||
#include <boost/preprocessor/repetition/enum.hpp>
|
||||
#include <boost/preprocessor/facilities/empty.hpp>
|
||||
#include <boost/preprocessor/repetition/enum_params_with_a_default.hpp>
|
||||
|
||||
namespace fc {
|
||||
|
||||
/**
|
||||
* Provides a fast-compiling tuple that doesn't use fancy meta-programming
|
||||
* techniques. It is limited to 4 parameters which is sufficient for most
|
||||
* methods argument lists which is the primary use case for this tuple. Methods
|
||||
* that require more than 4 parameters are probably better served by defining
|
||||
* a struct.
|
||||
*
|
||||
* The members of the tuple are easily visited with a simple visitor functor
|
||||
* of the form:
|
||||
* @code
|
||||
* struct visitor {
|
||||
* template<typename MemberType>
|
||||
* void operator()( MemberType& m );
|
||||
*
|
||||
* template<typename MemberType>
|
||||
* void operator()( const MemberType& m );
|
||||
* };
|
||||
* @endcode
|
||||
template<typename A=void, typename B=void,typename C=void, typename D=void>
|
||||
struct tuple {
|
||||
tuple(){}
|
||||
enum size_enum { size = 4 };
|
||||
|
||||
template<typename AA, typename BB, typename CC, typename DD>
|
||||
tuple( AA&& aa, BB&& bb, CC&& cc, DD&& dd )
|
||||
:a( fc::forward<AA>(aa) ),
|
||||
b( fc::forward<BB>(bb) ),
|
||||
c( fc::forward<CC>(cc) ),
|
||||
d( fc::forward<DD>(dd) )
|
||||
{}
|
||||
|
||||
template<typename V>
|
||||
void visit( V&& v ) { v(a); v(b); v(c); v(d); }
|
||||
template<typename V>
|
||||
void visit( V&& v )const { v(a); v(b); v(c); v(d); }
|
||||
|
||||
A a;
|
||||
B b;
|
||||
C c;
|
||||
D d;
|
||||
};
|
||||
*/
|
||||
template<BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(9, typename A, void)> struct tuple{};
|
||||
|
||||
template<>
|
||||
struct tuple<> {
|
||||
enum size_enum { size = 0 };
|
||||
template<typename V>
|
||||
void visit( V&& v)const{};
|
||||
};
|
||||
template<typename Functor>
|
||||
auto call_fused( Functor f, const tuple<>& t ) -> decltype( f( ) ) {
|
||||
return f();
|
||||
}
|
||||
|
||||
inline tuple<> make_tuple(){ return tuple<>(); }
|
||||
|
||||
template<typename T>
|
||||
struct is_tuple {
|
||||
typedef fc::false_type type;
|
||||
};
|
||||
|
||||
#define RREF_PARAMS(z,n,data) BOOST_PP_CAT(AA,n)&& BOOST_PP_CAT(p,n)
|
||||
#define ILIST_PARAMS(z,n,data) BOOST_PP_CAT(a,n)( fc::forward<BOOST_PP_CAT(AA,n)>( BOOST_PP_CAT(p,n) ) )
|
||||
#define ILIST_PARAMS_COPY(z,n,data) BOOST_PP_CAT(a,n)( t.BOOST_PP_CAT(a,n) )
|
||||
#define VISIT_PARAMS(z,n,data) v(BOOST_PP_CAT(a,n));
|
||||
#define LIST_MEMBERS_ON(z,n,data) data.BOOST_PP_CAT(a,n)
|
||||
#define DEDUCE_MEMBERS(z,n,data) typename fc::deduce<BOOST_PP_CAT(AA,n)>::type
|
||||
#define FORWARD_PARAMS(z,n,data) fc::forward<BOOST_PP_CAT(AA,n)>(BOOST_PP_CAT(p,n))
|
||||
#define MEM_PARAMS(z,n,data) BOOST_PP_CAT(A,n) BOOST_PP_CAT(a,n);
|
||||
#define TUPLE(z,n,unused) \
|
||||
template<BOOST_PP_ENUM_PARAMS( n, typename A)> \
|
||||
struct tuple<BOOST_PP_ENUM_PARAMS(n,A)> { \
|
||||
enum size_enum { size = n }; \
|
||||
template<BOOST_PP_ENUM_PARAMS( n, typename AA)> \
|
||||
explicit tuple( BOOST_PP_ENUM(n, RREF_PARAMS, unused ) )BOOST_PP_IF(n,:,BOOST_PP_EMPTY())BOOST_PP_ENUM( n, ILIST_PARAMS,unused){} \
|
||||
tuple( const tuple& t )BOOST_PP_IF(n,:,BOOST_PP_EMPTY())BOOST_PP_ENUM( n, ILIST_PARAMS_COPY,unused){} \
|
||||
tuple( tuple&& t )BOOST_PP_IF(n,:,BOOST_PP_EMPTY())BOOST_PP_ENUM( n, ILIST_PARAMS_COPY,unused){} \
|
||||
tuple(){}\
|
||||
template<typename V>\
|
||||
void visit( V&& v ) { BOOST_PP_REPEAT(n,VISIT_PARAMS,a) }\
|
||||
template<typename V>\
|
||||
void visit( V&& v )const { BOOST_PP_REPEAT(n,VISIT_PARAMS,a) }\
|
||||
BOOST_PP_REPEAT(n,MEM_PARAMS,a) \
|
||||
}; \
|
||||
template<BOOST_PP_ENUM_PARAMS( n, typename AA)> \
|
||||
tuple<BOOST_PP_ENUM_PARAMS(n,AA)> make_tuple( BOOST_PP_ENUM( n, RREF_PARAMS, unused) ) { \
|
||||
return tuple<BOOST_PP_ENUM_PARAMS(n,AA)>( BOOST_PP_ENUM( n, FORWARD_PARAMS,unused ) ); \
|
||||
} \
|
||||
template<typename Functor, BOOST_PP_ENUM_PARAMS(n,typename AA)> \
|
||||
auto call_fused( Functor f, tuple<BOOST_PP_ENUM_PARAMS(n,AA)>& t ) \
|
||||
-> decltype( f( BOOST_PP_ENUM( n, LIST_MEMBERS_ON, t) ) ) { \
|
||||
return f( BOOST_PP_ENUM( n, LIST_MEMBERS_ON, t) ); \
|
||||
} \
|
||||
template<typename Functor, BOOST_PP_ENUM_PARAMS(n,typename AA)> \
|
||||
auto call_fused( Functor f, const tuple<BOOST_PP_ENUM_PARAMS(n,AA)>& t ) \
|
||||
-> decltype( f( BOOST_PP_ENUM( n, LIST_MEMBERS_ON, t) ) ) { \
|
||||
return f( BOOST_PP_ENUM( n, LIST_MEMBERS_ON, t) ); \
|
||||
} \
|
||||
template<BOOST_PP_ENUM_PARAMS( n, typename AA)> \
|
||||
struct is_tuple<fc::tuple<BOOST_PP_ENUM_PARAMS(n,AA)> > { \
|
||||
typedef fc::true_type type; \
|
||||
}; \
|
||||
template<BOOST_PP_ENUM_PARAMS( n, typename AA)> \
|
||||
struct deduce<fc::tuple<BOOST_PP_ENUM_PARAMS(n,AA)> > { \
|
||||
typedef fc::tuple<BOOST_PP_ENUM( n, DEDUCE_MEMBERS,unused)> type; \
|
||||
};
|
||||
|
||||
BOOST_PP_REPEAT_FROM_TO( 1, 5, TUPLE, unused )
|
||||
|
||||
|
||||
#undef FORWARD_PARAMS
|
||||
#undef DEDUCE_MEMBERS
|
||||
#undef RREF_PARAMS
|
||||
#undef LIST_MEMBERS_ON
|
||||
#undef ILIST_PARAMS
|
||||
#undef ILIST_PARAMS_COPY
|
||||
#undef VISIT_PARAMS
|
||||
#undef MEM_PARAMS
|
||||
#undef TUPLE
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef _FC_VECTOR_FWD_HPP_
|
||||
#define _FC_VECTOR_FWD_HPP_
|
||||
#if 0
|
||||
#include <fc/vector.hpp>
|
||||
#else
|
||||
namespace fc {
|
||||
template<typename T> class vector;
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // _FC_VECTOR_FWD_HPP_
|
||||
Submodule libraries/libfc/libraries/bn256 deleted from 9ed6fd84fd
Submodule
+1
Submodule libraries/libfc/libraries/ff added at 483970922c
@@ -29,9 +29,9 @@ target_include_directories(secp256k1
|
||||
target_link_libraries(secp256k1 PRIVATE secp256k1-internal)
|
||||
|
||||
install( TARGETS secp256k1
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} COMPONENT dev EXCLUDE_FROM_ALL
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR} COMPONENT dev EXCLUDE_FROM_ALL
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR} COMPONENT dev EXCLUDE_FROM_ALL
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} ${INSTALL_COMPONENT_ARGS}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR} ${INSTALL_COMPONENT_ARGS}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR} ${INSTALL_COMPONENT_ARGS}
|
||||
)
|
||||
|
||||
add_executable(secp256k1-bench secp256k1/src/bench.c)
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
#include <fc/exception/exception.hpp>
|
||||
namespace fc {
|
||||
|
||||
typedef const char* const_char_ptr;
|
||||
/* Our compression codebook, used for compression */
|
||||
static const_char_ptr Smaz_cb[241] = {
|
||||
"\002s,\266", "\003had\232\002leW", "\003on \216", "", "\001yS",
|
||||
"\002ma\255\002li\227", "\003or \260", "", "\002ll\230\003s t\277",
|
||||
"\004fromg\002mel", "", "\003its\332", "\001z\333", "\003ingF", "\001>\336",
|
||||
"\001 \000\003 (\002nc\344", "\002nd=\003 on\312",
|
||||
"\002ne\213\003hat\276\003re q", "", "\002ngT\003herz\004have\306\003s o\225",
|
||||
"", "\003ionk\003s a\254\002ly\352", "\003hisL\003 inN\003 be\252", "",
|
||||
"\003 fo\325\003 of \003 ha\311", "", "\002of\005",
|
||||
"\003 co\241\002no\267\003 ma\370", "", "", "\003 cl\356\003enta\003 an7",
|
||||
"\002ns\300\001\"e", "\003n t\217\002ntP\003s, \205",
|
||||
"\002pe\320\003 we\351\002om\223", "\002on\037", "", "\002y G", "\003 wa\271",
|
||||
"\003 re\321\002or*", "", "\002=\"\251\002ot\337", "\003forD\002ou[",
|
||||
"\003 toR", "\003 th\r", "\003 it\366",
|
||||
"\003but\261\002ra\202\003 wi\363\002</\361", "\003 wh\237", "\002 4",
|
||||
"\003nd ?", "\002re!", "", "\003ng c", "",
|
||||
"\003ly \307\003ass\323\001a\004\002rir", "", "", "", "\002se_", "\003of \"",
|
||||
"\003div\364\002ros\003ere\240", "", "\002ta\310\001bZ\002si\324", "",
|
||||
"\003and\a\002rs\335", "\002rt\362", "\002teE", "\003ati\316", "\002so\263",
|
||||
"\002th\021", "\002tiJ\001c\034\003allp", "\003ate\345", "\002ss\246",
|
||||
"\002stM", "", "\002><\346", "\002to\024", "\003arew", "\001d\030",
|
||||
"\002tr\303", "", "\001\n1\003 a \222", "\003f tv\002veo", "\002un\340", "",
|
||||
"\003e o\242", "\002a \243\002wa\326\001e\002", "\002ur\226\003e a\274",
|
||||
"\002us\244\003\n\r\n\247", "\002ut\304\003e c\373", "\002we\221", "", "",
|
||||
"\002wh\302", "\001f,", "", "", "", "\003d t\206", "", "", "\003th \343",
|
||||
"\001g;", "", "", "\001\r9\003e s\265", "\003e t\234", "", "\003to Y",
|
||||
"\003e\r\n\236", "\002d \036\001h\022", "", "\001,Q", "\002 a\031", "\002 b^",
|
||||
"\002\r\n\025\002 cI", "\002 d\245", "\002 e\253", "\002 fh\001i\b\002e \v",
|
||||
"", "\002 hU\001-\314", "\002 i8", "", "", "\002 l\315", "\002 m{",
|
||||
"\002f :\002 n\354", "\002 o\035", "\002 p}\001.n\003\r\n\r\250", "",
|
||||
"\002 r\275", "\002 s>", "\002 t\016", "", "\002g \235\005which+\003whi\367",
|
||||
"\002 w5", "\001/\305", "\003as \214", "\003at \207", "", "\003who\331", "",
|
||||
"\001l\026\002h \212", "", "\002, $", "", "\004withV", "", "", "", "\001m-", "",
|
||||
"", "\002ac\357", "\002ad\350", "\003TheH", "", "", "\004this\233\001n\t",
|
||||
"", "\002. y", "", "\002alX\003e, \365", "\003tio\215\002be\\",
|
||||
"\002an\032\003ver\347", "", "\004that0\003tha\313\001o\006", "\003was2",
|
||||
"\002arO", "\002as.", "\002at'\003the\001\004they\200\005there\322\005theird",
|
||||
"\002ce\210", "\004were]", "", "\002ch\231\002l \264\001p<", "", "",
|
||||
"\003one\256", "", "\003he \023\002dej", "\003ter\270", "\002cou", "",
|
||||
"\002by\177\002di\201\002eax", "", "\002ec\327", "\002edB", "\002ee\353", "",
|
||||
"", "\001r\f\002n )", "", "", "", "\002el\262", "", "\003in i\002en3", "",
|
||||
"\002o `\001s\n", "", "\002er\033", "\003is t\002es6", "", "\002ge\371",
|
||||
"\004.com\375", "\002fo\334\003our\330", "\003ch \301\001t\003", "\002hab", "",
|
||||
"\003men\374", "", "\002he\020", "", "", "\001u&", "\002hif", "",
|
||||
"\003not\204\002ic\203", "\003ed @\002id\355", "", "", "\002ho\273",
|
||||
"\002r K\001vm", "", "", "", "\003t t\257\002il\360", "\002im\342",
|
||||
"\003en \317\002in\017", "\002io\220", "\002s \027\001wA", "", "\003er |",
|
||||
"\003es ~\002is%", "\002it/", "", "\002iv\272", "",
|
||||
"\002t #\ahttp://C\001x\372", "\002la\211", "\001<\341", "\003, a\224"
|
||||
};
|
||||
|
||||
|
||||
/* Reverse compression codebook, used for decompression */
|
||||
static const_char_ptr Smaz_rcb[254] = {
|
||||
" ", "the", "e", "t", "a", "of", "o", "and", "i", "n", "s", "e ", "r", " th",
|
||||
" t", "in", "he", "th", "h", "he ", "to", "\r\n", "l", "s ", "d", " a", "an",
|
||||
"er", "c", " o", "d ", "on", " of", "re", "of ", "t ", ", ", "is", "u", "at",
|
||||
" ", "n ", "or", "which", "f", "m", "as", "it", "that", "\n", "was", "en",
|
||||
" ", " w", "es", " an", " i", "\r", "f ", "g", "p", "nd", " s", "nd ", "ed ",
|
||||
"w", "ed", "http://", "for", "te", "ing", "y ", "The", " c", "ti", "r ", "his",
|
||||
"st", " in", "ar", "nt", ",", " to", "y", "ng", " h", "with", "le", "al", "to ",
|
||||
"b", "ou", "be", "were", " b", "se", "o ", "ent", "ha", "ng ", "their", "\"",
|
||||
"hi", "from", " f", "in ", "de", "ion", "me", "v", ".", "ve", "all", "re ",
|
||||
"ri", "ro", "is ", "co", "f t", "are", "ea", ". ", "her", " m", "er ", " p",
|
||||
"es ", "by", "they", "di", "ra", "ic", "not", "s, ", "d t", "at ", "ce", "la",
|
||||
"h ", "ne", "as ", "tio", "on ", "n t", "io", "we", " a ", "om", ", a", "s o",
|
||||
"ur", "li", "ll", "ch", "had", "this", "e t", "g ", "e\r\n", " wh", "ere",
|
||||
" co", "e o", "a ", "us", " d", "ss", "\n\r\n", "\r\n\r", "=\"", " be", " e",
|
||||
"s a", "ma", "one", "t t", "or ", "but", "el", "so", "l ", "e s", "s,", "no",
|
||||
"ter", " wa", "iv", "ho", "e a", " r", "hat", "s t", "ns", "ch ", "wh", "tr",
|
||||
"ut", "/", "have", "ly ", "ta", " ha", " on", "tha", "-", " l", "ati", "en ",
|
||||
"pe", " re", "there", "ass", "si", " fo", "wa", "ec", "our", "who", "its", "z",
|
||||
"fo", "rs", ">", "ot", "un", "<", "im", "th ", "nc", "ate", "><", "ver", "ad",
|
||||
" we", "ly", "ee", " n", "id", " cl", "ac", "il", "</", "rt", " wi", "div",
|
||||
"e, ", " it", "whi", " ma", "ge", "x", "e c", "men", ".com"
|
||||
};
|
||||
|
||||
uint32_t smaz_compress(const char *in, int inlen, char *out, int outlen) {
|
||||
unsigned int h1,h2,h3=0;
|
||||
int verblen = 0, _outlen = outlen;
|
||||
char verb[256], *_out = out;
|
||||
|
||||
while(inlen) {
|
||||
int j = 7, needed;
|
||||
char *flush = NULL;
|
||||
const char *slot;
|
||||
|
||||
h1 = h2 = in[0]<<3;
|
||||
if (inlen > 1) h2 += in[1];
|
||||
if (inlen > 2) h3 = h2^in[2];
|
||||
if (j > inlen) j = inlen;
|
||||
|
||||
/* Try to lookup substrings into the hash table, starting from the
|
||||
* longer to the shorter substrings */
|
||||
for (; j > 0; j--) {
|
||||
switch(j) {
|
||||
case 1: slot = Smaz_cb[h1%241]; break;
|
||||
case 2: slot = Smaz_cb[h2%241]; break;
|
||||
default: slot = Smaz_cb[h3%241]; break;
|
||||
}
|
||||
while(slot[0]) {
|
||||
if (slot[0] == j && memcmp(slot+1,in,j) == 0) {
|
||||
/* Match found in the hash table,
|
||||
* prepare a verbatim bytes flush if needed */
|
||||
if (verblen) {
|
||||
needed = (verblen == 1) ? 2 : 2+verblen;
|
||||
flush = out;
|
||||
out += needed;
|
||||
outlen -= needed;
|
||||
}
|
||||
/* Emit the byte */
|
||||
if (outlen <= 0) return _outlen+1;
|
||||
out[0] = slot[slot[0]+1];
|
||||
out++;
|
||||
outlen--;
|
||||
inlen -= j;
|
||||
in += j;
|
||||
goto out;
|
||||
} else {
|
||||
slot += slot[0]+2;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Match not found - add the byte to the verbatim buffer */
|
||||
verb[verblen] = in[0];
|
||||
verblen++;
|
||||
inlen--;
|
||||
in++;
|
||||
out:
|
||||
/* Prepare a flush if we reached the flush length limit, and there
|
||||
* is not already a pending flush operation. */
|
||||
if (!flush && (verblen == 256 || (verblen > 0 && inlen == 0))) {
|
||||
needed = (verblen == 1) ? 2 : 2+verblen;
|
||||
flush = out;
|
||||
out += needed;
|
||||
outlen -= needed;
|
||||
if (outlen < 0) return _outlen+1;
|
||||
}
|
||||
/* Perform a verbatim flush if needed */
|
||||
if (flush) {
|
||||
if (verblen == 1) {
|
||||
flush[0] = (signed char)254;
|
||||
flush[1] = verb[0];
|
||||
} else {
|
||||
flush[0] = (signed char)255;
|
||||
flush[1] = (signed char)(verblen-1);
|
||||
memcpy(flush+2,verb,verblen);
|
||||
}
|
||||
flush = NULL;
|
||||
verblen = 0;
|
||||
}
|
||||
}
|
||||
return out-_out;
|
||||
}
|
||||
|
||||
void smaz_decompress(const char *in, uint32_t inlen, std::stringstream& ss ) {
|
||||
const unsigned char *c = (const unsigned char*) in;
|
||||
// char *_out = out;
|
||||
// int _outlen = outlen;
|
||||
|
||||
while(inlen) {
|
||||
if (*c == 254) {
|
||||
/* Verbatim byte */
|
||||
//if (outlen < 1) return _outlen+1;
|
||||
//*out = *(c+1);
|
||||
ss.put( *(c+1) );
|
||||
//out++;
|
||||
//outlen--;
|
||||
c += 2;
|
||||
inlen -= 2;
|
||||
} else if (*c == 255) {
|
||||
/* Verbatim string */
|
||||
int len = (*(c+1))+1;
|
||||
//if (outlen < len) return _outlen+1;
|
||||
ss.write( (const char*)(c+2),len );
|
||||
|
||||
//memcpy(out,c+2,len);
|
||||
//out += len;
|
||||
//outlen -= len;
|
||||
c += 2+len;
|
||||
inlen -= 2+len;
|
||||
} else {
|
||||
/* Codebook entry */
|
||||
const char *s = Smaz_rcb[*c];
|
||||
int len = strlen(s);
|
||||
|
||||
//if (outlen < len) return _outlen+1;
|
||||
//memcpy(out,s,len);
|
||||
ss.write( s, len );
|
||||
//out += len;
|
||||
//outlen -= len;
|
||||
c++;
|
||||
inlen--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string smaz_compress( const std::string& in )
|
||||
{
|
||||
std::string out;
|
||||
out.resize(in.size());
|
||||
auto out_len = smaz_compress( in.c_str(), in.size(), &out[0], out.size() );
|
||||
FC_ASSERT( out_len <= out.size() );
|
||||
out.resize(out_len);
|
||||
return out;
|
||||
}
|
||||
std::string smaz_decompress( const std::string& compressed )
|
||||
{
|
||||
std::stringstream ss;
|
||||
smaz_decompress( compressed.c_str(), compressed.length(), ss );
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // namespace fc
|
||||
@@ -0,0 +1,230 @@
|
||||
#include <gmp.h>
|
||||
#include <fc/crypto/alt_bn128.hpp>
|
||||
|
||||
#include <libff/algebra/curves/alt_bn128/alt_bn128_g1.hpp>
|
||||
#include <libff/algebra/curves/alt_bn128/alt_bn128_g2.hpp>
|
||||
#include <libff/algebra/curves/alt_bn128/alt_bn128_pairing.hpp>
|
||||
#include <libff/algebra/curves/alt_bn128/alt_bn128_pp.hpp>
|
||||
|
||||
#include <libff/common/profiling.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace fc {
|
||||
|
||||
using Scalar = libff::bigint<libff::alt_bn128_q_limbs>;
|
||||
|
||||
void initLibSnark() noexcept {
|
||||
static bool s_initialized = []() noexcept {
|
||||
libff::inhibit_profiling_info = true;
|
||||
libff::inhibit_profiling_counters = true;
|
||||
libff::alt_bn128_pp::init_public_params();
|
||||
return true;
|
||||
}();
|
||||
(void)s_initialized;
|
||||
}
|
||||
|
||||
Scalar to_scalar(const bytes& be) noexcept {
|
||||
mpz_t m;
|
||||
mpz_init(m);
|
||||
mpz_import(m, be.size(), /*order=*/1, /*size=*/1, /*endian=*/0, /*nails=*/0, &be[0]);
|
||||
Scalar out{m};
|
||||
mpz_clear(m);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Notation warning: Yellow Paper's p is the same libff's q.
|
||||
// Returns x < p (YP notation).
|
||||
static bool valid_element_of_fp(const Scalar& x) noexcept {
|
||||
return mpn_cmp(x.data, libff::alt_bn128_modulus_q.data, libff::alt_bn128_q_limbs) < 0;
|
||||
}
|
||||
|
||||
std::variant<alt_bn128_error, libff::alt_bn128_G1> decode_g1_element(const bytes& bytes64_be) noexcept {
|
||||
if(bytes64_be.size() != 64) {
|
||||
return alt_bn128_error::input_len_error;
|
||||
}
|
||||
|
||||
bytes sub1(bytes64_be.begin(), bytes64_be.begin()+32);
|
||||
bytes sub2(bytes64_be.begin()+32, bytes64_be.begin()+64);
|
||||
|
||||
Scalar x{to_scalar(sub1)};
|
||||
Scalar y{to_scalar(sub2)};
|
||||
|
||||
if (!valid_element_of_fp(x) || !valid_element_of_fp(y)) {
|
||||
return alt_bn128_error::operand_component_invalid;
|
||||
}
|
||||
|
||||
if (x.is_zero() && y.is_zero()) {
|
||||
return libff::alt_bn128_G1::zero();
|
||||
}
|
||||
|
||||
libff::alt_bn128_G1 point{x, y, libff::alt_bn128_Fq::one()};
|
||||
if (!point.is_well_formed()) {
|
||||
return alt_bn128_error::operand_not_in_curve;
|
||||
}
|
||||
return point;
|
||||
}
|
||||
|
||||
std::variant<alt_bn128_error, libff::alt_bn128_Fq2> decode_fp2_element(const bytes& bytes64_be) noexcept {
|
||||
if(bytes64_be.size() != 64) {
|
||||
return alt_bn128_error::input_len_error;
|
||||
}
|
||||
|
||||
// big-endian encoding
|
||||
bytes sub1(bytes64_be.begin()+32, bytes64_be.begin()+64);
|
||||
bytes sub2(bytes64_be.begin(), bytes64_be.begin()+32);
|
||||
|
||||
Scalar c0{to_scalar(sub1)};
|
||||
Scalar c1{to_scalar(sub2)};
|
||||
|
||||
if (!valid_element_of_fp(c0) || !valid_element_of_fp(c1)) {
|
||||
return alt_bn128_error::operand_component_invalid;
|
||||
}
|
||||
|
||||
return libff::alt_bn128_Fq2{c0, c1};
|
||||
}
|
||||
|
||||
std::variant<alt_bn128_error, libff::alt_bn128_G2> decode_g2_element(const bytes& bytes128_be) noexcept {
|
||||
if(bytes128_be.size() != 128) {
|
||||
return alt_bn128_error::input_len_error;
|
||||
}
|
||||
|
||||
bytes sub1(bytes128_be.begin(), bytes128_be.begin()+64);
|
||||
auto maybe_x = decode_fp2_element(sub1);
|
||||
if (std::holds_alternative<alt_bn128_error>(maybe_x)) {
|
||||
return std::get<alt_bn128_error>(maybe_x);
|
||||
}
|
||||
|
||||
bytes sub2(bytes128_be.begin()+64, bytes128_be.begin()+128);
|
||||
auto maybe_y = decode_fp2_element(sub2);
|
||||
if (std::holds_alternative<alt_bn128_error>(maybe_y)) {
|
||||
return std::get<alt_bn128_error>(maybe_y);
|
||||
}
|
||||
|
||||
const auto& x = std::get<libff::alt_bn128_Fq2>(maybe_x);
|
||||
const auto& y = std::get<libff::alt_bn128_Fq2>(maybe_y);
|
||||
|
||||
if (x.is_zero() && y.is_zero()) {
|
||||
return libff::alt_bn128_G2::zero();
|
||||
}
|
||||
|
||||
libff::alt_bn128_G2 point{x, y, libff::alt_bn128_Fq2::one()};
|
||||
if (!point.is_well_formed()) {
|
||||
return alt_bn128_error::operand_not_in_curve;
|
||||
}
|
||||
|
||||
if (!(libff::alt_bn128_G2::order() * point).is_zero()) {
|
||||
// wrong order, doesn't belong to the subgroup G2
|
||||
return alt_bn128_error::operand_outside_g2;
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
bytes encode_g1_element(libff::alt_bn128_G1 p) noexcept {
|
||||
bytes out(64, '\0');
|
||||
if (p.is_zero()) {
|
||||
return out;
|
||||
}
|
||||
|
||||
p.to_affine_coordinates();
|
||||
|
||||
auto x{p.X.as_bigint()};
|
||||
auto y{p.Y.as_bigint()};
|
||||
|
||||
std::memcpy(&out[0], y.data, 32);
|
||||
std::memcpy(&out[32], x.data, 32);
|
||||
|
||||
std::reverse(out.begin(), out.end());
|
||||
return out;
|
||||
}
|
||||
|
||||
std::variant<alt_bn128_error, bytes> alt_bn128_add(const bytes& op1, const bytes& op2) {
|
||||
fc::initLibSnark();
|
||||
|
||||
auto maybe_x = decode_g1_element(op1);
|
||||
if (std::holds_alternative<alt_bn128_error>(maybe_x)) {
|
||||
return std::get<alt_bn128_error>(maybe_x);
|
||||
}
|
||||
|
||||
auto maybe_y = decode_g1_element(op2);
|
||||
if (std::holds_alternative<alt_bn128_error>(maybe_y)) {
|
||||
return std::get<alt_bn128_error>(maybe_y);
|
||||
}
|
||||
|
||||
const auto& x = std::get<libff::alt_bn128_G1>(maybe_x);
|
||||
const auto& y = std::get<libff::alt_bn128_G1>(maybe_y);
|
||||
|
||||
libff::alt_bn128_G1 g1Sum = x + y;
|
||||
return encode_g1_element(g1Sum);
|
||||
}
|
||||
|
||||
std::variant<alt_bn128_error, bytes> alt_bn128_mul(const bytes& g1_point, const bytes& scalar) {
|
||||
initLibSnark();
|
||||
|
||||
auto maybe_x = decode_g1_element(g1_point);
|
||||
if (std::holds_alternative<alt_bn128_error>(maybe_x)) {
|
||||
return std::get<alt_bn128_error>(maybe_x);
|
||||
}
|
||||
|
||||
auto& x = std::get<libff::alt_bn128_G1>(maybe_x);
|
||||
|
||||
if(scalar.size() != 32) {
|
||||
return alt_bn128_error::invalid_scalar_size;
|
||||
}
|
||||
|
||||
Scalar n{to_scalar(scalar)};
|
||||
|
||||
libff::alt_bn128_G1 g1Product = n * x;
|
||||
return encode_g1_element(g1Product);
|
||||
}
|
||||
|
||||
static constexpr size_t kSnarkvStride{192};
|
||||
|
||||
std::variant<alt_bn128_error, bool> alt_bn128_pair(const bytes& g1_g2_pairs, const yield_function_t& yield) {
|
||||
if (g1_g2_pairs.size() % kSnarkvStride != 0) {
|
||||
return alt_bn128_error::pairing_list_size_error;
|
||||
}
|
||||
|
||||
size_t k{g1_g2_pairs.size() / kSnarkvStride};
|
||||
|
||||
initLibSnark();
|
||||
using namespace libff;
|
||||
|
||||
static const auto one{alt_bn128_Fq12::one()};
|
||||
auto accumulator{one};
|
||||
|
||||
for (size_t i{0}; i < k; ++i) {
|
||||
auto offset = i * kSnarkvStride;
|
||||
|
||||
bytes sub1(g1_g2_pairs.begin()+offset, g1_g2_pairs.begin()+offset+64);
|
||||
auto maybe_a = decode_g1_element(sub1);
|
||||
if (std::holds_alternative<alt_bn128_error>(maybe_a)) {
|
||||
return std::get<alt_bn128_error>(maybe_a);
|
||||
}
|
||||
|
||||
bytes sub2(g1_g2_pairs.begin()+offset+64, g1_g2_pairs.begin()+offset+64+128);
|
||||
auto maybe_b = decode_g2_element(sub2);
|
||||
if (std::holds_alternative<alt_bn128_error>(maybe_b)) {
|
||||
return std::get<alt_bn128_error>(maybe_b);
|
||||
}
|
||||
|
||||
const auto& a = std::get<libff::alt_bn128_G1>(maybe_a);
|
||||
const auto& b = std::get<libff::alt_bn128_G2>(maybe_b);
|
||||
|
||||
if (a.is_zero() || b.is_zero()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
accumulator = accumulator * alt_bn128_miller_loop(alt_bn128_precompute_G1(a), alt_bn128_precompute_G2(b));
|
||||
yield();
|
||||
}
|
||||
|
||||
bool pair_result = false;
|
||||
if (alt_bn128_final_exponentiation(accumulator) == one) {
|
||||
pair_result = true;
|
||||
}
|
||||
|
||||
return pair_result;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user