Merge branch 'main' of github.com:AntelopeIO/leap into gh-17
This commit is contained in:
@@ -6,11 +6,12 @@ RUN apt-get update && apt-get upgrade -y && \
|
||||
cmake \
|
||||
git \
|
||||
jq \
|
||||
libboost-all-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libgmp-dev \
|
||||
libssl-dev \
|
||||
llvm-11-dev \
|
||||
ninja-build \
|
||||
python3-numpy \
|
||||
file \
|
||||
zlib1g-dev \
|
||||
zstd
|
||||
|
||||
@@ -6,11 +6,12 @@ RUN apt-get update && apt-get upgrade -y && \
|
||||
cmake \
|
||||
git \
|
||||
jq \
|
||||
libboost-all-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libgmp-dev \
|
||||
libssl-dev \
|
||||
llvm-11-dev \
|
||||
ninja-build \
|
||||
python3-numpy \
|
||||
file \
|
||||
zlib1g-dev \
|
||||
zstd
|
||||
|
||||
@@ -31,20 +31,13 @@ defaults:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
d:
|
||||
name: Discover Platforms
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
missing-platforms: ${{steps.discover.outputs.missing-platforms}}
|
||||
p: ${{steps.discover.outputs.platforms}}
|
||||
steps:
|
||||
- name: Discover Platforms
|
||||
id: discover
|
||||
uses: AntelopeIO/discover-platforms-action@v1
|
||||
with:
|
||||
platform-file: .cicd/platforms.json
|
||||
password: ${{secrets.GITHUB_TOKEN}}
|
||||
package-name: builders
|
||||
build-base:
|
||||
name: Run Build Workflow
|
||||
uses: ./.github/workflows/build_base.yaml
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
v:
|
||||
name: Discover Versions
|
||||
runs-on: ubuntu-latest
|
||||
@@ -72,73 +65,21 @@ jobs:
|
||||
if [[ "${{inputs.override-eos-system-contracts}}" != "" ]]; then
|
||||
echo eos-system-contracts-ref=${{inputs.override-eos-system-contracts}} >> $GITHUB_OUTPUT
|
||||
fi
|
||||
build-platforms:
|
||||
name: Build Platforms
|
||||
needs: d
|
||||
if: needs.d.outputs.missing-platforms != '[]'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: ${{fromJSON(needs.d.outputs.missing-platforms)}}
|
||||
runs-on: ["self-hosted", "enf-x86-beefy"]
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Login to Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{github.repository_owner}}
|
||||
password: ${{secrets.GITHUB_TOKEN}}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
push: true
|
||||
tags: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}}
|
||||
file: ${{fromJSON(needs.d.outputs.p)[matrix.platform].dockerfile}}
|
||||
|
||||
Build:
|
||||
needs: [d, build-platforms]
|
||||
if: always() && needs.d.result == 'success' && (needs.build-platforms.result == 'success' || needs.build-platforms.result == 'skipped')
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu20, ubuntu22]
|
||||
runs-on: ["self-hosted", "enf-x86-beefy"]
|
||||
container: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build
|
||||
id: build
|
||||
run: |
|
||||
# https://github.com/actions/runner/issues/2033
|
||||
chown -R $(id -u):$(id -g) $PWD
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -GNinja
|
||||
cmake --build build
|
||||
tar -pc --exclude "*.o" build | zstd --long -T0 -9 > build.tar.zst
|
||||
- name: Upload builddir
|
||||
uses: AntelopeIO/upload-artifact-large-chunks-action@v1
|
||||
with:
|
||||
name: ${{matrix.platform}}-build
|
||||
path: build.tar.zst
|
||||
|
||||
dev-package:
|
||||
name: Build leap-dev package
|
||||
needs: [d, Build]
|
||||
if: always() && needs.Build.result == 'success'
|
||||
needs: [build-base]
|
||||
if: always() && needs.build-base.result == 'success'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu20, ubuntu22]
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}}
|
||||
container: ${{fromJSON(needs.build-base.outputs.p)[matrix.platform].image}}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
submodules: recursive
|
||||
- name: Download builddir
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
@@ -150,8 +91,8 @@ jobs:
|
||||
cpack
|
||||
- name: Install dev package
|
||||
run: |
|
||||
apt update && apt upgrade -y
|
||||
apt install -y ./build/leap_*.deb ./build/leap-dev*.deb
|
||||
apt-get update && apt-get upgrade -y
|
||||
apt-get install -y ./build/leap_*.deb ./build/leap-dev*.deb
|
||||
- name: Test using TestHarness
|
||||
run: |
|
||||
python3 -c "from TestHarness import Cluster"
|
||||
@@ -163,15 +104,15 @@ jobs:
|
||||
|
||||
tests:
|
||||
name: Tests
|
||||
needs: [d, Build]
|
||||
if: always() && needs.Build.result == 'success'
|
||||
needs: [build-base]
|
||||
if: always() && needs.build-base.result == 'success'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu20, ubuntu22]
|
||||
runs-on: ["self-hosted", "enf-x86-hightier"]
|
||||
container:
|
||||
image: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}}
|
||||
image: ${{fromJSON(needs.build-base.outputs.p)[matrix.platform].image}}
|
||||
options: --security-opt seccomp=unconfined
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -189,8 +130,8 @@ jobs:
|
||||
|
||||
np-tests:
|
||||
name: NP Tests
|
||||
needs: [d, Build]
|
||||
if: always() && needs.Build.result == 'success'
|
||||
needs: [build-base]
|
||||
if: always() && needs.build-base.result == 'success'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -205,7 +146,7 @@ jobs:
|
||||
- name: Run tests in parallel containers
|
||||
uses: ./.github/actions/parallel-ctest-containers
|
||||
with:
|
||||
container: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}}
|
||||
container: ${{fromJSON(needs.build-base.outputs.p)[matrix.platform].image}}
|
||||
error-log-paths: '["build/etc", "build/var", "build/leap-ignition-wd", "build/TestLogs"]'
|
||||
log-tarball-prefix: ${{matrix.platform}}
|
||||
tests-label: nonparallelizable_tests
|
||||
@@ -219,8 +160,8 @@ jobs:
|
||||
|
||||
lr-tests:
|
||||
name: LR Tests
|
||||
needs: [d, Build]
|
||||
if: always() && needs.Build.result == 'success'
|
||||
needs: [build-base]
|
||||
if: always() && needs.build-base.result == 'success'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -235,7 +176,7 @@ jobs:
|
||||
- name: Run tests in parallel containers
|
||||
uses: ./.github/actions/parallel-ctest-containers
|
||||
with:
|
||||
container: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}}
|
||||
container: ${{fromJSON(needs.build-base.outputs.p)[matrix.platform].image}}
|
||||
error-log-paths: '["build/etc", "build/var", "build/leap-ignition-wd", "build/TestLogs"]'
|
||||
log-tarball-prefix: ${{matrix.platform}}
|
||||
tests-label: long_running_tests
|
||||
@@ -249,7 +190,7 @@ jobs:
|
||||
|
||||
libtester-tests:
|
||||
name: libtester tests
|
||||
needs: [d, v, Build, dev-package]
|
||||
needs: [build-base, v, dev-package]
|
||||
if: always() && needs.v.result == 'success' && needs.dev-package.result == 'success'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -257,8 +198,16 @@ jobs:
|
||||
platform: [ubuntu20, ubuntu22]
|
||||
test: [build-tree, make-dev-install, deb-install]
|
||||
runs-on: ["self-hosted", "enf-x86-midtier"]
|
||||
container: ${{ matrix.test != 'deb-install' && fromJSON(needs.d.outputs.p)[matrix.platform].image || matrix.platform == 'ubuntu20' && 'ubuntu:focal' || 'ubuntu:jammy' }}
|
||||
container: ${{ matrix.test != 'deb-install' && fromJSON(needs.build-base.outputs.p)[matrix.platform].image || matrix.platform == 'ubuntu20' && 'ubuntu:focal' || 'ubuntu:jammy' }}
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
TZ: Etc/UTC
|
||||
steps:
|
||||
- name: Update Package Index & Upgrade Packages
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get upgrade -y
|
||||
|
||||
# LEAP
|
||||
- if: ${{ matrix.test != 'deb-install' }}
|
||||
name: Clone leap
|
||||
@@ -295,9 +244,6 @@ jobs:
|
||||
- if: ${{ matrix.test == 'deb-install' }}
|
||||
name: Install leap-dev Package
|
||||
run: |
|
||||
apt-get update
|
||||
export DEBIAN_FRONTEND='noninteractive'
|
||||
export TZ='Etc/UTC'
|
||||
apt-get install -y ./*.deb
|
||||
rm ./*.deb
|
||||
|
||||
@@ -314,7 +260,7 @@ jobs:
|
||||
token: ${{github.token}}
|
||||
- name: Install cdt Packages
|
||||
run: |
|
||||
apt install -y ./*.deb
|
||||
apt-get install -y ./*.deb
|
||||
rm ./*.deb
|
||||
|
||||
# Reference Contracts
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
name: "Build leap"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
outputs:
|
||||
p:
|
||||
description: "Discovered Build Platforms"
|
||||
value: ${{ jobs.d.outputs.p }}
|
||||
|
||||
permissions:
|
||||
packages: read
|
||||
contents: read
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
d:
|
||||
name: Discover Platforms
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
missing-platforms: ${{steps.discover.outputs.missing-platforms}}
|
||||
p: ${{steps.discover.outputs.platforms}}
|
||||
steps:
|
||||
- name: Discover Platforms
|
||||
id: discover
|
||||
uses: AntelopeIO/discover-platforms-action@v1
|
||||
with:
|
||||
platform-file: .cicd/platforms.json
|
||||
password: ${{secrets.GITHUB_TOKEN}}
|
||||
package-name: builders
|
||||
|
||||
build-platforms:
|
||||
name: Build Platforms
|
||||
needs: d
|
||||
if: needs.d.outputs.missing-platforms != '[]'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: ${{fromJSON(needs.d.outputs.missing-platforms)}}
|
||||
runs-on: ["self-hosted", "enf-x86-beefy"]
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Login to Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{github.repository_owner}}
|
||||
password: ${{secrets.GITHUB_TOKEN}}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
push: true
|
||||
tags: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}}
|
||||
file: ${{fromJSON(needs.d.outputs.p)[matrix.platform].dockerfile}}
|
||||
|
||||
Build:
|
||||
name: Build leap
|
||||
needs: [d, build-platforms]
|
||||
if: always() && needs.d.result == 'success' && (needs.build-platforms.result == 'success' || needs.build-platforms.result == 'skipped')
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu20, ubuntu22]
|
||||
runs-on: ["self-hosted", "enf-x86-beefy"]
|
||||
container: ${{fromJSON(needs.d.outputs.p)[matrix.platform].image}}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build
|
||||
id: build
|
||||
run: |
|
||||
# https://github.com/actions/runner/issues/2033
|
||||
chown -R $(id -u):$(id -g) $PWD
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_LEAP_DEV_DEB=On -GNinja
|
||||
cmake --build build
|
||||
tar -pc --exclude "*.o" build | zstd --long -T0 -9 > build.tar.zst
|
||||
- name: Upload builddir
|
||||
uses: AntelopeIO/upload-artifact-large-chunks-action@v1
|
||||
with:
|
||||
name: ${{matrix.platform}}-build
|
||||
path: build.tar.zst
|
||||
@@ -12,10 +12,59 @@ defaults:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
tmp:
|
||||
name: Stub
|
||||
runs-on: ubuntu-latest
|
||||
build-base:
|
||||
name: Run Build Workflow
|
||||
uses: ./.github/workflows/build_base.yaml
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
tests:
|
||||
name: Tests
|
||||
needs: [build-base]
|
||||
if: always() && needs.build-base.result == 'success'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu20, ubuntu22]
|
||||
release: [3.1, 3.2, 4.0]
|
||||
runs-on: ["self-hosted", "enf-x86-lowtier"]
|
||||
container:
|
||||
image: ${{fromJSON(needs.build-base.outputs.p)[matrix.platform].image}}
|
||||
options: --security-opt seccomp=unconfined
|
||||
steps:
|
||||
- name: Workflow Stub
|
||||
- uses: actions/checkout@v3
|
||||
- name: Download builddir
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{matrix.platform}}-build
|
||||
- name: Extract Build Directory
|
||||
run: |
|
||||
echo "Workflow Stub"
|
||||
zstdcat build.tar.zst | tar x
|
||||
- name: Download Prev Leap Version
|
||||
uses: AntelopeIO/asset-artifact-download-action@v2
|
||||
with:
|
||||
owner: AntelopeIO
|
||||
repo: leap
|
||||
file: '(leap).*${{matrix.platform}}.04.*(x86_64|amd64).deb'
|
||||
target: '${{matrix.release}}'
|
||||
token: ${{github.token}}
|
||||
- name: Install leap & replace binaries for PH use
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y ./leap*.deb
|
||||
rm build/bin/nodeos
|
||||
rm build/bin/cleos
|
||||
cp /usr/bin/nodeos build/bin
|
||||
cp /usr/bin/cleos build/bin
|
||||
./build/bin/nodeos --version
|
||||
- if: ${{ matrix.release == '3.1' || matrix.release == '3.2' }}
|
||||
name: Run Performance Tests (<v4.0)
|
||||
run: |
|
||||
cd build
|
||||
ctest --output-on-failure -R performance_test -E read_only --timeout 480
|
||||
- if: ${{ matrix.release != '3.1' && matrix.release != '3.2' }}
|
||||
name: Run Performance Tests (>=v4.0)
|
||||
run: |
|
||||
cd build
|
||||
ctest --output-on-failure -R performance_test --timeout 480
|
||||
|
||||
@@ -75,6 +75,8 @@ witness_node_data_dir
|
||||
*.pyc
|
||||
*.pyo
|
||||
|
||||
*.gdb_history
|
||||
|
||||
Testing/*
|
||||
build.tar.gz
|
||||
[Bb]uild*/*
|
||||
|
||||
@@ -31,3 +31,6 @@
|
||||
[submodule "libraries/cli11/cli11"]
|
||||
path = libraries/cli11/cli11
|
||||
url = https://github.com/AntelopeIO/CLI11.git
|
||||
[submodule "libraries/boost"]
|
||||
path = libraries/boost
|
||||
url = https://github.com/boostorg/boost.git
|
||||
|
||||
+18
-9
@@ -101,12 +101,6 @@ else()
|
||||
set(no_whole_archive_flag "--no-whole-archive")
|
||||
endif()
|
||||
|
||||
set(Boost_USE_MULTITHREADED ON)
|
||||
set( Boost_USE_STATIC_LIBS ON CACHE STRING "ON or OFF" )
|
||||
# Most boost deps get implictly picked up via fc, as just about everything links to fc. In addition we pick up
|
||||
# the pthread dependency through fc.
|
||||
find_package(Boost 1.71 REQUIRED COMPONENTS program_options unit_test_framework system)
|
||||
|
||||
if( APPLE AND UNIX )
|
||||
# Apple Specific Options Here
|
||||
message( STATUS "Configuring Leap on macOS" )
|
||||
@@ -174,9 +168,10 @@ endif()
|
||||
|
||||
message( STATUS "Using '${EOSIO_ROOT_KEY}' as public key for 'eosio' account" )
|
||||
|
||||
find_package( Gperftools QUIET )
|
||||
if( GPERFTOOLS_FOUND )
|
||||
message( STATUS "Found gperftools; compiling Leap with TCMalloc")
|
||||
option(ENABLE_TCMALLOC "use tcmalloc (requires gperftools)" OFF)
|
||||
if( ENABLE_TCMALLOC )
|
||||
find_package( Gperftools REQUIRED )
|
||||
message( STATUS "Compiling Leap with TCMalloc")
|
||||
#if doing this by the book, simply link_libraries( ${GPERFTOOLS_TCMALLOC} ) here. That will
|
||||
#give the performance benefits of tcmalloc but since it won't be linked last
|
||||
#the heap profiler & checker may not be accurate. This here is rather undocumented behavior
|
||||
@@ -277,6 +272,18 @@ configure_file(${CMAKE_SOURCE_DIR}/libraries/cli11/bash-completion/completions/c
|
||||
install(FILES libraries/cli11/bash-completion/completions/leap-util DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/bash-completion/completions COMPONENT base)
|
||||
install(FILES libraries/cli11/bash-completion/completions/cleos DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/bash-completion/completions COMPONENT base)
|
||||
|
||||
# Add the boost submodule we used to build to our install package, so headers can be found for libtester
|
||||
install(DIRECTORY "${CMAKE_SOURCE_DIR}/libraries/boost/"
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/leap_boost
|
||||
COMPONENT dev EXCLUDE_FROM_ALL
|
||||
PATTERN ".git/*" EXCLUDE
|
||||
PATTERN "example/*" EXCLUDE
|
||||
PATTERN "bench/*" EXCLUDE
|
||||
PATTERN "doc/*" EXCLUDE
|
||||
PATTERN "libs/*/test" EXCLUDE
|
||||
PATTERN "tools/*/test" EXCLUDE
|
||||
)
|
||||
|
||||
add_custom_target(dev-install
|
||||
COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}"
|
||||
COMMAND "${CMAKE_COMMAND}" --install "${CMAKE_BINARY_DIR}"
|
||||
@@ -286,5 +293,7 @@ add_custom_target(dev-install
|
||||
|
||||
include(doxygen)
|
||||
|
||||
option(ENABLE_LEAP_DEV_DEB "Enable building the leap-dev .deb package" OFF)
|
||||
|
||||
include(package.cmake)
|
||||
include(CPack)
|
||||
|
||||
@@ -14,12 +14,6 @@ if (LLVM_DIR STREQUAL "" OR NOT LLVM_DIR)
|
||||
set(LLVM_DIR @LLVM_DIR@)
|
||||
endif()
|
||||
|
||||
find_package( Gperftools QUIET )
|
||||
if( GPERFTOOLS_FOUND )
|
||||
message( STATUS "Found gperftools; compiling tests with TCMalloc")
|
||||
list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc )
|
||||
endif()
|
||||
|
||||
if(NOT "@LLVM_FOUND@" STREQUAL "")
|
||||
find_package(LLVM @LLVM_VERSION@ EXACT REQUIRED CONFIG)
|
||||
llvm_map_components_to_libnames(LLVM_LIBS support core passes mcjit native DebugInfoDWARF orcjit)
|
||||
@@ -41,14 +35,10 @@ else ( APPLE )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS} -Wall")
|
||||
endif ( APPLE )
|
||||
|
||||
set( Boost_USE_MULTITHREADED ON )
|
||||
set( Boost_USE_STATIC_LIBS ON CACHE STRING "ON or OFF" )
|
||||
find_package(Boost @Boost_MAJOR_VERSION@.@Boost_MINOR_VERSION@ EXACT REQUIRED COMPONENTS
|
||||
date_time
|
||||
filesystem
|
||||
system
|
||||
chrono
|
||||
iostreams
|
||||
unit_test_framework)
|
||||
|
||||
add_subdirectory( @CMAKE_INSTALL_FULL_DATAROOTDIR@/leap_boost ${PROJECT_BINARY_DIR}/libraries/boost EXCLUDE_FROM_ALL)
|
||||
|
||||
find_library(libtester eosio_testing @CMAKE_INSTALL_FULL_LIBDIR@ NO_DEFAULT_PATH)
|
||||
find_library(libchain eosio_chain @CMAKE_INSTALL_FULL_LIBDIR@ NO_DEFAULT_PATH)
|
||||
@@ -94,12 +84,18 @@ macro(add_eosio_test_executable test_name)
|
||||
${libbn256}
|
||||
@GMP_LIBRARY@
|
||||
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_CHRONO_LIBRARY}
|
||||
${Boost_IOSTREAMS_LIBRARY}
|
||||
Boost::date_time
|
||||
Boost::filesystem
|
||||
Boost::system
|
||||
Boost::chrono
|
||||
Boost::multi_index
|
||||
Boost::multiprecision
|
||||
Boost::interprocess
|
||||
Boost::asio
|
||||
Boost::signals2
|
||||
Boost::iostreams
|
||||
"-lz" # Needed by Boost iostreams
|
||||
${Boost_DATE_TIME_LIBRARY}
|
||||
Boost::unit_test_framework
|
||||
|
||||
${LLVM_LIBS}
|
||||
|
||||
@@ -115,7 +111,6 @@ macro(add_eosio_test_executable test_name)
|
||||
endif()
|
||||
|
||||
target_include_directories( ${test_name} PUBLIC
|
||||
${Boost_INCLUDE_DIRS}
|
||||
@OPENSSL_INCLUDE_DIR@
|
||||
@CMAKE_INSTALL_PREFIX@
|
||||
@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
||||
|
||||
@@ -12,12 +12,6 @@ if (LLVM_DIR STREQUAL "" OR NOT LLVM_DIR)
|
||||
set(LLVM_DIR @LLVM_DIR@)
|
||||
endif()
|
||||
|
||||
find_package( Gperftools QUIET )
|
||||
if( GPERFTOOLS_FOUND )
|
||||
message( STATUS "Found gperftools; compiling tests with TCMalloc")
|
||||
list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc )
|
||||
endif()
|
||||
|
||||
if(NOT "@LLVM_FOUND@" STREQUAL "")
|
||||
find_package(LLVM @LLVM_VERSION@ EXACT REQUIRED CONFIG)
|
||||
llvm_map_components_to_libnames(LLVM_LIBS support core passes mcjit native DebugInfoDWARF orcjit)
|
||||
@@ -38,14 +32,10 @@ else ( APPLE )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS} -Wall")
|
||||
endif ( APPLE )
|
||||
|
||||
set( Boost_USE_MULTITHREADED ON )
|
||||
set( Boost_USE_STATIC_LIBS ON CACHE STRING "ON or OFF" )
|
||||
find_package(Boost @Boost_MAJOR_VERSION@.@Boost_MINOR_VERSION@ EXACT REQUIRED COMPONENTS
|
||||
date_time
|
||||
filesystem
|
||||
system
|
||||
chrono
|
||||
iostreams
|
||||
unit_test_framework)
|
||||
|
||||
add_subdirectory( @CMAKE_SOURCE_DIR@/libraries/boost ${PROJECT_BINARY_DIR}/libraries/boost EXCLUDE_FROM_ALL)
|
||||
|
||||
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)
|
||||
@@ -91,12 +81,18 @@ macro(add_eosio_test_executable test_name)
|
||||
${libbn256}
|
||||
@GMP_LIBRARY@
|
||||
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_CHRONO_LIBRARY}
|
||||
${Boost_IOSTREAMS_LIBRARY}
|
||||
Boost::date_time
|
||||
Boost::filesystem
|
||||
Boost::system
|
||||
Boost::chrono
|
||||
Boost::multi_index
|
||||
Boost::multiprecision
|
||||
Boost::interprocess
|
||||
Boost::asio
|
||||
Boost::signals2
|
||||
Boost::iostreams
|
||||
"-lz" # Needed by Boost iostreams
|
||||
${Boost_DATE_TIME_LIBRARY}
|
||||
Boost::unit_test_framework
|
||||
|
||||
${LLVM_LIBS}
|
||||
|
||||
@@ -112,7 +108,6 @@ macro(add_eosio_test_executable test_name)
|
||||
endif()
|
||||
|
||||
target_include_directories( ${test_name} PUBLIC
|
||||
${Boost_INCLUDE_DIRS}
|
||||
@OPENSSL_INCLUDE_DIR@
|
||||
@CMAKE_SOURCE_DIR@/libraries/chain/include
|
||||
@CMAKE_BINARY_DIR@/libraries/chain/include
|
||||
|
||||
@@ -132,12 +132,13 @@ sudo apt-get install -y \
|
||||
build-essential \
|
||||
cmake \
|
||||
git \
|
||||
libboost-all-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libgmp-dev \
|
||||
libssl-dev \
|
||||
llvm-11-dev \
|
||||
python3-numpy
|
||||
python3-numpy \
|
||||
file \
|
||||
zlib1g-dev
|
||||
```
|
||||
To build, make sure you are in the root of the `leap` repo, then run the following command:
|
||||
```bash
|
||||
|
||||
@@ -131,8 +131,7 @@ Config Options for eosio::chain_plugin:
|
||||
received via the P2P network are not
|
||||
relayed and transactions cannot be
|
||||
pushed via the chain API.
|
||||
In "speculative" mode: (DEPRECATED:
|
||||
head mode recommended) database
|
||||
In "speculative" mode: database
|
||||
contains state changes by transactions
|
||||
in the blockchain up to the head block
|
||||
as well as some transactions not yet
|
||||
|
||||
@@ -5,6 +5,10 @@ set(SOFTFLOAT_INSTALL_COMPONENT "dev")
|
||||
set(EOSVM_INSTALL_COMPONENT "dev")
|
||||
set(BN256_INSTALL_COMPONENT "dev")
|
||||
|
||||
set( Boost_USE_MULTITHREADED ON )
|
||||
set( Boost_USE_STATIC_LIBS ON CACHE STRING "ON or OFF" )
|
||||
add_subdirectory( boost EXCLUDE_FROM_ALL )
|
||||
|
||||
add_subdirectory( libfc )
|
||||
add_subdirectory( builtins )
|
||||
add_subdirectory( softfloat )
|
||||
|
||||
+1
-1
Submodule libraries/appbase updated: 54cc7fb4f9...b75b31e14f
Submodule
+1
Submodule libraries/boost added at b6928ae5c9
@@ -100,6 +100,7 @@ add_library( eosio_chain
|
||||
|
||||
wast_to_wasm.cpp
|
||||
wasm_interface.cpp
|
||||
wasm_interface_collection.cpp
|
||||
wasm_eosio_validation.cpp
|
||||
wasm_eosio_injection.cpp
|
||||
wasm_config.cpp
|
||||
@@ -130,8 +131,17 @@ add_library( eosio_chain
|
||||
${HEADERS}
|
||||
)
|
||||
|
||||
## Boost::accumulators depends on Boost::numeric_ublas, which is still missing cmake support (see
|
||||
## https://github.com/boostorg/cmake/issues/39). Until this is fixed, manually add Boost::numeric_ublas
|
||||
## as an interface library
|
||||
## ----------------------------------------------------------------------------------------------------
|
||||
add_library(boost_numeric_ublas INTERFACE)
|
||||
add_library(Boost::numeric_ublas ALIAS boost_numeric_ublas)
|
||||
|
||||
target_link_libraries( eosio_chain PUBLIC bn256 fc chainbase eosio_rapidjson Logging IR WAST WASM
|
||||
softfloat builtins ${CHAIN_EOSVM_LIBRARIES} ${LLVM_LIBS} ${CHAIN_RT_LINKAGE}
|
||||
Boost::signals2 Boost::hana Boost::property_tree Boost::multi_index Boost::asio Boost::lockfree
|
||||
Boost::assign Boost::accumulators
|
||||
)
|
||||
target_include_directories( eosio_chain
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include"
|
||||
|
||||
@@ -548,14 +548,15 @@ namespace eosio { namespace chain {
|
||||
bool disallow_additional_fields = false;
|
||||
for( uint32_t i = 0; i < st.fields.size(); ++i ) {
|
||||
const auto& field = st.fields[i];
|
||||
if( vo.contains( string(field.name).c_str() ) ) {
|
||||
bool present = vo.contains(string(field.name).c_str());
|
||||
if( present || is_optional(field.type) ) {
|
||||
if( disallow_additional_fields )
|
||||
EOS_THROW( pack_exception, "Unexpected field '${f}' found in input object while processing struct '${p}'",
|
||||
("f", ctx.maybe_shorten(field.name))("p", ctx.get_path_string()) );
|
||||
{
|
||||
auto h1 = ctx.push_to_path( impl::field_path_item{ .parent_struct_itr = s_itr, .field_ordinal = i } );
|
||||
auto h2 = ctx.disallow_extensions_unless( &field == &st.fields.back() );
|
||||
_variant_to_binary(_remove_bin_extension(field.type), vo[field.name], ds, ctx);
|
||||
_variant_to_binary(_remove_bin_extension(field.type), present ? vo[field.name] : fc::variant(nullptr), ds, ctx);
|
||||
}
|
||||
} else if( ends_with(field.type, "$") && ctx.extensions_allowed() ) {
|
||||
disallow_additional_fields = true;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <eosio/chain/controller.hpp>
|
||||
#include <eosio/chain/transaction_context.hpp>
|
||||
#include <eosio/chain/exceptions.hpp>
|
||||
#include <eosio/chain/wasm_interface.hpp>
|
||||
#include <eosio/chain/wasm_interface_collection.hpp>
|
||||
#include <eosio/chain/generated_transaction_object.hpp>
|
||||
#include <eosio/chain/authorization_manager.hpp>
|
||||
#include <eosio/chain/resource_limits.hpp>
|
||||
|
||||
@@ -443,8 +443,6 @@ struct controller_impl {
|
||||
if( read_mode == db_read_mode::IRREVERSIBLE ) {
|
||||
controller::block_report br;
|
||||
apply_block( br, *bitr, controller::block_status::complete, trx_meta_cache_lookup{} );
|
||||
head = (*bitr);
|
||||
fork_db.mark_valid( head );
|
||||
}
|
||||
|
||||
emit( self.irreversible_block, *bitr );
|
||||
@@ -1177,7 +1175,7 @@ struct controller_impl {
|
||||
|
||||
transaction_checktime_timer trx_timer(timer);
|
||||
const packed_transaction trx( std::move( etrx ) );
|
||||
transaction_context trx_context( self, trx, std::move(trx_timer), start );
|
||||
transaction_context trx_context( self, trx, trx.id(), std::move(trx_timer), start );
|
||||
|
||||
if (auto dm_logger = get_deep_mind_logger(trx_context.is_transient())) {
|
||||
dm_logger->on_onerror(etrx);
|
||||
@@ -1343,7 +1341,7 @@ struct controller_impl {
|
||||
uint32_t cpu_time_to_bill_us = billed_cpu_time_us;
|
||||
|
||||
transaction_checktime_timer trx_timer( timer );
|
||||
transaction_context trx_context( self, *trx->packed_trx(), std::move(trx_timer) );
|
||||
transaction_context trx_context( self, *trx->packed_trx(), gtrx.trx_id, std::move(trx_timer) );
|
||||
trx_context.leeway = fc::microseconds(0); // avoid stealing cpu resource
|
||||
trx_context.block_deadline = block_deadline;
|
||||
trx_context.max_transaction_time_subjective = max_transaction_time;
|
||||
@@ -1557,7 +1555,7 @@ 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());
|
||||
transaction_context trx_context(self, *trx->packed_trx(), trx->id(), std::move(trx_timer), start, trx->get_trx_type());
|
||||
if ((bool)subjective_cpu_leeway && self.is_speculative_block()) {
|
||||
trx_context.leeway = *subjective_cpu_leeway;
|
||||
}
|
||||
@@ -1931,7 +1929,7 @@ struct controller_impl {
|
||||
/**
|
||||
* @post regardless of the success of commit block there is no active pending block
|
||||
*/
|
||||
void commit_block( bool add_to_fork_db ) {
|
||||
void commit_block( controller::block_status s ) {
|
||||
auto reset_pending_on_exit = fc::make_scoped_exit([this]{
|
||||
pending.reset();
|
||||
});
|
||||
@@ -1940,24 +1938,26 @@ struct controller_impl {
|
||||
EOS_ASSERT( std::holds_alternative<completed_block>(pending->_block_stage), block_validate_exception,
|
||||
"cannot call commit_block until pending block is completed" );
|
||||
|
||||
auto bsp = std::get<completed_block>(pending->_block_stage)._block_state;
|
||||
const auto& bsp = std::get<completed_block>(pending->_block_stage)._block_state;
|
||||
|
||||
if( add_to_fork_db ) {
|
||||
if( s == controller::block_status::incomplete ) {
|
||||
fork_db.add( bsp );
|
||||
fork_db.mark_valid( bsp );
|
||||
emit( self.accepted_block_header, bsp );
|
||||
head = fork_db.head();
|
||||
EOS_ASSERT( bsp == head, fork_database_exception, "committed block did not become the new head in fork database");
|
||||
EOS_ASSERT( bsp == fork_db.head(), fork_database_exception, "committed block did not become the new head in fork database");
|
||||
} else if (s != controller::block_status::irreversible) {
|
||||
fork_db.mark_valid( bsp );
|
||||
}
|
||||
head = bsp;
|
||||
|
||||
// at block level, no transaction specific logging is possible
|
||||
if (auto dm_logger = get_deep_mind_logger(false)) {
|
||||
if (auto* dm_logger = get_deep_mind_logger(false)) {
|
||||
dm_logger->on_accepted_block(bsp);
|
||||
}
|
||||
|
||||
emit( self.accepted_block, bsp );
|
||||
|
||||
if( add_to_fork_db ) {
|
||||
if( s == controller::block_status::incomplete ) {
|
||||
log_irreversible();
|
||||
}
|
||||
} catch (...) {
|
||||
@@ -2157,7 +2157,7 @@ struct controller_impl {
|
||||
pending->_block_stage = completed_block{ bsp };
|
||||
|
||||
br = pending->_block_report; // copy before commit block destroys pending
|
||||
commit_block(false);
|
||||
commit_block(s);
|
||||
br.total_time = fc::time_point::now() - start;
|
||||
return;
|
||||
} catch ( const std::bad_alloc& ) {
|
||||
@@ -2309,7 +2309,6 @@ struct controller_impl {
|
||||
controller::block_report br;
|
||||
if( s == controller::block_status::irreversible ) {
|
||||
apply_block( br, bsp, s, trx_meta_cache_lookup{} );
|
||||
head = bsp;
|
||||
|
||||
// On replay, log_irreversible is not called and so no irreversible_block signal is emitted.
|
||||
// So emit it explicitly here.
|
||||
@@ -2335,8 +2334,6 @@ struct controller_impl {
|
||||
if( new_head->header.previous == head->id ) {
|
||||
try {
|
||||
apply_block( br, new_head, s, trx_lookup );
|
||||
fork_db.mark_valid( new_head );
|
||||
head = new_head;
|
||||
} catch ( const std::exception& e ) {
|
||||
fork_db.remove( new_head->id );
|
||||
throw;
|
||||
@@ -2369,8 +2366,6 @@ struct controller_impl {
|
||||
br = controller::block_report{};
|
||||
apply_block( br, *ritr, (*ritr)->is_valid() ? controller::block_status::validated
|
||||
: controller::block_status::complete, trx_lookup );
|
||||
fork_db.mark_valid( *ritr );
|
||||
head = *ritr;
|
||||
} catch ( const std::bad_alloc& ) {
|
||||
throw;
|
||||
} catch ( const boost::interprocess::bad_alloc& ) {
|
||||
@@ -2401,7 +2396,6 @@ struct controller_impl {
|
||||
for( auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr ) {
|
||||
br = controller::block_report{};
|
||||
apply_block( br, *ritr, controller::block_status::validated /* we previously validated these blocks*/, trx_lookup );
|
||||
head = *ritr;
|
||||
}
|
||||
std::rethrow_exception(except);
|
||||
} // end if exception
|
||||
@@ -2698,8 +2692,8 @@ struct controller_impl {
|
||||
wasm_if_collect.init_thread_local_data(db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty());
|
||||
}
|
||||
|
||||
wasm_interface& get_wasm_interface() {
|
||||
return wasm_if_collect.get_wasm_interface();
|
||||
wasm_interface_collection& get_wasm_interface() {
|
||||
return wasm_if_collect;
|
||||
}
|
||||
|
||||
void code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num) {
|
||||
@@ -2967,7 +2961,7 @@ block_state_ptr controller::finalize_block( block_report& br, const signer_callb
|
||||
|
||||
void controller::commit_block() {
|
||||
validate_db_available_size();
|
||||
my->commit_block(true);
|
||||
my->commit_block(block_status::incomplete);
|
||||
}
|
||||
|
||||
deque<transaction_metadata_ptr> controller::abort_block() {
|
||||
@@ -3400,7 +3394,7 @@ const apply_handler* controller::find_apply_handler( account_name receiver, acco
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
wasm_interface& controller::get_wasm_interface() {
|
||||
wasm_interface_collection& controller::get_wasm_interface() {
|
||||
return my->get_wasm_interface();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace eosio { namespace chain {
|
||||
class account_object;
|
||||
class deep_mind_handler;
|
||||
class subjective_billing;
|
||||
class wasm_interface_collection;
|
||||
using resource_limits::resource_limits_manager;
|
||||
using apply_handler = std::function<void(apply_context&)>;
|
||||
using forked_branch_callback = std::function<void(const branch_type&)>;
|
||||
@@ -348,7 +349,7 @@ namespace eosio { namespace chain {
|
||||
*/
|
||||
|
||||
const apply_handler* find_apply_handler( account_name contract, scope_name scope, action_name act )const;
|
||||
wasm_interface& get_wasm_interface();
|
||||
wasm_interface_collection& get_wasm_interface();
|
||||
|
||||
static chain_id_type extract_chain_id(snapshot_reader& snapshot);
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
#pragma once
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <boost/iostreams/device/mapped_file.hpp>
|
||||
#include <fc/io/cfile.hpp>
|
||||
#include <fc/io/datastream.hpp>
|
||||
#include <filesystem>
|
||||
#include <regex>
|
||||
#include <map>
|
||||
|
||||
namespace eosio {
|
||||
namespace chain {
|
||||
|
||||
|
||||
template <typename Lambda>
|
||||
void for_each_file_in_dir_matches(const std::filesystem::path& dir, std::string pattern, Lambda&& lambda) {
|
||||
const std::regex my_filter(pattern);
|
||||
void for_each_file_in_dir_matches(const std::filesystem::path& dir, std::string_view pattern, Lambda&& lambda) {
|
||||
const std::regex my_filter(pattern.begin(), pattern.size());
|
||||
std::smatch what;
|
||||
std::filesystem::directory_iterator end_itr; // Default ctor yields past-the-end
|
||||
for (std::filesystem::directory_iterator p(dir); p != end_itr; ++p) {
|
||||
@@ -36,10 +36,10 @@ struct log_catalog {
|
||||
using block_num_t = uint32_t;
|
||||
|
||||
struct mapped_type {
|
||||
block_num_t last_block_num;
|
||||
block_num_t last_block_num = 0;
|
||||
std::filesystem::path filename_base;
|
||||
};
|
||||
using collection_t = boost::container::flat_map<block_num_t, mapped_type>;
|
||||
using collection_t = std::map<block_num_t, mapped_type>;
|
||||
using size_type = typename collection_t::size_type;
|
||||
static constexpr size_type npos = std::numeric_limits<size_type>::max();
|
||||
|
||||
@@ -84,9 +84,10 @@ struct log_catalog {
|
||||
archive_dir = make_absolute_dir(log_dir, archive_path);
|
||||
}
|
||||
|
||||
for_each_file_in_dir_matches(retained_dir, std::string(name) + suffix_pattern, [this](std::filesystem::path path) {
|
||||
std::string pattern = std::string(name) + suffix_pattern;
|
||||
for_each_file_in_dir_matches(retained_dir, pattern, [this](std::filesystem::path path) {
|
||||
auto log_path = path;
|
||||
auto index_path = path.replace_extension("index");
|
||||
const auto& index_path = path.replace_extension("index");
|
||||
auto path_without_extension = log_path.parent_path() / log_path.stem().string();
|
||||
|
||||
LogData log(log_path);
|
||||
@@ -94,8 +95,10 @@ struct log_catalog {
|
||||
verifier.verify(log, log_path);
|
||||
|
||||
// check if index file matches the log file
|
||||
if (!index_matches_data(index_path, log))
|
||||
log.construct_index(index_path);
|
||||
if (!index_matches_data(index_path, log)) {
|
||||
ilog("Recreating index for: ${i}", ("i", index_path.string()));
|
||||
log.construct_index( index_path );
|
||||
}
|
||||
|
||||
auto existing_itr = collection.find(log.first_block_num());
|
||||
if (existing_itr != collection.end()) {
|
||||
@@ -112,7 +115,7 @@ struct log_catalog {
|
||||
}
|
||||
}
|
||||
|
||||
collection.insert_or_assign(log.first_block_num(), mapped_type{log.last_block_num(), path_without_extension});
|
||||
collection.insert_or_assign(log.first_block_num(), mapped_type{log.last_block_num(), std::move(path_without_extension)});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -120,24 +123,19 @@ struct log_catalog {
|
||||
if (!std::filesystem::exists(index_path))
|
||||
return false;
|
||||
|
||||
auto num_blocks_in_index = std::filesystem::file_size(index_path) / sizeof(uint64_t);
|
||||
if (num_blocks_in_index != log.num_blocks())
|
||||
LogIndex log_i;
|
||||
log_i.open(index_path);
|
||||
|
||||
if (log_i.num_blocks() != log.num_blocks())
|
||||
return false;
|
||||
|
||||
// make sure the last 8 bytes of index and log matches
|
||||
fc::cfile index_file;
|
||||
index_file.set_file_path(index_path);
|
||||
index_file.open("r");
|
||||
index_file.seek_end(-sizeof(uint64_t));
|
||||
uint64_t pos;
|
||||
index_file.read(reinterpret_cast<char*>(&pos), sizeof(pos));
|
||||
return pos == log.last_block_position();
|
||||
return log_i.back() == log.last_block_position();
|
||||
}
|
||||
|
||||
std::optional<uint64_t> get_block_position(uint32_t block_num) {
|
||||
try {
|
||||
if (active_index != npos) {
|
||||
auto active_item = collection.nth(active_index);
|
||||
auto active_item = std::next(collection.begin(), active_index);
|
||||
if (active_item->first <= block_num && block_num <= active_item->second.last_block_num) {
|
||||
return log_index.nth_block_position(block_num - log_data.first_block_num());
|
||||
}
|
||||
@@ -151,7 +149,7 @@ struct log_catalog {
|
||||
auto name = it->second.filename_base;
|
||||
log_data.open(name.replace_extension("log"));
|
||||
log_index.open(name.replace_extension("index"));
|
||||
active_index = collection.index_of(it);
|
||||
active_index = std::distance(collection.begin(), it);
|
||||
return log_index.nth_block_position(block_num - log_data.first_block_num());
|
||||
}
|
||||
return {};
|
||||
@@ -204,7 +202,7 @@ struct log_catalog {
|
||||
/// Add a new entry into the catalog.
|
||||
///
|
||||
/// Notice that \c start_block_num must be monotonically increasing between the invocations of this function
|
||||
/// so that the new entry would be inserted at the end of the flat_map; otherwise, \c active_index would be
|
||||
/// so that the new entry would be inserted at the 'end' of the map; otherwise, \c active_index would be
|
||||
/// invalidated and the mapping between the log data their block range would be wrong. This function is only used
|
||||
/// during the splitting of block log. Using this function for other purpose should make sure if the monotonically
|
||||
/// increasing block num guarantee can be met.
|
||||
@@ -216,23 +214,24 @@ struct log_catalog {
|
||||
std::filesystem::path new_path = retained_dir / buf;
|
||||
rename_bundle(dir / name, new_path);
|
||||
size_type items_to_erase = 0;
|
||||
collection.emplace(start_block_num, mapped_type{end_block_num, new_path});
|
||||
collection.emplace(start_block_num, mapped_type{end_block_num, std::move(new_path)});
|
||||
if (collection.size() >= max_retained_files) {
|
||||
items_to_erase =
|
||||
max_retained_files > 0 ? collection.size() - max_retained_files : collection.size();
|
||||
auto last = std::next( collection.begin(), items_to_erase);
|
||||
|
||||
for (auto it = collection.begin(); it < collection.begin() + items_to_erase; ++it) {
|
||||
for (auto it = collection.begin(); it != last; ++it) {
|
||||
auto orig_name = it->second.filename_base;
|
||||
if (archive_dir.empty()) {
|
||||
// delete the old files when no backup dir is specified
|
||||
std::filesystem::remove(orig_name.replace_extension("log"));
|
||||
std::filesystem::remove(orig_name.replace_extension("index"));
|
||||
} else {
|
||||
// move the the archive dir
|
||||
// move the archive dir
|
||||
rename_bundle(orig_name, archive_dir / orig_name.filename());
|
||||
}
|
||||
}
|
||||
collection.erase(collection.begin(), collection.begin() + items_to_erase);
|
||||
collection.erase(collection.begin(), last);
|
||||
active_index = active_index == npos || active_index < items_to_erase
|
||||
? npos
|
||||
: active_index - items_to_erase;
|
||||
@@ -258,7 +257,7 @@ struct log_catalog {
|
||||
active_index = npos;
|
||||
auto it = collection.upper_bound(block_num);
|
||||
|
||||
if (it == collection.begin() || block_num > (it - 1)->second.last_block_num) {
|
||||
if (it == collection.begin() || block_num > std::prev(it)->second.last_block_num) {
|
||||
std::for_each(it, collection.end(), remove_files);
|
||||
collection.erase(it, collection.end());
|
||||
return 0;
|
||||
@@ -267,7 +266,7 @@ struct log_catalog {
|
||||
auto name = truncate_it->second.filename_base;
|
||||
std::filesystem::rename(name.replace_extension("log"), new_name.replace_extension("log"));
|
||||
std::filesystem::rename(name.replace_extension("index"), new_name.replace_extension("index"));
|
||||
std::for_each(truncate_it + 1, collection.end(), remove_files);
|
||||
std::for_each(std::next(truncate_it), collection.end(), remove_files);
|
||||
auto result = truncate_it->first;
|
||||
collection.erase(truncate_it, collection.end());
|
||||
return result;
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace eosio { namespace chain {
|
||||
|
||||
transaction_context( controller& c,
|
||||
const packed_transaction& t,
|
||||
const transaction_id_type& trx_id, // trx_id diff than t.id() before replace_deferred
|
||||
transaction_checktime_timer&& timer,
|
||||
fc::time_point start = fc::time_point::now(),
|
||||
transaction_metadata::trx_type type = transaction_metadata::trx_type::input);
|
||||
@@ -127,6 +128,7 @@ namespace eosio { namespace chain {
|
||||
|
||||
controller& control;
|
||||
const packed_transaction& packed_trx;
|
||||
const transaction_id_type& id;
|
||||
std::optional<chainbase::database::session> undo_session;
|
||||
transaction_trace_ptr trace;
|
||||
fc::time_point start;
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace eosio { namespace chain {
|
||||
oc_none
|
||||
};
|
||||
|
||||
wasm_interface(vm_type vm, vm_oc_enable eosvmoc_tierup, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile);
|
||||
wasm_interface(vm_type vm, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile);
|
||||
~wasm_interface();
|
||||
|
||||
// initialize exec per thread
|
||||
@@ -61,19 +61,14 @@ namespace eosio { namespace chain {
|
||||
//indicate that a particular code probably won't be used after given block_num
|
||||
void code_block_num_last_used(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, const uint32_t& block_num);
|
||||
|
||||
//indicate the current LIB. evicts old cache entries
|
||||
void current_lib(const uint32_t lib);
|
||||
//indicate the current LIB. evicts old cache entries, each evicted entry is provided to callback
|
||||
void current_lib(const uint32_t lib, const std::function<void(const digest_type&, uint8_t)>& callback);
|
||||
|
||||
//Calls apply or error on a given code
|
||||
void apply(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, apply_context& context);
|
||||
|
||||
//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(
|
||||
const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, apply_context& context)> substitute_apply;
|
||||
private:
|
||||
unique_ptr<struct wasm_interface_impl> my;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
#include <eosio/chain/wasm_interface.hpp>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace eosio::chain {
|
||||
|
||||
@@ -8,50 +11,27 @@ namespace eosio::chain {
|
||||
*/
|
||||
class wasm_interface_collection {
|
||||
public:
|
||||
inline static bool test_disable_tierup = false; // set by unittests to test tierup failing
|
||||
|
||||
wasm_interface_collection(wasm_interface::vm_type vm, wasm_interface::vm_oc_enable eosvmoc_tierup,
|
||||
const chainbase::database& d, const std::filesystem::path& data_dir,
|
||||
const eosvmoc::config& eosvmoc_config, bool profile)
|
||||
: main_thread_id(std::this_thread::get_id())
|
||||
, wasm_runtime(vm)
|
||||
, eosvmoc_tierup(eosvmoc_tierup)
|
||||
, wasmif(vm, eosvmoc_tierup, d, data_dir, eosvmoc_config, profile)
|
||||
{}
|
||||
const eosvmoc::config& eosvmoc_config, bool profile);
|
||||
|
||||
wasm_interface& get_wasm_interface() {
|
||||
if (is_on_main_thread()
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
|| is_eos_vm_oc_enabled()
|
||||
#endif
|
||||
)
|
||||
return wasmif;
|
||||
return *threaded_wasmifs[std::this_thread::get_id()];
|
||||
~wasm_interface_collection();
|
||||
|
||||
void apply(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, apply_context& context);
|
||||
|
||||
// used for tests, only valid on main thread
|
||||
bool is_code_cached(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) {
|
||||
EOS_ASSERT(is_on_main_thread(), wasm_execution_error, "is_code_cached called off the main thread");
|
||||
return wasmif.is_code_cached(code_hash, vm_type, vm_version);
|
||||
}
|
||||
|
||||
// update current lib of all wasm interfaces
|
||||
void current_lib(const uint32_t lib) {
|
||||
// producer_plugin has already asserted irreversible_block signal is called in write window
|
||||
wasmif.current_lib(lib);
|
||||
for (auto& w: threaded_wasmifs) {
|
||||
w.second->current_lib(lib);
|
||||
}
|
||||
}
|
||||
void current_lib(const uint32_t lib);
|
||||
|
||||
// only called from non-main threads (read-only trx execution threads) when producer_plugin starts them
|
||||
void init_thread_local_data(const chainbase::database& d, const std::filesystem::path& data_dir,
|
||||
const eosvmoc::config& eosvmoc_config, bool profile) {
|
||||
EOS_ASSERT(!is_on_main_thread(), misc_exception, "init_thread_local_data called on the main thread");
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
if (is_eos_vm_oc_enabled()) {
|
||||
// EOSVMOC needs further initialization of its thread local data
|
||||
wasmif.init_thread_local_data();
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
std::lock_guard g(threaded_wasmifs_mtx);
|
||||
// Non-EOSVMOC needs a wasmif per thread
|
||||
threaded_wasmifs[std::this_thread::get_id()] = std::make_unique<wasm_interface>(wasm_runtime, eosvmoc_tierup, d, data_dir, eosvmoc_config, profile);
|
||||
}
|
||||
}
|
||||
void init_thread_local_data(const chainbase::database& d, const std::filesystem::path& data_dir, const eosvmoc::config& eosvmoc_config, bool profile);
|
||||
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
bool is_eos_vm_oc_enabled() const {
|
||||
@@ -59,15 +39,11 @@ namespace eosio::chain {
|
||||
}
|
||||
#endif
|
||||
|
||||
void code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num) {
|
||||
// The caller of this function apply_eosio_setcode has already asserted that
|
||||
// the transaction is not a read-only trx, which implies we are
|
||||
// in write window. Safe to call threaded_wasmifs's code_block_num_last_used
|
||||
wasmif.code_block_num_last_used(code_hash, vm_type, vm_version, block_num);
|
||||
for (auto& w: threaded_wasmifs) {
|
||||
w.second->code_block_num_last_used(code_hash, vm_type, vm_version, block_num);
|
||||
}
|
||||
}
|
||||
void code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num);
|
||||
|
||||
// If substitute_apply is set, then apply calls it before doing anything else. If substitute_apply returns true,
|
||||
// then apply returns immediately. Provided function must be multi-thread safe.
|
||||
std::function<bool(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, apply_context& context)> substitute_apply;
|
||||
|
||||
private:
|
||||
bool is_on_main_thread() { return main_thread_id == std::this_thread::get_id(); };
|
||||
@@ -77,9 +53,13 @@ namespace eosio::chain {
|
||||
const wasm_interface::vm_type wasm_runtime;
|
||||
const wasm_interface::vm_oc_enable eosvmoc_tierup;
|
||||
|
||||
wasm_interface wasmif; // used by main thread and all threads for EOSVMOC
|
||||
wasm_interface wasmif; // used by main thread
|
||||
std::mutex threaded_wasmifs_mtx;
|
||||
std::unordered_map<std::thread::id, std::unique_ptr<wasm_interface>> threaded_wasmifs; // one for each read-only thread, used by eos-vm and eos-vm-jit
|
||||
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
std::unique_ptr<struct eosvmoc_tier> eosvmoc; // used by all threads
|
||||
#endif
|
||||
};
|
||||
|
||||
} // eosio::chain
|
||||
|
||||
@@ -43,32 +43,10 @@ namespace eosio { namespace chain {
|
||||
struct by_hash;
|
||||
struct by_last_block_num;
|
||||
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
struct eosvmoc_tier {
|
||||
eosvmoc_tier(const std::filesystem::path& d, const eosvmoc::config& c, const chainbase::database& db)
|
||||
: cc(d, c, db) {
|
||||
// construct exec for the main thread
|
||||
init_thread_local_data();
|
||||
}
|
||||
|
||||
// Support multi-threaded execution.
|
||||
void init_thread_local_data() {
|
||||
exec = std::make_unique<eosvmoc::executor>(cc);
|
||||
}
|
||||
|
||||
eosvmoc::code_cache_async cc;
|
||||
|
||||
// Each thread requires its own exec and mem. Defined in wasm_interface.cpp
|
||||
thread_local static std::unique_ptr<eosvmoc::executor> exec;
|
||||
thread_local static eosvmoc::memory mem;
|
||||
};
|
||||
#endif
|
||||
|
||||
wasm_interface_impl(wasm_interface::vm_type vm, wasm_interface::vm_oc_enable eosvmoc_tierup, const chainbase::database& d,
|
||||
wasm_interface_impl(wasm_interface::vm_type vm, const chainbase::database& d,
|
||||
const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile)
|
||||
: db(d)
|
||||
, wasm_runtime_time(vm)
|
||||
, eosvmoc_tierup(eosvmoc_tierup)
|
||||
{
|
||||
#ifdef EOSIO_EOS_VM_RUNTIME_ENABLED
|
||||
if(vm == wasm_interface::vm_type::eos_vm)
|
||||
@@ -88,22 +66,9 @@ namespace eosio { namespace chain {
|
||||
#endif
|
||||
if(!runtime_interface)
|
||||
EOS_THROW(wasm_exception, "${r} wasm runtime not supported on this platform and/or configuration", ("r", vm));
|
||||
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
if(eosvmoc_tierup != wasm_interface::vm_oc_enable::oc_none) {
|
||||
EOS_ASSERT(vm != wasm_interface::vm_type::eos_vm_oc, wasm_exception, "You can't use EOS VM OC as the base runtime when tier up is activated");
|
||||
eosvmoc.emplace(data_dir, eosvmoc_config, d);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
~wasm_interface_impl() {
|
||||
if(is_shutting_down)
|
||||
for(wasm_cache_index::iterator it = wasm_instantiation_cache.begin(); it != wasm_instantiation_cache.end(); ++it)
|
||||
wasm_instantiation_cache.modify(it, [](wasm_cache_entry& e) {
|
||||
e.module.release()->fast_shutdown();
|
||||
});
|
||||
}
|
||||
~wasm_interface_impl() = default;
|
||||
|
||||
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) );
|
||||
@@ -118,14 +83,16 @@ namespace eosio { namespace chain {
|
||||
});
|
||||
}
|
||||
|
||||
void current_lib(uint32_t lib) {
|
||||
// reports each code_hash and vm_version that will be erased to callback
|
||||
void current_lib(uint32_t lib, const std::function<void(const digest_type&, uint8_t)>& callback) {
|
||||
//anything last used before or on the LIB can be evicted
|
||||
const auto first_it = wasm_instantiation_cache.get<by_last_block_num>().begin();
|
||||
const auto last_it = wasm_instantiation_cache.get<by_last_block_num>().upper_bound(lib);
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
if(eosvmoc) for(auto it = first_it; it != last_it; it++)
|
||||
eosvmoc->cc.free_code(it->code_hash, it->vm_version);
|
||||
#endif
|
||||
if (callback) {
|
||||
for(auto it = first_it; it != last_it; it++) {
|
||||
callback(it->code_hash, it->vm_version);
|
||||
}
|
||||
}
|
||||
wasm_instantiation_cache.get<by_last_block_num>().erase(first_it, last_it);
|
||||
}
|
||||
|
||||
@@ -162,7 +129,6 @@ namespace eosio { namespace chain {
|
||||
return it->module;
|
||||
}
|
||||
|
||||
bool is_shutting_down = false;
|
||||
std::unique_ptr<wasm_runtime_interface> runtime_interface;
|
||||
|
||||
typedef boost::multi_index_container<
|
||||
@@ -182,11 +148,6 @@ namespace eosio { namespace chain {
|
||||
|
||||
const chainbase::database& db;
|
||||
const wasm_interface::vm_type wasm_runtime_time;
|
||||
const wasm_interface::vm_oc_enable eosvmoc_tierup;
|
||||
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
std::optional<eosvmoc_tier> eosvmoc;
|
||||
#endif
|
||||
};
|
||||
|
||||
} } // eosio::chain
|
||||
|
||||
@@ -13,7 +13,6 @@ class apply_context;
|
||||
class wasm_instantiated_module_interface {
|
||||
public:
|
||||
virtual void apply(apply_context& context) = 0;
|
||||
virtual void fast_shutdown() {}
|
||||
|
||||
virtual ~wasm_instantiated_module_interface();
|
||||
};
|
||||
|
||||
@@ -46,11 +46,13 @@ namespace eosio { namespace chain {
|
||||
|
||||
transaction_context::transaction_context( controller& c,
|
||||
const packed_transaction& t,
|
||||
const transaction_id_type& trx_id,
|
||||
transaction_checktime_timer&& tmr,
|
||||
fc::time_point s,
|
||||
transaction_metadata::trx_type type)
|
||||
:control(c)
|
||||
,packed_trx(t)
|
||||
,id(trx_id)
|
||||
,undo_session()
|
||||
,trace(std::make_shared<transaction_trace>())
|
||||
,start(s)
|
||||
@@ -62,7 +64,7 @@ namespace eosio { namespace chain {
|
||||
if (!c.skip_db_sessions() && !is_read_only()) {
|
||||
undo_session.emplace(c.mutable_db().start_undo_session(true));
|
||||
}
|
||||
trace->id = packed_trx.id();
|
||||
trace->id = id;
|
||||
trace->block_num = c.head_block_num() + 1;
|
||||
trace->block_time = c.pending_block_time();
|
||||
trace->producer_block_id = c.pending_producer_block_id();
|
||||
@@ -295,7 +297,7 @@ namespace eosio { namespace chain {
|
||||
|
||||
init( initial_net_usage );
|
||||
if ( !is_read_only() ) {
|
||||
record_transaction( packed_trx.id(), trx.expiration );
|
||||
record_transaction( id, trx.expiration );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -753,7 +755,7 @@ namespace eosio { namespace chain {
|
||||
|
||||
uint32_t trx_size = 0;
|
||||
const auto& cgto = control.mutable_db().create<generated_transaction_object>( [&]( auto& gto ) {
|
||||
gto.trx_id = packed_trx.id();
|
||||
gto.trx_id = id;
|
||||
gto.payer = first_auth;
|
||||
gto.sender = account_name(); /// delayed transactions have no sender
|
||||
gto.sender_id = transaction_id_to_sender_id( gto.trx_id );
|
||||
|
||||
@@ -32,16 +32,14 @@
|
||||
|
||||
namespace eosio { namespace chain {
|
||||
|
||||
wasm_interface::wasm_interface(vm_type vm, vm_oc_enable eosvmoc_tierup, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile)
|
||||
: my( new wasm_interface_impl(vm, eosvmoc_tierup, d, data_dir, eosvmoc_config, profile) ) {}
|
||||
wasm_interface::wasm_interface(vm_type vm, const chainbase::database& d, const std::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile)
|
||||
: my( new wasm_interface_impl(vm, d, data_dir, eosvmoc_config, profile) ) {}
|
||||
|
||||
wasm_interface::~wasm_interface() {}
|
||||
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
void wasm_interface::init_thread_local_data() {
|
||||
if (my->eosvmoc)
|
||||
my->eosvmoc->init_thread_local_data();
|
||||
else if (my->wasm_runtime_time == wasm_interface::vm_type::eos_vm_oc && my->runtime_interface)
|
||||
if (my->wasm_runtime_time == wasm_interface::vm_type::eos_vm_oc && my->runtime_interface)
|
||||
my->runtime_interface->init_thread_local_data();
|
||||
}
|
||||
#endif
|
||||
@@ -72,54 +70,17 @@ namespace eosio { namespace chain {
|
||||
//there are a couple opportunties for improvement here--
|
||||
//Easy: Cache the Module created here so it can be reused for instantiaion
|
||||
//Hard: Kick off instantiation in a separate thread at this location
|
||||
}
|
||||
|
||||
void wasm_interface::indicate_shutting_down() {
|
||||
my->is_shutting_down = true;
|
||||
}
|
||||
|
||||
void wasm_interface::code_block_num_last_used(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, const uint32_t& block_num) {
|
||||
my->code_block_num_last_used(code_hash, vm_type, vm_version, block_num);
|
||||
}
|
||||
|
||||
void wasm_interface::current_lib(const uint32_t lib) {
|
||||
my->current_lib(lib);
|
||||
void wasm_interface::current_lib(const uint32_t lib, const std::function<void(const digest_type&, uint8_t)>& callback) {
|
||||
my->current_lib(lib, callback);
|
||||
}
|
||||
|
||||
void wasm_interface::apply( const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, apply_context& context ) {
|
||||
if(substitute_apply && substitute_apply(code_hash, vm_type, vm_version, context))
|
||||
return;
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
if(my->eosvmoc && (my->eosvmoc_tierup == wasm_interface::vm_oc_enable::oc_all || context.should_use_eos_vm_oc())) {
|
||||
const chain::eosvmoc::code_descriptor* cd = nullptr;
|
||||
chain::eosvmoc::code_cache_base::get_cd_failure failure = chain::eosvmoc::code_cache_base::get_cd_failure::temporary;
|
||||
try {
|
||||
const bool high_priority = context.get_receiver().prefix() == chain::config::system_account_name;
|
||||
cd = my->eosvmoc->cc.get_descriptor_for_code(high_priority, code_hash, vm_version, context.control.is_write_window(), failure);
|
||||
}
|
||||
catch(...) {
|
||||
//swallow errors here, if EOS VM OC has gone in to the weeds we shouldn't bail: continue to try and run baseline
|
||||
//In the future, consider moving bits of EOS VM that can fire exceptions and such out of this call path
|
||||
static bool once_is_enough;
|
||||
if(!once_is_enough)
|
||||
elog("EOS VM OC has encountered an unexpected failure");
|
||||
once_is_enough = true;
|
||||
}
|
||||
if(cd) {
|
||||
if (!context.is_applying_block()) // read_only_trx_test.py looks for this log statement
|
||||
tlog("${a} speculatively executing ${h} with eos vm oc", ("a", context.get_receiver())("h", code_hash));
|
||||
my->eosvmoc->exec->execute(*cd, my->eosvmoc->mem, context);
|
||||
return;
|
||||
}
|
||||
else if (context.trx_context.is_read_only()) {
|
||||
if (failure == chain::eosvmoc::code_cache_base::get_cd_failure::temporary) {
|
||||
EOS_ASSERT(false, ro_trx_vm_oc_compile_temporary_failure, "get_descriptor_for_code failed with temporary failure");
|
||||
} else {
|
||||
EOS_ASSERT(false, ro_trx_vm_oc_compile_permanent_failure, "get_descriptor_for_code failed with permanent failure");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
my->get_instantiated_module(code_hash, vm_type, vm_version, context.trx_context)->apply(context);
|
||||
}
|
||||
|
||||
@@ -127,13 +88,8 @@ namespace eosio { namespace chain {
|
||||
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() {}
|
||||
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
thread_local std::unique_ptr<eosvmoc::executor> wasm_interface_impl::eosvmoc_tier::exec {};
|
||||
thread_local eosvmoc::memory wasm_interface_impl::eosvmoc_tier::mem{ wasm_constraints::maximum_linear_memory/wasm_constraints::wasm_page_size };
|
||||
#endif
|
||||
wasm_instantiated_module_interface::~wasm_instantiated_module_interface() = default;
|
||||
wasm_runtime_interface::~wasm_runtime_interface() = default;
|
||||
|
||||
std::istream& operator>>(std::istream& in, wasm_interface::vm_type& runtime) {
|
||||
std::string s;
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
#include <eosio/chain/wasm_interface_collection.hpp>
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
#include <eosio/chain/webassembly/eos-vm-oc.hpp>
|
||||
#else
|
||||
#define _REGISTER_EOSVMOC_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG)
|
||||
#endif
|
||||
|
||||
namespace eosio::chain {
|
||||
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
struct eosvmoc_tier {
|
||||
eosvmoc_tier(const std::filesystem::path& d, const eosvmoc::config& c, const chainbase::database& db)
|
||||
: cc(d, c, db) {
|
||||
// construct exec for the main thread
|
||||
init_thread_local_data();
|
||||
}
|
||||
|
||||
// Support multi-threaded execution.
|
||||
void init_thread_local_data() {
|
||||
exec = std::make_unique<eosvmoc::executor>(cc);
|
||||
}
|
||||
|
||||
eosvmoc::code_cache_async cc;
|
||||
|
||||
// Each thread requires its own exec and mem.
|
||||
thread_local static std::unique_ptr<eosvmoc::executor> exec;
|
||||
thread_local static eosvmoc::memory mem;
|
||||
};
|
||||
|
||||
thread_local std::unique_ptr<eosvmoc::executor> eosvmoc_tier::exec{};
|
||||
thread_local eosvmoc::memory eosvmoc_tier::mem{wasm_constraints::maximum_linear_memory / wasm_constraints::wasm_page_size};
|
||||
#endif
|
||||
|
||||
wasm_interface_collection::wasm_interface_collection(wasm_interface::vm_type vm, wasm_interface::vm_oc_enable eosvmoc_tierup,
|
||||
const chainbase::database& d, const std::filesystem::path& data_dir,
|
||||
const eosvmoc::config& eosvmoc_config, bool profile)
|
||||
: main_thread_id(std::this_thread::get_id())
|
||||
, wasm_runtime(vm)
|
||||
, eosvmoc_tierup(eosvmoc_tierup)
|
||||
, wasmif(vm, d, data_dir, eosvmoc_config, profile) {
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
if (eosvmoc_tierup != wasm_interface::vm_oc_enable::oc_none) {
|
||||
EOS_ASSERT(vm != wasm_interface::vm_type::eos_vm_oc, wasm_exception, "You can't use EOS VM OC as the base runtime when tier up is activated");
|
||||
eosvmoc = std::make_unique<eosvmoc_tier>(data_dir, eosvmoc_config, d);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
wasm_interface_collection::~wasm_interface_collection() = default;
|
||||
|
||||
void wasm_interface_collection::apply(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, apply_context& context) {
|
||||
if (substitute_apply && substitute_apply(code_hash, vm_type, vm_version, context))
|
||||
return;
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
if (eosvmoc && (eosvmoc_tierup == wasm_interface::vm_oc_enable::oc_all || context.should_use_eos_vm_oc())) {
|
||||
const chain::eosvmoc::code_descriptor* cd = nullptr;
|
||||
chain::eosvmoc::code_cache_base::get_cd_failure failure = chain::eosvmoc::code_cache_base::get_cd_failure::temporary;
|
||||
try {
|
||||
const bool high_priority = context.get_receiver().prefix() == chain::config::system_account_name;
|
||||
cd = eosvmoc->cc.get_descriptor_for_code(high_priority, code_hash, vm_version, context.control.is_write_window(), failure);
|
||||
if (test_disable_tierup)
|
||||
cd = nullptr;
|
||||
} catch (...) {
|
||||
// swallow errors here, if EOS VM OC has gone in to the weeds we shouldn't bail: continue to try and run baseline
|
||||
// In the future, consider moving bits of EOS VM that can fire exceptions and such out of this call path
|
||||
static bool once_is_enough;
|
||||
if (!once_is_enough)
|
||||
elog("EOS VM OC has encountered an unexpected failure");
|
||||
once_is_enough = true;
|
||||
}
|
||||
if (cd) {
|
||||
if (!context.is_applying_block()) // read_only_trx_test.py looks for this log statement
|
||||
tlog("${a} speculatively executing ${h} with eos vm oc", ("a", context.get_receiver())("h", code_hash));
|
||||
eosvmoc->exec->execute(*cd, eosvmoc->mem, context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (is_on_main_thread()) {
|
||||
wasmif.apply(code_hash, vm_type, vm_version, context);
|
||||
return;
|
||||
}
|
||||
threaded_wasmifs[std::this_thread::get_id()]->apply(code_hash, vm_type, vm_version, context);
|
||||
}
|
||||
|
||||
// update current lib of all wasm interfaces
|
||||
void wasm_interface_collection::current_lib(const uint32_t lib) {
|
||||
// producer_plugin has already asserted irreversible_block signal is called in write window
|
||||
std::function<void(const digest_type& code_hash, uint8_t vm_version)> cb{};
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
if (eosvmoc) {
|
||||
cb = [&](const digest_type& code_hash, uint8_t vm_version) {
|
||||
eosvmoc->cc.free_code(code_hash, vm_version);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
wasmif.current_lib(lib, cb);
|
||||
for (auto& w : threaded_wasmifs) {
|
||||
w.second->current_lib(lib, cb);
|
||||
}
|
||||
}
|
||||
|
||||
// only called from non-main threads (read-only trx execution threads) when producer_plugin starts them
|
||||
void wasm_interface_collection::init_thread_local_data(const chainbase::database& d, const std::filesystem::path& data_dir,
|
||||
const eosvmoc::config& eosvmoc_config, bool profile) {
|
||||
EOS_ASSERT(!is_on_main_thread(), misc_exception, "init_thread_local_data called on the main thread");
|
||||
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
|
||||
if (is_eos_vm_oc_enabled()) {
|
||||
// EOSVMOC needs further initialization of its thread local data
|
||||
if (eosvmoc)
|
||||
eosvmoc->init_thread_local_data();
|
||||
wasmif.init_thread_local_data();
|
||||
}
|
||||
#endif
|
||||
|
||||
std::lock_guard g(threaded_wasmifs_mtx);
|
||||
// Non-EOSVMOC needs a wasmif per thread
|
||||
threaded_wasmifs[std::this_thread::get_id()] = std::make_unique<wasm_interface>(wasm_runtime, d, data_dir, eosvmoc_config, profile);
|
||||
}
|
||||
|
||||
void wasm_interface_collection::code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num) {
|
||||
// The caller of this function apply_eosio_setcode has already asserted that
|
||||
// the transaction is not a read-only trx, which implies we are
|
||||
// in write window. Safe to call threaded_wasmifs's code_block_num_last_used
|
||||
wasmif.code_block_num_last_used(code_hash, vm_type, vm_version, block_num);
|
||||
for (auto& w : threaded_wasmifs) {
|
||||
w.second->code_block_num_last_used(code_hash, vm_type, vm_version, block_num);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace eosio::chain
|
||||
@@ -201,10 +201,6 @@ class eos_vm_profiling_module : public wasm_instantiated_module_interface {
|
||||
}
|
||||
}
|
||||
|
||||
void fast_shutdown() override {
|
||||
_prof.clear();
|
||||
}
|
||||
|
||||
profile_data* start(apply_context& context) {
|
||||
name account = context.get_receiver();
|
||||
if(!context.control.is_profiling(account)) return nullptr;
|
||||
@@ -242,7 +238,7 @@ std::unique_ptr<wasm_instantiated_module_interface> eos_vm_runtime<Impl>::instan
|
||||
wasm_code_ptr code((uint8_t*)code_bytes, code_size);
|
||||
apply_options options = { .max_pages = 65536,
|
||||
.max_call_depth = 0 };
|
||||
std::unique_ptr<backend_t> bkend = std::make_unique<backend_t>(code, code_size, nullptr, options);
|
||||
std::unique_ptr<backend_t> bkend = std::make_unique<backend_t>(code, code_size, nullptr, options, false); // uses 2-passes parsing
|
||||
eos_vm_host_functions_t::resolve(bkend->get_module());
|
||||
return std::make_unique<eos_vm_instantiated_module<Impl>>(this, std::move(bkend));
|
||||
} catch(eosio::vm::exception& e) {
|
||||
@@ -264,7 +260,7 @@ std::unique_ptr<wasm_instantiated_module_interface> eos_vm_profile_runtime::inst
|
||||
wasm_code_ptr code((uint8_t*)code_bytes, code_size);
|
||||
apply_options options = { .max_pages = 65536,
|
||||
.max_call_depth = 0 };
|
||||
std::unique_ptr<backend_t> bkend = std::make_unique<backend_t>(code, code_size, nullptr, options);
|
||||
std::unique_ptr<backend_t> bkend = std::make_unique<backend_t>(code, code_size, nullptr, options, false); // uses 2-passes parsing
|
||||
eos_vm_host_functions_t::resolve(bkend->get_module());
|
||||
return std::make_unique<eos_vm_profiling_module>(std::move(bkend), code_bytes, code_size);
|
||||
} catch(eosio::vm::exception& e) {
|
||||
|
||||
+1
-1
Submodule libraries/chainbase updated: 0cc3c62aa6...bffb7ebde6
+1
-1
Submodule libraries/eos-vm updated: 329db27d88...1e9345f96a
@@ -78,12 +78,6 @@ if(APPLE)
|
||||
add_library(zstd INTERFACE)
|
||||
endif()
|
||||
|
||||
find_package(Boost 1.66 REQUIRED COMPONENTS
|
||||
date_time
|
||||
chrono
|
||||
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})
|
||||
@@ -117,7 +111,8 @@ if(APPLE)
|
||||
find_library(security_framework Security)
|
||||
find_library(corefoundation_framework CoreFoundation)
|
||||
endif()
|
||||
target_link_libraries( fc PUBLIC Boost::date_time Boost::chrono Boost::iostreams Threads::Threads
|
||||
target_link_libraries( fc PUBLIC Boost::date_time Boost::chrono Boost::iostreams Boost::interprocess Boost::multi_index Boost::dll
|
||||
Boost::multiprecision Boost::beast Boost::asio Boost::thread Boost::unit_test_framework Threads::Threads
|
||||
OpenSSL::Crypto 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
|
||||
|
||||
@@ -12,7 +12,8 @@ namespace fc {
|
||||
class microseconds {
|
||||
public:
|
||||
constexpr explicit microseconds( int64_t c = 0) :_count(c){}
|
||||
static constexpr microseconds maximum() { return microseconds(0x7fffffffffffffffll); }
|
||||
static constexpr microseconds maximum() { return microseconds(std::numeric_limits<int64_t>::max()); }
|
||||
static constexpr microseconds minimum() { return microseconds(std::numeric_limits<int64_t>::min()); }
|
||||
friend constexpr microseconds operator + (const microseconds& l, const microseconds& r ) { return microseconds(l._count+r._count); }
|
||||
friend constexpr microseconds operator - (const microseconds& l, const microseconds& r ) { return microseconds(l._count-r._count); }
|
||||
|
||||
@@ -52,12 +53,12 @@ namespace fc {
|
||||
|
||||
// protect against overflow/underflow
|
||||
constexpr time_point& safe_add( const microseconds& m ) {
|
||||
if (m.count() > 0 && elapsed > fc::microseconds::maximum() - m) {
|
||||
if (m.count() > 0 && elapsed > microseconds::maximum() - m) {
|
||||
elapsed = microseconds::maximum();
|
||||
} else if (m.count() < 0 && elapsed.count() < std::numeric_limits<int64_t >::min() - m.count()) {
|
||||
elapsed = microseconds(std::numeric_limits<int64_t >::min());
|
||||
} else if (m.count() < 0 && elapsed < microseconds::minimum() - m) {
|
||||
elapsed = microseconds::minimum();
|
||||
} else {
|
||||
elapsed += m;
|
||||
elapsed += m;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -94,7 +95,7 @@ namespace fc {
|
||||
constexpr explicit time_point_sec( const time_point& t )
|
||||
:utc_seconds( t.time_since_epoch().count() / 1000000ll ){}
|
||||
|
||||
static constexpr time_point_sec maximum() { return time_point_sec(0xffffffff); }
|
||||
static constexpr time_point_sec maximum() { return time_point_sec(std::numeric_limits<uint32_t>::max()); }
|
||||
static constexpr time_point_sec min() { return time_point_sec(0); }
|
||||
|
||||
constexpr time_point to_time_point()const { return time_point( fc::seconds( utc_seconds) ); }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <eosio/testing/tester.hpp>
|
||||
#include <eosio/chain/wasm_interface_collection.hpp>
|
||||
#include <eosio/chain/block_log.hpp>
|
||||
#include <eosio/chain/wast_to_wasm.hpp>
|
||||
#include <eosio/chain/eosio_contract.hpp>
|
||||
|
||||
+8
-4
@@ -46,13 +46,17 @@ set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/AntelopeIO/leap")
|
||||
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
set(CPACK_DEBIAN_BASE_PACKAGE_SECTION "utils")
|
||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.22)
|
||||
set(CPACK_DEBIAN_COMPRESSION_TYPE "zstd")
|
||||
endif()
|
||||
|
||||
set(CPACK_DEBIAN_PACKAGE_CONFLICTS "eosio, mandel")
|
||||
set(CPACK_RPM_PACKAGE_CONFLICTS "eosio, mandel")
|
||||
|
||||
#only consider "base" and "dev" components for per-component packages
|
||||
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
|
||||
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified")
|
||||
set(CPACK_COMPONENTS_ALL "base")
|
||||
if(ENABLE_LEAP_DEV_DEB)
|
||||
list(APPEND CPACK_COMPONENTS_ALL "dev")
|
||||
endif()
|
||||
|
||||
#enable per component packages for .deb; ensure main package is just "leap", not "leap-base", and make the dev package have "leap-dev" at the front not the back
|
||||
set(CPACK_DEB_COMPONENT_INSTALL ON)
|
||||
@@ -61,7 +65,7 @@ set(CPACK_DEBIAN_BASE_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.deb")
|
||||
string(REGEX REPLACE "^(${CMAKE_PROJECT_NAME})" "\\1-dev" CPACK_DEBIAN_DEV_FILE_NAME "${CPACK_DEBIAN_BASE_FILE_NAME}")
|
||||
|
||||
#deb package tooling will be unable to detect deps for the dev package. llvm is tricky since we don't know what package could have been used; try to figure it out
|
||||
set(CPACK_DEBIAN_DEV_PACKAGE_DEPENDS "libboost-all-dev, libssl-dev, libgmp-dev, python3-numpy")
|
||||
set(CPACK_DEBIAN_DEV_PACKAGE_DEPENDS "libssl-dev, libgmp-dev, python3-distutils, python3-numpy, zlib1g-dev")
|
||||
find_program(DPKG_QUERY "dpkg-query")
|
||||
if(DPKG_QUERY AND OS_RELEASE MATCHES "\n?ID=\"?ubuntu" AND LLVM_CMAKE_DIR)
|
||||
execute_process(COMMAND "${DPKG_QUERY}" -S "${LLVM_CMAKE_DIR}" COMMAND cut -d: -f1 RESULT_VARIABLE LLVM_PKG_FIND_RESULT OUTPUT_VARIABLE LLVM_PKG_FIND_OUTPUT)
|
||||
|
||||
@@ -11,7 +11,7 @@ if(EOSIO_ENABLE_DEVELOPER_OPTIONS)
|
||||
target_compile_definitions(chain_plugin PUBLIC EOSIO_DEVELOPER)
|
||||
endif()
|
||||
|
||||
target_link_libraries( chain_plugin eosio_chain custom_appbase appbase resource_monitor_plugin )
|
||||
target_link_libraries( chain_plugin eosio_chain custom_appbase appbase resource_monitor_plugin Boost::bimap )
|
||||
target_include_directories( chain_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../chain_interface/include" "${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/appbase/include" "${CMAKE_CURRENT_SOURCE_DIR}/../resource_monitor_plugin/include")
|
||||
|
||||
add_subdirectory( test )
|
||||
add_subdirectory( test )
|
||||
|
||||
@@ -327,7 +327,7 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip
|
||||
"In \"head\" mode: database contains state changes up to the head block; transactions received by the node are relayed if valid.\n"
|
||||
"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.\n"
|
||||
"In \"speculative\" mode: (DEPRECATED: head mode recommended) database contains state changes by transactions in the blockchain "
|
||||
"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; transactions received by the node are relayed if valid.\n"
|
||||
)
|
||||
( "api-accept-transactions", bpo::value<bool>()->default_value(true), "Allow API transactions to be evaluated and relayed if valid.")
|
||||
@@ -1177,8 +1177,6 @@ void chain_plugin_impl::plugin_shutdown() {
|
||||
accepted_transaction_connection.reset();
|
||||
applied_transaction_connection.reset();
|
||||
block_start_connection.reset();
|
||||
if(app().is_quiting())
|
||||
chain->get_wasm_interface().indicate_shutting_down();
|
||||
chain.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -224,11 +224,15 @@ BOOST_AUTO_TEST_CASE(trx_retry_logic) {
|
||||
std::promise<chain_plugin*> plugin_promise;
|
||||
std::future<chain_plugin*> plugin_fut = plugin_promise.get_future();
|
||||
std::thread app_thread( [&]() {
|
||||
std::vector<const char*> argv = {"test"};
|
||||
app->initialize( argv.size(), (char**) &argv[0] );
|
||||
app->startup();
|
||||
plugin_promise.set_value(app->find_plugin<chain_plugin>());
|
||||
app->exec();
|
||||
try {
|
||||
std::vector<const char*> argv = {"test"};
|
||||
app->initialize(argv.size(), (char**)&argv[0]);
|
||||
app->startup();
|
||||
plugin_promise.set_value(app->find_plugin<chain_plugin>());
|
||||
app->exec();
|
||||
return;
|
||||
} FC_LOG_AND_DROP()
|
||||
BOOST_CHECK(!"app threw exception see logged error");
|
||||
} );
|
||||
(void)plugin_fut.get(); // wait for app to be started
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ class bp_connection_manager {
|
||||
|
||||
fc_dlog(self()->get_logger(), "pending_downstream_neighbors: ${pending_downstream_neighbors}",
|
||||
("pending_downstream_neighbors", to_string(pending_downstream_neighbors)));
|
||||
for (auto neighbor : pending_downstream_neighbors) { self()->connections.connect(config.bp_peer_addresses[neighbor]); }
|
||||
for (auto neighbor : pending_downstream_neighbors) { self()->connections.connect(config.bp_peer_addresses[neighbor], *self()->p2p_addresses.begin() ); }
|
||||
|
||||
pending_neighbors = std::move(pending_downstream_neighbors);
|
||||
finder.add_upstream_neighbors(pending_neighbors);
|
||||
|
||||
@@ -9,9 +9,14 @@ namespace eosio {
|
||||
|
||||
struct connection_status {
|
||||
string peer;
|
||||
bool connecting = false;
|
||||
bool syncing = false;
|
||||
bool is_bp_peer = false;
|
||||
string remote_ip;
|
||||
string remote_port;
|
||||
bool connecting = false;
|
||||
bool syncing = false;
|
||||
bool is_bp_peer = false;
|
||||
bool is_socket_open = false;
|
||||
bool is_blocks_only = false;
|
||||
bool is_transactions_only = false;
|
||||
handshake_message last_handshake;
|
||||
};
|
||||
|
||||
@@ -49,4 +54,4 @@ namespace eosio {
|
||||
|
||||
}
|
||||
|
||||
FC_REFLECT( eosio::connection_status, (peer)(connecting)(syncing)(is_bp_peer)(last_handshake) )
|
||||
FC_REFLECT( eosio::connection_status, (peer)(remote_ip)(remote_port)(connecting)(syncing)(is_bp_peer)(is_socket_open)(is_blocks_only)(is_transactions_only)(last_handshake) )
|
||||
|
||||
@@ -42,6 +42,21 @@
|
||||
|
||||
using namespace eosio::chain::plugin_interface;
|
||||
|
||||
namespace boost
|
||||
{
|
||||
/// @brief Overload for boost::lexical_cast to convert vector of strings to string
|
||||
///
|
||||
/// Used by boost::program_options to print the default value of an std::vector<std::string> option
|
||||
///
|
||||
/// @param v the vector to convert
|
||||
/// @return the contents of the vector as a comma-separated string
|
||||
template<>
|
||||
inline std::string lexical_cast<std::string>(const std::vector<std::string>& v)
|
||||
{
|
||||
return boost::join(v, ",");
|
||||
}
|
||||
}
|
||||
|
||||
namespace eosio {
|
||||
static auto _net_plugin = application::register_plugin<net_plugin>();
|
||||
|
||||
@@ -335,7 +350,7 @@ namespace eosio {
|
||||
private: // must call with held mutex
|
||||
connection_ptr find_connection_i(const string& host) const;
|
||||
void add_i(connection_ptr&& c);
|
||||
void connect_i(const string& peer);
|
||||
void connect_i(const string& peer, const string& p2p_address);
|
||||
|
||||
void connection_monitor(const std::weak_ptr<connection>& from_connection);
|
||||
|
||||
@@ -355,14 +370,14 @@ namespace eosio {
|
||||
|
||||
void register_update_p2p_connection_metrics(std::function<void(net_plugin::p2p_connections_metrics)>&& fun);
|
||||
|
||||
void connect_supplied_peers();
|
||||
void connect_supplied_peers(const string& p2p_address);
|
||||
|
||||
void start_conn_timer();
|
||||
void start_conn_timer(boost::asio::steady_timer::duration du, std::weak_ptr<connection> from_connection);
|
||||
void stop_conn_timer();
|
||||
|
||||
void add(connection_ptr c);
|
||||
string connect(const string& host);
|
||||
string connect(const string& host, const string& p2p_address);
|
||||
string disconnect(const string& host);
|
||||
void close_all();
|
||||
|
||||
@@ -395,8 +410,8 @@ namespace eosio {
|
||||
* Thread safe, only updated in plugin initialize
|
||||
* @{
|
||||
*/
|
||||
string p2p_address;
|
||||
string p2p_server_address;
|
||||
vector<string> p2p_addresses;
|
||||
vector<string> p2p_server_addresses;
|
||||
|
||||
vector<chain::public_key_type> allowed_peers; ///< peer keys allowed to connect
|
||||
std::map<chain::public_key_type,
|
||||
@@ -518,7 +533,7 @@ namespace eosio {
|
||||
bool in_sync() const;
|
||||
fc::logger& get_logger() { return logger; }
|
||||
|
||||
void create_session(tcp::socket&& socket);
|
||||
void create_session(tcp::socket&& socket, const string listen_address);
|
||||
};
|
||||
|
||||
// peer_[x]log must be called from thread in connection strand
|
||||
@@ -750,8 +765,11 @@ namespace eosio {
|
||||
public:
|
||||
enum class connection_state { connecting, connected, closing, closed };
|
||||
|
||||
explicit connection( const string& endpoint );
|
||||
explicit connection( tcp::socket&& socket );
|
||||
explicit connection( const string& endpoint, const string& listen_address );
|
||||
/// @brief ctor
|
||||
/// @param socket created by boost::asio in fc::listener
|
||||
/// @param address identifier of listen socket which accepted this new connection
|
||||
explicit connection( tcp::socket&& socket, const string& listen_address );
|
||||
~connection() = default;
|
||||
|
||||
connection( const connection& ) = delete;
|
||||
@@ -790,6 +808,7 @@ namespace eosio {
|
||||
|
||||
std::atomic<connection_state> conn_state{connection_state::connecting};
|
||||
|
||||
string listen_address; // address sent to peer in handshake
|
||||
const string peer_addr;
|
||||
enum connection_types : char {
|
||||
both,
|
||||
@@ -1004,7 +1023,7 @@ namespace eosio {
|
||||
return mvo;
|
||||
}
|
||||
|
||||
bool incoming() const { return peer_address().empty(); } // thread safe becuase of peer_address
|
||||
bool incoming() const { return peer_address().empty(); } // thread safe because of peer_address
|
||||
bool incoming_and_handshake_received() const {
|
||||
if (!incoming()) return false;
|
||||
fc::lock_guard g_conn( conn_mtx );
|
||||
@@ -1127,8 +1146,9 @@ namespace eosio {
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
connection::connection( const string& endpoint )
|
||||
: peer_addr( endpoint ),
|
||||
connection::connection( const string& endpoint, const string& listen_address )
|
||||
: listen_address( listen_address ),
|
||||
peer_addr( endpoint ),
|
||||
strand( my_impl->thread_pool.get_executor() ),
|
||||
socket( new tcp::socket( my_impl->thread_pool.get_executor() ) ),
|
||||
log_p2p_address( endpoint ),
|
||||
@@ -1141,8 +1161,9 @@ namespace eosio {
|
||||
fc_ilog( logger, "created connection ${c} to ${n}", ("c", connection_id)("n", endpoint) );
|
||||
}
|
||||
|
||||
connection::connection(tcp::socket&& s)
|
||||
: peer_addr(),
|
||||
connection::connection(tcp::socket&& s, const string& listen_address)
|
||||
: listen_address( listen_address ),
|
||||
peer_addr(),
|
||||
strand( my_impl->thread_pool.get_executor() ),
|
||||
socket( new tcp::socket( std::move(s) ) ),
|
||||
connection_id( ++my_impl->current_connection_id ),
|
||||
@@ -1150,7 +1171,8 @@ namespace eosio {
|
||||
last_handshake_recv(),
|
||||
last_handshake_sent()
|
||||
{
|
||||
fc_dlog( logger, "new connection object created" );
|
||||
update_endpoints();
|
||||
fc_dlog( logger, "new connection object created for peer ${address}:${port} from listener ${addr}", ("address", log_remote_endpoint_ip)("port", log_remote_endpoint_port)("addr", listen_address) );
|
||||
}
|
||||
|
||||
// called from connection strand
|
||||
@@ -1212,9 +1234,12 @@ namespace eosio {
|
||||
connection_status connection::get_status()const {
|
||||
connection_status stat;
|
||||
stat.peer = peer_addr;
|
||||
stat.remote_ip = log_remote_endpoint_ip;
|
||||
stat.remote_port = log_remote_endpoint_port;
|
||||
stat.connecting = state() == connection_state::connecting;
|
||||
stat.syncing = peer_syncing_from_us;
|
||||
stat.is_bp_peer = is_bp_connection;
|
||||
stat.is_socket_open = socket_is_open();
|
||||
fc::lock_guard g( conn_mtx );
|
||||
stat.last_handshake = last_handshake_recv;
|
||||
return stat;
|
||||
@@ -1224,7 +1249,6 @@ namespace eosio {
|
||||
bool connection::start_session() {
|
||||
verify_strand_in_this_thread( strand, __func__, __LINE__ );
|
||||
|
||||
update_endpoints();
|
||||
boost::asio::ip::tcp::no_delay nodelay( true );
|
||||
boost::system::error_code ec;
|
||||
socket->set_option( nodelay, ec );
|
||||
@@ -1289,10 +1313,8 @@ namespace eosio {
|
||||
void connection::_close( bool reconnect, bool shutdown ) {
|
||||
socket_open = false;
|
||||
boost::system::error_code ec;
|
||||
if( socket->is_open() ) {
|
||||
socket->shutdown( tcp::socket::shutdown_both, ec );
|
||||
socket->close( ec );
|
||||
}
|
||||
socket->shutdown( tcp::socket::shutdown_both, ec );
|
||||
socket->close( ec );
|
||||
socket.reset( new tcp::socket( my_impl->thread_pool.get_executor() ) );
|
||||
flush_queues();
|
||||
peer_syncing_from_us = false;
|
||||
@@ -1518,11 +1540,18 @@ namespace eosio {
|
||||
try {
|
||||
c->buffer_queue.clear_out_queue();
|
||||
// May have closed connection and cleared buffer_queue
|
||||
if( !c->socket_is_open() || socket != c->socket ) {
|
||||
peer_ilog( c, "async write socket ${r} before callback", ("r", c->socket_is_open() ? "changed" : "closed") );
|
||||
if (!c->socket->is_open() && c->socket_is_open()) { // if socket_open then close not called
|
||||
peer_ilog(c, "async write socket closed before callback");
|
||||
c->close();
|
||||
return;
|
||||
}
|
||||
if (socket != c->socket ) { // different socket, c must have created a new socket, make sure previous is closed
|
||||
peer_ilog( c, "async write socket changed before callback");
|
||||
boost::system::error_code ec;
|
||||
socket->shutdown( tcp::socket::shutdown_both, ec );
|
||||
socket->close( ec );
|
||||
return;
|
||||
}
|
||||
|
||||
if( ec ) {
|
||||
if( ec.value() != boost::asio::error::eof ) {
|
||||
@@ -2650,7 +2679,7 @@ namespace eosio {
|
||||
}
|
||||
|
||||
|
||||
void net_plugin_impl::create_session(tcp::socket&& socket) {
|
||||
void net_plugin_impl::create_session(tcp::socket&& socket, const string listen_address) {
|
||||
uint32_t visitors = 0;
|
||||
uint32_t from_addr = 0;
|
||||
boost::system::error_code rec;
|
||||
@@ -2676,7 +2705,7 @@ namespace eosio {
|
||||
visitors < connections.get_max_client_count())) {
|
||||
fc_ilog(logger, "Accepted new connection: " + paddr_str);
|
||||
|
||||
connection_ptr new_connection = std::make_shared<connection>(std::move(socket));
|
||||
connection_ptr new_connection = std::make_shared<connection>(std::move(socket), listen_address);
|
||||
new_connection->strand.post([new_connection, this]() {
|
||||
if (new_connection->start_session()) {
|
||||
connections.add(new_connection);
|
||||
@@ -2735,7 +2764,18 @@ namespace eosio {
|
||||
boost::asio::bind_executor( strand,
|
||||
[conn = shared_from_this(), socket=socket]( boost::system::error_code ec, std::size_t bytes_transferred ) {
|
||||
// may have closed connection and cleared pending_message_buffer
|
||||
if( !conn->socket_is_open() || socket != conn->socket ) return;
|
||||
if (!conn->socket->is_open() && conn->socket_is_open()) { // if socket_open then close not called
|
||||
peer_dlog( conn, "async_read socket not open, closing");
|
||||
conn->close();
|
||||
return;
|
||||
}
|
||||
if (socket != conn->socket ) { // different socket, conn must have created a new socket, make sure previous is closed
|
||||
peer_dlog( conn, "async_read diff socket closing");
|
||||
boost::system::error_code ec;
|
||||
socket->shutdown( tcp::socket::shutdown_both, ec );
|
||||
socket->close( ec );
|
||||
return;
|
||||
}
|
||||
|
||||
bool close_connection = false;
|
||||
try {
|
||||
@@ -3078,6 +3118,7 @@ namespace eosio {
|
||||
peer_head_block_num = msg.head_num;
|
||||
fc::unique_lock g_conn( conn_mtx );
|
||||
last_handshake_recv = msg;
|
||||
auto c_time = last_handshake_sent.time;
|
||||
g_conn.unlock();
|
||||
|
||||
set_state(connection_state::connected);
|
||||
@@ -3103,16 +3144,11 @@ namespace eosio {
|
||||
return;
|
||||
}
|
||||
|
||||
if( peer_address().empty() ) {
|
||||
if( incoming() ) {
|
||||
auto [host, port, type] = split_host_port_type(msg.p2p_address);
|
||||
if (host.size())
|
||||
set_connection_type( msg.p2p_address );
|
||||
}
|
||||
|
||||
g_conn.lock();
|
||||
if( peer_address().empty() || last_handshake_recv.node_id == fc::sha256()) {
|
||||
auto c_time = last_handshake_sent.time;
|
||||
g_conn.unlock();
|
||||
peer_dlog( this, "checking for duplicate" );
|
||||
auto is_duplicate = [&](const auto& check) {
|
||||
if(check.get() == this)
|
||||
@@ -3132,9 +3168,9 @@ namespace eosio {
|
||||
if (msg.time + c_time <= check_time)
|
||||
return false;
|
||||
} else if (net_version < proto_dup_node_id_goaway || msg.network_version < proto_dup_node_id_goaway) {
|
||||
if (my_impl->p2p_address < msg.p2p_address) {
|
||||
fc_dlog( logger, "my_impl->p2p_address '${lhs}' < msg.p2p_address '${rhs}'",
|
||||
("lhs", my_impl->p2p_address)( "rhs", msg.p2p_address ) );
|
||||
if (listen_address < msg.p2p_address) {
|
||||
fc_dlog( logger, "listen_address '${lhs}' < msg.p2p_address '${rhs}'",
|
||||
("lhs", listen_address)( "rhs", msg.p2p_address ) );
|
||||
// only the connection from lower p2p_address to higher p2p_address will be considered as a duplicate,
|
||||
// so there is no chance for both connections to be closed
|
||||
return false;
|
||||
@@ -3159,9 +3195,7 @@ namespace eosio {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
peer_dlog( this, "skipping duplicate check, addr == ${pa}, id = ${ni}",
|
||||
("pa", peer_address())( "ni", last_handshake_recv.node_id ) );
|
||||
g_conn.unlock();
|
||||
peer_dlog(this, "skipping duplicate check, addr == ${pa}, id = ${ni}", ("pa", peer_address())("ni", msg.node_id));
|
||||
}
|
||||
|
||||
if( msg.chain_id != my_impl->chain_id ) {
|
||||
@@ -3818,7 +3852,7 @@ namespace eosio {
|
||||
// If we couldn't sign, don't send a token.
|
||||
if(hello.sig == chain::signature_type())
|
||||
hello.token = sha256();
|
||||
hello.p2p_address = my_impl->p2p_address;
|
||||
hello.p2p_address = listen_address;
|
||||
if( is_transactions_only_connection() ) hello.p2p_address += ":trx";
|
||||
// if we are not accepting transactions tell peer we are blocks only
|
||||
if( is_blocks_only_connection() || !my_impl->p2p_accept_transactions ) hello.p2p_address += ":blk";
|
||||
@@ -3850,8 +3884,8 @@ namespace eosio {
|
||||
void net_plugin::set_program_options( options_description& /*cli*/, options_description& cfg )
|
||||
{
|
||||
cfg.add_options()
|
||||
( "p2p-listen-endpoint", bpo::value<string>()->default_value( "0.0.0.0:9876" ), "The actual host:port used to listen for incoming p2p connections.")
|
||||
( "p2p-server-address", bpo::value<string>(), "An externally accessible host:port for identifying this node. Defaults to p2p-listen-endpoint.")
|
||||
( "p2p-listen-endpoint", bpo::value< vector<string> >()->default_value( vector<string>(1, string("0.0.0.0:9876")) ), "The actual host:port used to listen for incoming p2p connections. May be used multiple times.")
|
||||
( "p2p-server-address", bpo::value< vector<string> >(), "An externally accessible host:port for identifying this node. Defaults to p2p-listen-endpoint. May be used as many times as p2p-listen-endpoint. If provided, the first address will be used in handshakes with other nodes. Otherwise the default is used.")
|
||||
( "p2p-peer-address", bpo::value< vector<string> >()->composing(),
|
||||
"The public endpoint of a peer node to connect to. Use multiple p2p-peer-address options as needed to compose a network.\n"
|
||||
" Syntax: host:port[:<trx>|<blk>]\n"
|
||||
@@ -3872,7 +3906,7 @@ namespace eosio {
|
||||
( "agent-name", bpo::value<string>()->default_value("EOS Test Agent"), "The name supplied to identify this node amongst the peers.")
|
||||
( "allowed-connection", bpo::value<vector<string>>()->multitoken()->default_value({"any"}, "any"), "Can be 'any' or 'producers' or 'specified' or 'none'. If 'specified', peer-key must be specified at least once. If only 'producers', peer-key is not required. 'producers' and 'specified' may be combined.")
|
||||
( "peer-key", bpo::value<vector<string>>()->composing()->multitoken(), "Optional public key of peer allowed to connect. May be used multiple times.")
|
||||
( "peer-private-key", boost::program_options::value<vector<string>>()->composing()->multitoken(),
|
||||
( "peer-private-key", bpo::value<vector<string>>()->composing()->multitoken(),
|
||||
"Tuple of [PublicKey, WIF private key] (may specify multiple times)")
|
||||
( "max-clients", bpo::value<uint32_t>()->default_value(def_max_clients), "Maximum number of clients from which connections are accepted, use 0 for no limit")
|
||||
( "connection-cleanup-period", bpo::value<int>()->default_value(def_conn_retry_wait), "number of seconds to wait before cleaning up dead connections")
|
||||
@@ -3932,16 +3966,35 @@ namespace eosio {
|
||||
std::chrono::seconds( options.at("connection-cleanup-period").as<int>() ),
|
||||
options.at("max-clients").as<uint32_t>() );
|
||||
|
||||
if( options.count( "p2p-listen-endpoint" ) && options.at("p2p-listen-endpoint").as<string>().length()) {
|
||||
p2p_address = options.at( "p2p-listen-endpoint" ).as<string>();
|
||||
EOS_ASSERT( p2p_address.length() <= max_p2p_address_length, chain::plugin_config_exception,
|
||||
"p2p-listen-endpoint too long, must be less than ${m}", ("m", max_p2p_address_length) );
|
||||
if( options.count( "p2p-listen-endpoint" )) {
|
||||
auto p2ps = options.at("p2p-listen-endpoint").as<vector<string>>();
|
||||
if (!p2ps.front().empty()) {
|
||||
p2p_addresses = p2ps;
|
||||
auto addr_count = p2p_addresses.size();
|
||||
std::sort(p2p_addresses.begin(), p2p_addresses.end());
|
||||
auto last = std::unique(p2p_addresses.begin(), p2p_addresses.end());
|
||||
p2p_addresses.erase(last, p2p_addresses.end());
|
||||
if( size_t addr_diff = addr_count - p2p_addresses.size(); addr_diff != 0) {
|
||||
fc_wlog( logger, "Removed ${count} duplicate p2p-listen-endpoint entries", ("count", addr_diff));
|
||||
}
|
||||
for( const auto& addr : p2p_addresses ) {
|
||||
EOS_ASSERT( addr.length() <= max_p2p_address_length, chain::plugin_config_exception,
|
||||
"p2p-listen-endpoint ${a} too long, must be less than ${m}",
|
||||
("a", addr)("m", max_p2p_address_length) );
|
||||
}
|
||||
}
|
||||
}
|
||||
if( options.count( "p2p-server-address" ) ) {
|
||||
p2p_server_address = options.at( "p2p-server-address" ).as<string>();
|
||||
EOS_ASSERT( p2p_server_address.length() <= max_p2p_address_length, chain::plugin_config_exception,
|
||||
"p2p_server_address too long, must be less than ${m}", ("m", max_p2p_address_length) );
|
||||
p2p_server_addresses = options.at( "p2p-server-address" ).as<vector<string>>();
|
||||
EOS_ASSERT( p2p_server_addresses.size() <= p2p_addresses.size(), chain::plugin_config_exception,
|
||||
"p2p-server-address may not be specified more times than p2p-listen-endpoint" );
|
||||
for( const auto& addr: p2p_server_addresses ) {
|
||||
EOS_ASSERT( addr.length() <= max_p2p_address_length, chain::plugin_config_exception,
|
||||
"p2p-server-address ${a} too long, must be less than ${m}",
|
||||
("a", addr)("m", max_p2p_address_length) );
|
||||
}
|
||||
}
|
||||
p2p_server_addresses.resize(p2p_addresses.size()); // extend with empty entries as needed
|
||||
|
||||
thread_pool_size = options.at( "net-threads" ).as<uint16_t>();
|
||||
EOS_ASSERT( thread_pool_size > 0, chain::plugin_config_exception,
|
||||
@@ -4034,7 +4087,7 @@ namespace eosio {
|
||||
|
||||
dispatcher = std::make_unique<dispatch_manager>( my_impl->thread_pool.get_executor() );
|
||||
|
||||
if( !p2p_accept_transactions && p2p_address.size() ) {
|
||||
if( !p2p_accept_transactions && p2p_addresses.size() ) {
|
||||
fc_ilog( logger, "\n"
|
||||
"***********************************\n"
|
||||
"* p2p-accept-transactions = false *\n"
|
||||
@@ -4042,13 +4095,15 @@ namespace eosio {
|
||||
"***********************************\n" );
|
||||
}
|
||||
|
||||
std::string listen_address = p2p_address;
|
||||
std::vector<string> listen_addresses = p2p_addresses;
|
||||
|
||||
if( !p2p_address.empty() ) {
|
||||
auto [host, port] = fc::split_host_port(listen_address);
|
||||
EOS_ASSERT( p2p_addresses.size() == p2p_server_addresses.size(), chain::plugin_config_exception, "" );
|
||||
std::transform(p2p_addresses.begin(), p2p_addresses.end(), p2p_server_addresses.begin(),
|
||||
p2p_addresses.begin(), [](const string& p2p_address, const string& p2p_server_address) {
|
||||
auto [host, port] = fc::split_host_port(p2p_address);
|
||||
|
||||
if( !p2p_server_address.empty() ) {
|
||||
p2p_address = p2p_server_address;
|
||||
return p2p_server_address;
|
||||
} else if( host.empty() || host == "0.0.0.0" || host == "[::]") {
|
||||
boost::system::error_code ec;
|
||||
auto hostname = host_name( ec );
|
||||
@@ -4058,9 +4113,10 @@ namespace eosio {
|
||||
"Unable to retrieve host_name. ${msg}", ("msg", ec.message()));
|
||||
|
||||
}
|
||||
p2p_address = hostname + ":" + port;
|
||||
return hostname + ":" + port;
|
||||
}
|
||||
}
|
||||
return p2p_address;
|
||||
});
|
||||
|
||||
{
|
||||
chain::controller& cc = chain_plug->chain();
|
||||
@@ -4084,8 +4140,10 @@ namespace eosio {
|
||||
incoming_transaction_ack_subscription = app().get_channel<compat::channels::transaction_ack>().subscribe(
|
||||
[this](auto&& t) { transaction_ack(std::forward<decltype(t)>(t)); });
|
||||
|
||||
app().executor().post(priority::highest, [my=shared_from_this(), address = std::move(listen_address)](){
|
||||
if (address.size()) {
|
||||
for(auto listen_itr = listen_addresses.begin(), p2p_iter = p2p_addresses.begin();
|
||||
listen_itr != listen_addresses.end();
|
||||
++listen_itr, ++p2p_iter) {
|
||||
app().executor().post(priority::highest, [my=shared_from_this(), address = std::move(*listen_itr), p2p_addr = *p2p_iter](){
|
||||
try {
|
||||
const boost::posix_time::milliseconds accept_timeout(100);
|
||||
|
||||
@@ -4094,19 +4152,20 @@ namespace eosio {
|
||||
|
||||
fc::create_listener<tcp>(
|
||||
my->thread_pool.get_executor(), logger, accept_timeout, address, extra_listening_log_info,
|
||||
[my = my](tcp::socket&& socket) { my->create_session(std::move(socket)); });
|
||||
[my = my, addr = p2p_addr](tcp::socket&& socket) { my->create_session(std::move(socket), addr); });
|
||||
} catch (const std::exception& e) {
|
||||
fc_elog( logger, "net_plugin::plugin_startup failed to listen on ${addr}, ${what}",
|
||||
("addr", address)("what", e.what()) );
|
||||
app().quit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
app().executor().post(priority::highest, [my=shared_from_this()](){
|
||||
my->ticker();
|
||||
my->start_monitors();
|
||||
my->update_chain_info();
|
||||
my->connections.connect_supplied_peers();
|
||||
my->connections.connect_supplied_peers(*my->p2p_addresses.begin()); // attribute every outbound connection to the first listen port
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4143,7 +4202,7 @@ namespace eosio {
|
||||
|
||||
/// RPC API
|
||||
string net_plugin::connect( const string& host ) {
|
||||
return my->connections.connect( host );
|
||||
return my->connections.connect( host, *my->p2p_addresses.begin() );
|
||||
}
|
||||
|
||||
/// RPC API
|
||||
@@ -4217,10 +4276,10 @@ namespace eosio {
|
||||
update_p2p_connection_metrics = std::move(fun);
|
||||
}
|
||||
|
||||
void connections_manager::connect_supplied_peers() {
|
||||
void connections_manager::connect_supplied_peers(const string& p2p_address) {
|
||||
std::lock_guard g(connections_mtx);
|
||||
for (const auto& peer : supplied_peers) {
|
||||
connect_i(peer);
|
||||
connect_i(peer, p2p_address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4230,12 +4289,12 @@ namespace eosio {
|
||||
}
|
||||
|
||||
// called by API
|
||||
string connections_manager::connect( const string& host ) {
|
||||
string connections_manager::connect( const string& host, const string& p2p_address ) {
|
||||
std::lock_guard g( connections_mtx );
|
||||
if( find_connection_i( host ) )
|
||||
return "already connected";
|
||||
|
||||
connect_i( host );
|
||||
connect_i( host, p2p_address );
|
||||
supplied_peers.insert(host);
|
||||
return "added connection";
|
||||
}
|
||||
@@ -4292,8 +4351,8 @@ namespace eosio {
|
||||
}
|
||||
|
||||
// call with connections_mtx
|
||||
void connections_manager::connect_i( const string& host ) {
|
||||
connection_ptr c = std::make_shared<connection>( host );
|
||||
void connections_manager::connect_i( const string& host, const string& p2p_address ) {
|
||||
connection_ptr c = std::make_shared<connection>( host, p2p_address );
|
||||
fc_dlog( logger, "calling active connector: ${h}", ("h", host) );
|
||||
if( c->resolve_and_connect() ) {
|
||||
fc_dlog( logger, "adding new connection to the list: ${host} ${cid}", ("host", host)("cid", c->connection_id) );
|
||||
|
||||
@@ -18,7 +18,7 @@ struct mock_connections_manager {
|
||||
uint32_t max_client_count = 0;
|
||||
std::vector<mock_connection> connections;
|
||||
|
||||
std::function<void(std::string)> connect;
|
||||
std::function<void(std::string, std::string)> connect;
|
||||
std::function<void(std::string)> disconnect;
|
||||
|
||||
uint32_t get_max_client_count() const { return max_client_count; }
|
||||
@@ -36,6 +36,7 @@ struct mock_net_plugin : eosio::auto_bp_peering::bp_connection_manager<mock_net_
|
||||
|
||||
bool is_in_sync = false;
|
||||
mock_connections_manager connections;
|
||||
std::vector<std::string> p2p_addresses{"0.0.0.0:9876"};
|
||||
|
||||
bool in_sync() { return is_in_sync; }
|
||||
|
||||
@@ -165,7 +166,7 @@ BOOST_AUTO_TEST_CASE(test_on_pending_schedule) {
|
||||
|
||||
std::vector<std::string> connected_hosts;
|
||||
|
||||
plugin.connections.connect = [&connected_hosts](std::string host) { connected_hosts.push_back(host); };
|
||||
plugin.connections.connect = [&connected_hosts](std::string host, std::string p2p_address) { connected_hosts.push_back(host); };
|
||||
|
||||
// make sure nothing happens when it is not in_sync
|
||||
plugin.is_in_sync = false;
|
||||
@@ -209,7 +210,7 @@ BOOST_AUTO_TEST_CASE(test_on_active_schedule1) {
|
||||
plugin.config.my_bp_accounts = { "prodd"_n, "produ"_n };
|
||||
|
||||
plugin.active_neighbors = { "proda"_n, "prodh"_n, "prodn"_n };
|
||||
plugin.connections.connect = [](std::string host) {};
|
||||
plugin.connections.connect = [](std::string host, std::string p2p_address) {};
|
||||
|
||||
std::vector<std::string> disconnected_hosts;
|
||||
plugin.connections.disconnect = [&disconnected_hosts](std::string host) { disconnected_hosts.push_back(host); };
|
||||
@@ -245,7 +246,7 @@ BOOST_AUTO_TEST_CASE(test_on_active_schedule2) {
|
||||
plugin.config.my_bp_accounts = { "prodd"_n, "produ"_n };
|
||||
|
||||
plugin.active_neighbors = { "proda"_n, "prodh"_n, "prodn"_n };
|
||||
plugin.connections.connect = [](std::string host) {};
|
||||
plugin.connections.connect = [](std::string host, std::string p2p_address) {};
|
||||
std::vector<std::string> disconnected_hosts;
|
||||
plugin.connections.disconnect = [&disconnected_hosts](std::string host) { disconnected_hosts.push_back(host); };
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
add_executable( test_producer_plugin
|
||||
test_trx_full.cpp
|
||||
test_options.cpp
|
||||
test_read_only_trx.cpp
|
||||
test_block_timing_util.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
@@ -30,17 +30,21 @@ BOOST_AUTO_TEST_CASE(state_dir) {
|
||||
std::promise<std::tuple<producer_plugin*, chain_plugin*>> plugin_promise;
|
||||
std::future<std::tuple<producer_plugin*, chain_plugin*>> plugin_fut = plugin_promise.get_future();
|
||||
std::thread app_thread( [&]() {
|
||||
fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug);
|
||||
std::vector<const char*> argv =
|
||||
{"test",
|
||||
"--data-dir", temp_dir_str.c_str(),
|
||||
"--state-dir", custom_state_dir_str.c_str(),
|
||||
"--config-dir", temp_dir_str.c_str(),
|
||||
"-p", "eosio", "-e" };
|
||||
app->initialize<chain_plugin, producer_plugin>( argv.size(), (char**) &argv[0] );
|
||||
app->startup();
|
||||
plugin_promise.set_value( {app->find_plugin<producer_plugin>(), app->find_plugin<chain_plugin>()} );
|
||||
app->exec();
|
||||
try {
|
||||
fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug);
|
||||
std::vector<const char*> argv =
|
||||
{"test",
|
||||
"--data-dir", temp_dir_str.c_str(),
|
||||
"--state-dir", custom_state_dir_str.c_str(),
|
||||
"--config-dir", temp_dir_str.c_str(),
|
||||
"-p", "eosio", "-e" };
|
||||
app->initialize<chain_plugin, producer_plugin>( argv.size(), (char**) &argv[0] );
|
||||
app->startup();
|
||||
plugin_promise.set_value( {app->find_plugin<producer_plugin>(), app->find_plugin<chain_plugin>()} );
|
||||
app->exec();
|
||||
return;
|
||||
} FC_LOG_AND_DROP()
|
||||
BOOST_CHECK(!"app threw exception see logged error");
|
||||
} );
|
||||
|
||||
auto[prod_plug, chain_plug] = plugin_fut.get();
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <eosio/producer_plugin/producer_plugin.hpp>
|
||||
#include <eosio/testing/tester.hpp>
|
||||
#include <eosio/chain/genesis_state.hpp>
|
||||
#include <eosio/chain/thread_utils.hpp>
|
||||
#include <eosio/chain/transaction_metadata.hpp>
|
||||
#include <eosio/chain/trace.hpp>
|
||||
#include <eosio/chain/name.hpp>
|
||||
#include <eosio/chain/application.hpp>
|
||||
|
||||
namespace eosio::test::detail {
|
||||
using namespace eosio::chain::literals;
|
||||
struct testit {
|
||||
uint64_t id;
|
||||
testit( uint64_t id = 0 )
|
||||
:id(id){}
|
||||
static account_name get_account() {
|
||||
return chain::config::system_account_name;
|
||||
}
|
||||
static action_name get_name() {
|
||||
return "testit"_n;
|
||||
}
|
||||
};
|
||||
}
|
||||
FC_REFLECT( eosio::test::detail::testit, (id) )
|
||||
|
||||
namespace {
|
||||
using namespace eosio;
|
||||
using namespace eosio::chain;
|
||||
using namespace eosio::test::detail;
|
||||
|
||||
auto make_unique_trx( const chain_id_type& chain_id ) {
|
||||
static uint64_t nextid = 0;
|
||||
++nextid;
|
||||
account_name creator = config::system_account_name;
|
||||
signed_transaction trx;
|
||||
trx.expiration = fc::time_point_sec{fc::time_point::now() + fc::seconds( nextid % 50 == 0 ? 0 : 60 )}; // fail some transactions via expired
|
||||
if( nextid % 10 == 0 ) {
|
||||
// fail some for authorization (read-only transaction should not have authorization)
|
||||
trx.actions.emplace_back( vector<permission_level>{{creator, config::active_name}}, testit{nextid} );
|
||||
} else {
|
||||
vector<permission_level> no_auth{};
|
||||
trx.actions.emplace_back( no_auth, testit{nextid} );
|
||||
}
|
||||
return std::make_shared<packed_transaction>( std::move(trx) );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(read_only_trxs)
|
||||
|
||||
enum class app_init_status { failed, succeeded };
|
||||
|
||||
void test_configs_common(std::vector<const char*>& specific_args, app_init_status expected_status) {
|
||||
fc::temp_directory temp;
|
||||
appbase::scoped_app app;
|
||||
auto temp_dir_str = temp.path().string();
|
||||
|
||||
fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug);
|
||||
std::vector<const char*> argv =
|
||||
{"test", "--data-dir", temp_dir_str.c_str(), "--config-dir", temp_dir_str.c_str()};
|
||||
argv.insert( argv.end(), specific_args.begin(), specific_args.end() );
|
||||
|
||||
// app->initialize() returns a boolean. BOOST_CHECK_EQUAL cannot compare
|
||||
// a boolean with a app_init_status directly
|
||||
bool rc = (expected_status == app_init_status::succeeded) ? true : false;
|
||||
bool result = false;
|
||||
try {
|
||||
result = app->initialize<producer_plugin>( argv.size(), (char**) &argv[0]);
|
||||
} catch(...) {}
|
||||
BOOST_CHECK_EQUAL( result, rc );
|
||||
}
|
||||
|
||||
// --read-only-thread not allowed on producer node
|
||||
BOOST_AUTO_TEST_CASE(read_only_on_producer) {
|
||||
std::vector<const char*> specific_args = {"-p", "eosio", "-e", "--read-only-threads", "2" };
|
||||
test_configs_common(specific_args, app_init_status::failed);
|
||||
}
|
||||
|
||||
// read_window_time must be greater than max_transaction_time + 10ms
|
||||
BOOST_AUTO_TEST_CASE(invalid_read_window_time) {
|
||||
std::vector<const char*> specific_args = { "--read-only-threads", "2", "--max-transaction-time", "10", "--read-only-write-window-time-us", "50000", "--read-only-read-window-time-us", "20000" }; // 20000 not greater than --max-transaction-time (10ms) + 10000us (minimum margin)
|
||||
test_configs_common(specific_args, app_init_status::failed);
|
||||
}
|
||||
|
||||
// if --read-only-threads is not configured, read-only trx related configs should
|
||||
// not be checked
|
||||
BOOST_AUTO_TEST_CASE(not_check_configs_if_no_read_only_threads) {
|
||||
std::vector<const char*> specific_args = { "--max-transaction-time", "10", "--read-only-write-window-time-us", "50000", "--read-only-read-window-time-us", "20000" }; // 20000 not greater than --max-transaction-time (10ms) + 10000us (minimum margin)
|
||||
test_configs_common(specific_args, app_init_status::succeeded);
|
||||
}
|
||||
|
||||
void test_trxs_common(std::vector<const char*>& specific_args) {
|
||||
using namespace std::chrono_literals;
|
||||
fc::temp_directory temp;
|
||||
appbase::scoped_app app;
|
||||
auto temp_dir_str = temp.path().string();
|
||||
producer_plugin::set_test_mode(true);
|
||||
|
||||
std::promise<std::tuple<producer_plugin*, chain_plugin*>> plugin_promise;
|
||||
std::future<std::tuple<producer_plugin*, chain_plugin*>> plugin_fut = plugin_promise.get_future();
|
||||
std::thread app_thread( [&]() {
|
||||
fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug);
|
||||
std::vector<const char*> argv = {"test", "--data-dir", temp_dir_str.c_str(), "--config-dir", temp_dir_str.c_str()};
|
||||
argv.insert( argv.end(), specific_args.begin(), specific_args.end() );
|
||||
app->initialize<chain_plugin, producer_plugin>( argv.size(), (char**) &argv[0] );
|
||||
app->startup();
|
||||
plugin_promise.set_value( {app->find_plugin<producer_plugin>(), app->find_plugin<chain_plugin>()} );
|
||||
app->exec();
|
||||
} );
|
||||
|
||||
auto[prod_plug, chain_plug] = plugin_fut.get();
|
||||
auto chain_id = chain_plug->get_chain_id();
|
||||
|
||||
std::atomic<size_t> next_calls = 0;
|
||||
std::atomic<size_t> num_get_account_calls = 0;
|
||||
std::atomic<size_t> num_posts = 0;
|
||||
std::atomic<size_t> trace_with_except = 0;
|
||||
std::atomic<bool> trx_match = true;
|
||||
const size_t num_pushes = 4242;
|
||||
|
||||
for( size_t i = 1; i <= num_pushes; ++i ) {
|
||||
auto ptrx = make_unique_trx( chain_id );
|
||||
app->executor().post( priority::low, exec_queue::read_only, [&chain_plug=chain_plug, &num_get_account_calls]() {
|
||||
chain_plug->get_read_only_api(fc::seconds(90)).get_account(chain_apis::read_only::get_account_params{.account_name=config::system_account_name}, fc::time_point::now()+fc::seconds(90));
|
||||
++num_get_account_calls;
|
||||
});
|
||||
app->executor().post( priority::low, exec_queue::read_write, [ptrx, &next_calls, &num_posts, &trace_with_except, &trx_match, &app]() {
|
||||
++num_posts;
|
||||
bool return_failure_traces = true;
|
||||
app->get_method<plugin_interface::incoming::methods::transaction_async>()(ptrx,
|
||||
false, // api_trx
|
||||
transaction_metadata::trx_type::read_only, // trx_type
|
||||
return_failure_traces,
|
||||
[ptrx, &next_calls, &trace_with_except, &trx_match, return_failure_traces]
|
||||
(const next_function_variant<transaction_trace_ptr>& result) {
|
||||
if( !std::holds_alternative<fc::exception_ptr>( result ) && !std::get<chain::transaction_trace_ptr>( result )->except ) {
|
||||
if( std::get<chain::transaction_trace_ptr>( result )->id != ptrx->id() ) {
|
||||
elog( "trace not for trx ${id}: ${t}",
|
||||
("id", ptrx->id())("t", fc::json::to_pretty_string(*std::get<chain::transaction_trace_ptr>(result))) );
|
||||
trx_match = false;
|
||||
}
|
||||
} else if( !return_failure_traces && !std::holds_alternative<fc::exception_ptr>( result ) && std::get<chain::transaction_trace_ptr>( result )->except ) {
|
||||
elog( "trace with except ${e}",
|
||||
("e", fc::json::to_pretty_string( *std::get<chain::transaction_trace_ptr>( result ) )) );
|
||||
++trace_with_except;
|
||||
}
|
||||
++next_calls;
|
||||
});
|
||||
});
|
||||
app->executor().post( priority::low, exec_queue::read_only, [&chain_plug=chain_plug]() {
|
||||
chain_plug->get_read_only_api(fc::seconds(90)).get_consensus_parameters(chain_apis::read_only::get_consensus_parameters_params{}, fc::time_point::now()+fc::seconds(90));
|
||||
});
|
||||
}
|
||||
|
||||
// Wait long enough such that all transactions are executed
|
||||
auto start = fc::time_point::now();
|
||||
auto hard_deadline = start + fc::seconds(10); // To protect against waiting forever
|
||||
while ( (next_calls < num_pushes || num_get_account_calls < num_pushes) && fc::time_point::now() < hard_deadline ){
|
||||
std::this_thread::sleep_for( 100ms );;
|
||||
}
|
||||
|
||||
app->quit();
|
||||
app_thread.join();
|
||||
|
||||
BOOST_CHECK_EQUAL( trace_with_except, 0 ); // should not have any traces with except in it
|
||||
BOOST_CHECK_EQUAL( num_pushes, num_posts );
|
||||
BOOST_CHECK_EQUAL( num_pushes, next_calls.load() );
|
||||
BOOST_CHECK_EQUAL( num_pushes, num_get_account_calls.load() );
|
||||
BOOST_CHECK( trx_match.load() ); // trace should match the transaction
|
||||
}
|
||||
|
||||
// test read-only trxs on main thread (no --read-only-threads)
|
||||
BOOST_AUTO_TEST_CASE(no_read_only_threads) {
|
||||
std::vector<const char*> specific_args = { "-p", "eosio", "-e", "--abi-serializer-max-time-ms=999" };
|
||||
test_trxs_common(specific_args);
|
||||
}
|
||||
|
||||
// test read-only trxs on 1 threads (with --read-only-threads)
|
||||
BOOST_AUTO_TEST_CASE(with_1_read_only_threads) {
|
||||
std::vector<const char*> specific_args = { "-p", "eosio", "-e",
|
||||
"--read-only-threads=1",
|
||||
"--max-transaction-time=10",
|
||||
"--abi-serializer-max-time-ms=999",
|
||||
"--read-only-write-window-time-us=100000",
|
||||
"--read-only-read-window-time-us=40000" };
|
||||
test_trxs_common(specific_args);
|
||||
}
|
||||
|
||||
// test read-only trxs on 3 threads (with --read-only-threads)
|
||||
BOOST_AUTO_TEST_CASE(with_3_read_only_threads) {
|
||||
std::vector<const char*> specific_args = { "-p", "eosio", "-e",
|
||||
"--read-only-threads=3",
|
||||
"--max-transaction-time=10",
|
||||
"--abi-serializer-max-time-ms=999",
|
||||
"--read-only-write-window-time-us=100000",
|
||||
"--read-only-read-window-time-us=40000" };
|
||||
test_trxs_common(specific_args);
|
||||
}
|
||||
|
||||
// test read-only trxs on 8 separate threads (with --read-only-threads)
|
||||
BOOST_AUTO_TEST_CASE(with_8_read_only_threads) {
|
||||
std::vector<const char*> specific_args = { "-p", "eosio", "-e",
|
||||
"--read-only-threads=8",
|
||||
"--eos-vm-oc-enable=none",
|
||||
"--max-transaction-time=10",
|
||||
"--abi-serializer-max-time-ms=999",
|
||||
"--read-only-write-window-time-us=100000",
|
||||
"--read-only-read-window-time-us=40000" };
|
||||
test_trxs_common(specific_args);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
@@ -108,15 +108,19 @@ BOOST_AUTO_TEST_CASE(producer) {
|
||||
std::promise<std::tuple<producer_plugin*, chain_plugin*>> plugin_promise;
|
||||
std::future<std::tuple<producer_plugin*, chain_plugin*>> plugin_fut = plugin_promise.get_future();
|
||||
std::thread app_thread( [&]() {
|
||||
fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug);
|
||||
std::vector<const char*> argv =
|
||||
{"test", "--data-dir", temp_dir_str.c_str(), "--config-dir", temp_dir_str.c_str(),
|
||||
"-p", "eosio", "-e", "--disable-subjective-p2p-billing=true" };
|
||||
app->initialize<chain_plugin, producer_plugin>( argv.size(), (char**) &argv[0] );
|
||||
app->startup();
|
||||
plugin_promise.set_value(
|
||||
{app->find_plugin<producer_plugin>(), app->find_plugin<chain_plugin>()} );
|
||||
app->exec();
|
||||
try {
|
||||
fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug);
|
||||
std::vector<const char*> argv =
|
||||
{"test", "--data-dir", temp_dir_str.c_str(), "--config-dir", temp_dir_str.c_str(),
|
||||
"-p", "eosio", "-e", "--disable-subjective-p2p-billing=true" };
|
||||
app->initialize<chain_plugin, producer_plugin>( argv.size(), (char**) &argv[0] );
|
||||
app->startup();
|
||||
plugin_promise.set_value(
|
||||
{app->find_plugin<producer_plugin>(), app->find_plugin<chain_plugin>()} );
|
||||
app->exec();
|
||||
return;
|
||||
} FC_LOG_AND_DROP()
|
||||
BOOST_CHECK(!"app threw exception see logged error");
|
||||
} );
|
||||
|
||||
auto[prod_plug, chain_plug] = plugin_fut.get();
|
||||
|
||||
@@ -447,7 +447,7 @@ private:
|
||||
|
||||
void update_current_request(state_history::get_blocks_request_v0& req) {
|
||||
fc_dlog(plugin.get_logger(), "replying get_blocks_request_v0 = ${req}", ("req", req));
|
||||
to_send_block_num = req.start_block_num;
|
||||
to_send_block_num = std::max(req.start_block_num, plugin.get_first_available_block_num());
|
||||
for (auto& cp : req.have_positions) {
|
||||
if (req.start_block_num <= cp.block_num)
|
||||
continue;
|
||||
|
||||
@@ -54,6 +54,7 @@ private:
|
||||
chain_plugin* chain_plug = nullptr;
|
||||
std::optional<state_history_log> trace_log;
|
||||
std::optional<state_history_log> chain_state_log;
|
||||
uint32_t first_available_block = 0;
|
||||
bool trace_debug_mode = false;
|
||||
std::optional<scoped_connection> applied_transaction_connection;
|
||||
std::optional<scoped_connection> block_start_connection;
|
||||
@@ -113,10 +114,17 @@ public:
|
||||
|
||||
// thread-safe
|
||||
std::optional<chain::block_id_type> get_block_id(uint32_t block_num) {
|
||||
if (trace_log)
|
||||
return trace_log->get_block_id(block_num);
|
||||
if (chain_state_log)
|
||||
return chain_state_log->get_block_id(block_num);
|
||||
std::optional<chain::block_id_type> id;
|
||||
if( trace_log ) {
|
||||
id = trace_log->get_block_id( block_num );
|
||||
if( id )
|
||||
return id;
|
||||
}
|
||||
if( chain_state_log ) {
|
||||
id = chain_state_log->get_block_id( block_num );
|
||||
if( id )
|
||||
return id;
|
||||
}
|
||||
try {
|
||||
return chain_plug->chain().get_block_id_for_num(block_num);
|
||||
} catch (...) {
|
||||
@@ -142,6 +150,11 @@ public:
|
||||
return head_timestamp;
|
||||
}
|
||||
|
||||
// thread-safe
|
||||
uint32_t get_first_available_block_num() const {
|
||||
return first_available_block;
|
||||
}
|
||||
|
||||
template <typename Protocol>
|
||||
void create_listener(const std::string& address) {
|
||||
const boost::posix_time::milliseconds accept_timeout(200);
|
||||
@@ -177,15 +190,18 @@ public:
|
||||
trace_converter.add_transaction(p, t);
|
||||
}
|
||||
|
||||
// called from main thread
|
||||
void update_current() {
|
||||
const auto& chain = chain_plug->chain();
|
||||
std::lock_guard g(mtx);
|
||||
head_id = chain.head_block_id();
|
||||
lib_id = chain.last_irreversible_block_id();
|
||||
head_timestamp = chain.head_block_time();
|
||||
}
|
||||
|
||||
// called from main thread
|
||||
void on_accepted_block(const block_state_ptr& block_state) {
|
||||
{
|
||||
const auto& chain = chain_plug->chain();
|
||||
std::lock_guard g(mtx);
|
||||
head_id = chain.head_block_id();
|
||||
lib_id = chain.last_irreversible_block_id();
|
||||
head_timestamp = chain.head_block_time();
|
||||
}
|
||||
update_current();
|
||||
|
||||
try {
|
||||
store_traces(block_state);
|
||||
@@ -381,12 +397,26 @@ void state_history_plugin::plugin_initialize(const variables_map& options) {
|
||||
|
||||
void state_history_plugin_impl::plugin_startup() {
|
||||
try {
|
||||
auto bsp = chain_plug->chain().head_block_state();
|
||||
const auto& chain = chain_plug->chain();
|
||||
update_current();
|
||||
auto bsp = chain.head_block_state();
|
||||
if( bsp && chain_state_log && chain_state_log->empty() ) {
|
||||
fc_ilog( _log, "Storing initial state on startup, this can take a considerable amount of time" );
|
||||
store_chain_state( bsp );
|
||||
fc_ilog( _log, "Done storing initial state on startup" );
|
||||
}
|
||||
first_available_block = chain.earliest_available_block_num();
|
||||
if (trace_log) {
|
||||
auto first_trace_block = trace_log->block_range().first;
|
||||
if( first_trace_block > 0 )
|
||||
first_available_block = std::min( first_available_block, first_trace_block );
|
||||
}
|
||||
if (chain_state_log) {
|
||||
auto first_state_block = chain_state_log->block_range().first;
|
||||
if( first_state_block > 0 )
|
||||
first_available_block = std::min( first_available_block, first_state_block );
|
||||
}
|
||||
fc_ilog(_log, "First available block for SHiP ${b}", ("b", first_available_block));
|
||||
listen();
|
||||
// use of executor assumes only one thread
|
||||
thread_pool.start( 1, [](const fc::exception& e) {
|
||||
|
||||
@@ -135,6 +135,8 @@ struct mock_state_history_plugin {
|
||||
eosio::state_history::block_position get_block_head() { return block_head; }
|
||||
eosio::state_history::block_position get_last_irreversible() { return block_head; }
|
||||
|
||||
uint32_t get_first_available_block_num() const { return 0; }
|
||||
|
||||
void add_session(std::shared_ptr<eosio::session_base> s) {
|
||||
session_mgr.insert(std::move(s));
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ set(LOCALEDOMAIN ${CLI_CLIENT_EXECUTABLE_NAME})
|
||||
target_include_directories(${CLI_CLIENT_EXECUTABLE_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
target_link_libraries( ${CLI_CLIENT_EXECUTABLE_NAME}
|
||||
PRIVATE appbase version leap-cli11 chain_api_plugin producer_plugin chain_plugin http_plugin eosio_chain fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
|
||||
PRIVATE appbase version leap-cli11 chain_api_plugin producer_plugin chain_plugin http_plugin eosio_chain fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} Boost::process Boost::dll )
|
||||
|
||||
if (CURL_FOUND)
|
||||
target_sources(${CLI_CLIENT_EXECUTABLE_NAME} PRIVATE do_http_post_libcurl.cpp)
|
||||
|
||||
@@ -40,7 +40,8 @@ target_link_libraries( ${NODE_EXECUTABLE_NAME}
|
||||
PRIVATE -Wl,${whole_archive_flag} prometheus_plugin -Wl,${no_whole_archive_flag}
|
||||
PRIVATE -Wl,${build_id_flag}
|
||||
PRIVATE chain_plugin http_plugin producer_plugin http_client_plugin
|
||||
PRIVATE eosio_chain_wrap fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
|
||||
PRIVATE eosio_chain_wrap fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS}
|
||||
Boost::dll )
|
||||
|
||||
|
||||
include(additionalPlugins)
|
||||
|
||||
@@ -11,10 +11,16 @@
|
||||
#include <fc/log/logger_config.hpp>
|
||||
#include <fc/log/appender.hpp>
|
||||
#include <fc/exception/exception.hpp>
|
||||
#include <fc/scoped_exit.hpp>
|
||||
|
||||
#include <boost/dll/runtime_symbol_info.hpp>
|
||||
#include <boost/exception/diagnostic_information.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
#include "config.hpp"
|
||||
|
||||
using namespace appbase;
|
||||
@@ -22,6 +28,40 @@ using namespace eosio;
|
||||
|
||||
namespace detail {
|
||||
|
||||
void log_non_default_options(const std::vector<bpo::basic_option<char>>& options) {
|
||||
using namespace std::string_literals;
|
||||
string result;
|
||||
for (const auto& op : options) {
|
||||
bool mask = false;
|
||||
if (op.string_key == "signature-provider"s
|
||||
|| op.string_key == "peer-private-key"s
|
||||
|| op.string_key == "p2p-auto-bp-peer"s) {
|
||||
mask = true;
|
||||
}
|
||||
std::string v;
|
||||
for (auto i = op.value.cbegin(), b = op.value.cbegin(), e = op.value.cend(); i != e; ++i) {
|
||||
if (i != b)
|
||||
v += ", ";
|
||||
if (mask)
|
||||
v += "***";
|
||||
else
|
||||
v += *i;
|
||||
}
|
||||
|
||||
if (!result.empty())
|
||||
result += ", ";
|
||||
|
||||
if (v.empty()) {
|
||||
result += op.string_key;
|
||||
} else {
|
||||
result += op.string_key;
|
||||
result += " = ";
|
||||
result += v;
|
||||
}
|
||||
}
|
||||
ilog("Non-default options: ${v}", ("v", result));
|
||||
}
|
||||
|
||||
fc::logging_config& add_deep_mind_logger(fc::logging_config& config) {
|
||||
config.appenders.push_back(
|
||||
fc::appender_config( "deep-mind", "dmlog" )
|
||||
@@ -109,6 +149,12 @@ int main(int argc, char** argv)
|
||||
{
|
||||
try {
|
||||
appbase::scoped_app app;
|
||||
fc::scoped_exit<std::function<void()>> on_exit = [&]() {
|
||||
ilog("${name} version ${ver} ${fv}",
|
||||
("name", nodeos::config::node_executable_name)("ver", app->version_string())
|
||||
("fv", app->version_string() == app->full_version_string() ? "" : app->full_version_string()) );
|
||||
::detail::log_non_default_options(app->get_parsed_options());
|
||||
};
|
||||
uint32_t short_hash = 0;
|
||||
fc::from_hex(eosio::version::version_hash(), (char*)&short_hash, sizeof(short_hash));
|
||||
|
||||
@@ -137,11 +183,12 @@ int main(int argc, char** argv)
|
||||
elog("resource_monitor_plugin failed to initialize");
|
||||
return INITIALIZE_FAIL;
|
||||
}
|
||||
ilog( "${name} version ${ver} ${fv}",
|
||||
ilog("${name} version ${ver} ${fv}",
|
||||
("name", nodeos::config::node_executable_name)("ver", app->version_string())
|
||||
("fv", app->version_string() == app->full_version_string() ? "" : app->full_version_string()) );
|
||||
ilog("${name} using configuration file ${c}", ("name", nodeos::config::node_executable_name)("c", app->full_config_file_path().string()));
|
||||
ilog("${name} data directory is ${d}", ("name", nodeos::config::node_executable_name)("d", app->data_dir().string()));
|
||||
::detail::log_non_default_options(app->get_parsed_options());
|
||||
app->startup();
|
||||
app->set_thread_priority_max();
|
||||
app->exec();
|
||||
|
||||
+1
-19
@@ -30,7 +30,6 @@ DEP_DIR="$(realpath "$1")"
|
||||
LEAP_DIR="$2"
|
||||
JOBS="$3"
|
||||
CLANG_VER=11.0.1
|
||||
BOOST_VER=1.82.0
|
||||
LLVM_VER=11.0.1
|
||||
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )";
|
||||
START_DIR="$(pwd)"
|
||||
@@ -101,27 +100,10 @@ install_llvm() {
|
||||
export LLVM_DIR="${LLVM_DIR}"
|
||||
}
|
||||
|
||||
install_boost() {
|
||||
BOOST_DIR="$1"
|
||||
|
||||
if [ ! -d "${BOOST_DIR}" ]; then
|
||||
echo "Installing Boost ${BOOST_VER} @ ${BOOST_DIR}"
|
||||
try wget -O "boost_${BOOST_VER//\./_}.tar.gz" "https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VER}/source/boost_${BOOST_VER//\./_}.tar.gz"
|
||||
try tar -xvzf "boost_${BOOST_VER//\./_}.tar.gz" -C "${DEP_DIR}"
|
||||
pushdir "${BOOST_DIR}"
|
||||
try ./bootstrap.sh -with-toolset=clang --prefix="${BOOST_DIR}/bin"
|
||||
./b2 toolset=clang cxxflags="-stdlib=libc++ -D__STRICT_ANSI__ -nostdinc++ -I\${CLANG_DIR}/include/c++/v1 -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fPIE" linkflags='-stdlib=libc++ -pie' link=static threading=multi --with-iostreams --with-date_time --with-system --with-program_options --with-chrono --with-test -q -j "${JOBS}" install
|
||||
popdir "${DEP_DIR}"
|
||||
rm "boost_${BOOST_VER//\./_}.tar.gz"
|
||||
fi
|
||||
export BOOST_DIR="${BOOST_DIR}"
|
||||
}
|
||||
|
||||
pushdir "${DEP_DIR}"
|
||||
|
||||
install_clang "${DEP_DIR}/clang-${CLANG_VER}"
|
||||
install_llvm "${DEP_DIR}/llvm-${LLVM_VER}"
|
||||
install_boost "${DEP_DIR}/boost_${BOOST_VER//\./_}"
|
||||
|
||||
# go back to the directory where the script starts
|
||||
popdir "${START_DIR}"
|
||||
@@ -130,7 +112,7 @@ pushdir "${LEAP_DIR}"
|
||||
|
||||
# build Leap
|
||||
echo "Building Leap ${SCRIPT_DIR}"
|
||||
try cmake -DCMAKE_TOOLCHAIN_FILE="${SCRIPT_DIR}/pinned_toolchain.cmake" -DCMAKE_INSTALL_PREFIX=${LEAP_PINNED_INSTALL_PREFIX:-/usr/local} -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="${LLVM_DIR}/lib/cmake" -DCMAKE_PREFIX_PATH="${BOOST_DIR}/bin" "${SCRIPT_DIR}/.."
|
||||
try cmake -DCMAKE_TOOLCHAIN_FILE="${SCRIPT_DIR}/pinned_toolchain.cmake" -DCMAKE_INSTALL_PREFIX=${LEAP_PINNED_INSTALL_PREFIX:-/usr/local} -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="${LLVM_DIR}/lib/cmake" "${SCRIPT_DIR}/.."
|
||||
|
||||
try make -j "${JOBS}"
|
||||
try cpack
|
||||
|
||||
+10
-5
@@ -49,6 +49,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ship_streamer_test.py ${CMAKE_CURRENT
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/large-lib-test.py ${CMAKE_CURRENT_BINARY_DIR}/large-lib-test.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/http_plugin_test.py ${CMAKE_CURRENT_BINARY_DIR}/http_plugin_test.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/p2p_high_latency_test.py ${CMAKE_CURRENT_BINARY_DIR}/p2p_high_latency_test.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/p2p_multiple_listen_test.py ${CMAKE_CURRENT_BINARY_DIR}/p2p_multiple_listen_test.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/p2p_no_listen_test.py ${CMAKE_CURRENT_BINARY_DIR}/p2p_no_listen_test.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/compute_transaction_test.py ${CMAKE_CURRENT_BINARY_DIR}/compute_transaction_test.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/subjective_billing_test.py ${CMAKE_CURRENT_BINARY_DIR}/subjective_billing_test.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/get_account_test.py ${CMAKE_CURRENT_BINARY_DIR}/get_account_test.py COPYONLY)
|
||||
@@ -76,7 +78,7 @@ else()
|
||||
endif()
|
||||
|
||||
#To run plugin_test with all log from blockchain displayed, put --verbose after --, i.e. plugin_test -- --verbose
|
||||
add_test(NAME plugin_test COMMAND plugin_test --report_level=detailed --color_output)
|
||||
add_test(NAME plugin_test COMMAND plugin_test --report_level=detailed --color_output --catch_system_errors=no)
|
||||
|
||||
add_test(NAME nodeos_sanity_test COMMAND tests/nodeos_run_test.py -v --sanity-test ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_property(TEST nodeos_sanity_test PROPERTY LABELS nonparallelizable_tests)
|
||||
@@ -104,9 +106,9 @@ add_subdirectory( performance_tests )
|
||||
|
||||
find_package(Threads)
|
||||
add_executable(ship_client ship_client.cpp)
|
||||
target_link_libraries(ship_client abieos Boost::program_options Boost::system Threads::Threads)
|
||||
target_link_libraries(ship_client abieos Boost::program_options Boost::system Boost::algorithm Boost::asio Boost::beast Threads::Threads)
|
||||
add_executable(ship_streamer ship_streamer.cpp)
|
||||
target_link_libraries(ship_streamer abieos Boost::program_options Boost::system Threads::Threads)
|
||||
target_link_libraries(ship_streamer abieos Boost::program_options Boost::system Boost::asio Boost::beast Threads::Threads)
|
||||
|
||||
add_test(NAME cluster_launcher COMMAND tests/cluster_launcher.py -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_property(TEST cluster_launcher PROPERTY LABELS nonparallelizable_tests)
|
||||
@@ -183,7 +185,10 @@ set_property(TEST nested_container_multi_index_test PROPERTY LABELS nonparalleli
|
||||
add_test(NAME nodeos_run_check_test COMMAND tests/nodeos_run_test.py -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_property(TEST nodeos_run_check_test PROPERTY LABELS nonparallelizable_tests)
|
||||
|
||||
|
||||
add_test(NAME p2p_multiple_listen_test COMMAND tests/p2p_multiple_listen_test.py -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_property(TEST p2p_multiple_listen_test PROPERTY LABELS nonparallelizable_tests)
|
||||
add_test(NAME p2p_no_listen_test COMMAND tests/p2p_no_listen_test.py -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_property(TEST p2p_no_listen_test PROPERTY LABELS nonparallelizable_tests)
|
||||
|
||||
# needs iproute-tc or iproute2 depending on platform
|
||||
#add_test(NAME p2p_high_latency_test COMMAND tests/p2p_high_latency_test.py -v WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
@@ -263,7 +268,7 @@ set_property(TEST nodeos_repeat_transaction_lr_test PROPERTY LABELS long_running
|
||||
add_test(NAME light_validation_sync_test COMMAND tests/light_validation_sync_test.py -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_property(TEST light_validation_sync_test PROPERTY LABELS nonparallelizable_tests)
|
||||
|
||||
add_test(NAME auto_bp_peering_test COMMAND tests/auto_bp_peering_test.py ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
add_test(NAME auto_bp_peering_test COMMAND tests/auto_bp_peering_test.py -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_property(TEST auto_bp_peering_test PROPERTY LABELS long_running_tests)
|
||||
|
||||
add_test(NAME gelf_test COMMAND tests/gelf_test.py ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
@@ -1593,6 +1593,6 @@ class Cluster(object):
|
||||
firstTrxs.append(line.rstrip('\n'))
|
||||
Utils.Print(f"first transactions: {firstTrxs}")
|
||||
status = node.waitForTransactionsInBlock(firstTrxs)
|
||||
if status is None:
|
||||
if not status:
|
||||
Utils.Print('ERROR: Failed to spin up transaction generators: never received first transactions')
|
||||
return status
|
||||
|
||||
@@ -75,8 +75,8 @@ class Node(Transactions):
|
||||
self.fetchBlock = lambda blockNum: self.processUrllibRequest("chain", "get_block", {"block_num_or_id":blockNum}, silentErrors=False, exitOnError=True)
|
||||
self.fetchKeyCommand = lambda: "[trx][trx][ref_block_num]"
|
||||
self.fetchRefBlock = lambda trans: trans["trx"]["trx"]["ref_block_num"]
|
||||
self.cleosLimit = ""
|
||||
self.fetchHeadBlock = lambda node, headBlock: node.processUrllibRequest("chain", "get_block", {"block_num_or_id":headBlock}, silentErrors=False, exitOnError=True)
|
||||
self.cleosLimit = ""
|
||||
|
||||
else:
|
||||
self.fetchTransactionCommand = lambda: "get transaction_trace"
|
||||
@@ -84,8 +84,11 @@ class Node(Transactions):
|
||||
self.fetchBlock = lambda blockNum: self.processUrllibRequest("trace_api", "get_block", {"block_num":blockNum}, silentErrors=False, exitOnError=True)
|
||||
self.fetchKeyCommand = lambda: "[transaction][transaction_header][ref_block_num]"
|
||||
self.fetchRefBlock = lambda trans: trans["block_num"]
|
||||
self.cleosLimit = "--time-limit 999"
|
||||
self.fetchHeadBlock = lambda node, headBlock: node.processUrllibRequest("chain", "get_block_info", {"block_num":headBlock}, silentErrors=False, exitOnError=True)
|
||||
if 'v3.1' in self.nodeosVers:
|
||||
self.cleosLimit = ""
|
||||
else:
|
||||
self.cleosLimit = "--time-limit 999"
|
||||
|
||||
def __str__(self):
|
||||
return "Host: %s, Port:%d, NodeNum:%s, Pid:%s" % (self.host, self.port, self.nodeId, self.pid)
|
||||
|
||||
@@ -200,8 +200,8 @@ class cluster_generator:
|
||||
cfg.add_argument('--enable-gelf-logging', action='store_true', help='enable gelf logging appender in logging configuration file', default=False)
|
||||
cfg.add_argument('--gelf-endpoint', help='hostname:port or ip:port of GELF endpoint', default='128.0.0.1:12201')
|
||||
cfg.add_argument('--template', help='the startup script template', default='testnet.template')
|
||||
cfg.add_argument('--max-block-cpu-usage', type=int, help='the "max-block-cpu-usage" value to use in the genesis.json file', default=200000)
|
||||
cfg.add_argument('--max-transaction-cpu-usage', type=int, help='the "max-transaction-cpu-usage" value to use in the genesis.json file', default=150000)
|
||||
cfg.add_argument('--max-block-cpu-usage', type=int, help='the "max-block-cpu-usage" value to use in the genesis.json file', default=None)
|
||||
cfg.add_argument('--max-transaction-cpu-usage', type=int, help='the "max-transaction-cpu-usage" value to use in the genesis.json file', default=None)
|
||||
cfg.add_argument('--logging-level', type=fc_log_level, help='Provide the "level" value to use in the logging.json file')
|
||||
cfg.add_argument('--logging-level-map', type=json.loads, help='JSON string of a logging level dictionary to use in the logging.json file for specific nodes, matching based on node number. Ex: {"bios":"off","00":"info"}')
|
||||
cfg.add_argument('--is-nodeos-v2', action='store_true', help='Toggles old nodeos compatibility', default=False)
|
||||
@@ -359,9 +359,9 @@ class cluster_generator:
|
||||
'net_usage_leeway': 500,
|
||||
'context_free_discount_net_usage_num': 20,
|
||||
'context_free_discount_net_usage_den': 100,
|
||||
'max_block_cpu_usage': self.args.max_block_cpu_usage,
|
||||
'max_block_cpu_usage': 500000 if self.args.max_block_cpu_usage is None else self.args.max_block_cpu_usage,
|
||||
'target_block_cpu_usage_pct': 1000,
|
||||
'max_transaction_cpu_usage': self.args.max_transaction_cpu_usage,
|
||||
'max_transaction_cpu_usage': 475000 if self.args.max_transaction_cpu_usage is None else self.args.max_transaction_cpu_usage,
|
||||
'min_transaction_cpu_usage': 100,
|
||||
'max_transaction_lifetime': 3600,
|
||||
'deferred_trx_expiration_window': 600,
|
||||
@@ -375,8 +375,8 @@ class cluster_generator:
|
||||
with open(genesis_path, 'r') as f:
|
||||
genesis = json.load(f)
|
||||
genesis['initial_key'] = self.network.nodes['bios'].keys[0].pubkey
|
||||
genesis['max_block_cpu_usage'] = self.args.max_block_cpu_usage
|
||||
genesis['max_transaction_cpu_usage'] = self.args.max_transaction_cpu_usage
|
||||
if self.args.max_block_cpu_usage is not None: genesis['initial_configuration']['max_block_cpu_usage'] = self.args.max_block_cpu_usage
|
||||
if self.args.max_transaction_cpu_usage is not None: genesis['initial_configuration']['max_transaction_cpu_usage'] = self.args.max_transaction_cpu_usage
|
||||
return genesis
|
||||
|
||||
def write_genesis_file(self, node, genesis):
|
||||
|
||||
@@ -161,11 +161,13 @@ class Utils:
|
||||
return path
|
||||
|
||||
@staticmethod
|
||||
def rmNodeDataDir(ext, rmState=True, rmBlocks=True):
|
||||
def rmNodeDataDir(ext, rmState=True, rmBlocks=True, rmStateHist=True):
|
||||
if rmState:
|
||||
shutil.rmtree(Utils.getNodeDataDir(ext, "state"))
|
||||
if rmBlocks:
|
||||
shutil.rmtree(Utils.getNodeDataDir(ext, "blocks"))
|
||||
if rmStateHist:
|
||||
shutil.rmtree(Utils.getNodeDataDir(ext, "state-history"), ignore_errors=True)
|
||||
|
||||
@staticmethod
|
||||
def getNodeConfigDir(ext, relativeDir=None, trailingSlash=False):
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
import signal
|
||||
import time
|
||||
import socket
|
||||
|
||||
from TestHarness import Cluster, TestHelper, Utils, WalletMgr, ReturnType
|
||||
from TestHarness import Cluster, TestHelper, Utils, WalletMgr
|
||||
|
||||
###############################################################
|
||||
# auto_bp_peering_test
|
||||
@@ -35,7 +33,7 @@ Utils.Debug = args.v
|
||||
dumpErrorDetails = args.dump_error_details
|
||||
keepLogs = args.keep_logs
|
||||
|
||||
# Setup cluster and it's wallet manager
|
||||
# Setup cluster and its wallet manager
|
||||
walletMgr = WalletMgr(True)
|
||||
cluster = Cluster(unshared=args.unshared, keepRunning=args.leave_running, keepLogs=args.keep_logs)
|
||||
cluster.setWalletMgr(walletMgr)
|
||||
@@ -47,12 +45,17 @@ auto_bp_peer_args = ""
|
||||
for nodeId in range(0, producerNodes):
|
||||
producer_name = "defproducer" + chr(ord('a') + nodeId)
|
||||
port = cluster.p2pBasePort + nodeId
|
||||
hostname = "localhost:" + str(port)
|
||||
if producer_name == 'defproducerf':
|
||||
hostname = 'ext-ip0:9999'
|
||||
elif producer_name == 'defproducerk':
|
||||
hostname = socket.gethostname() + ':9886'
|
||||
else:
|
||||
hostname = "localhost:" + str(port)
|
||||
peer_names[hostname] = producer_name
|
||||
auto_bp_peer_args += (" --p2p-auto-bp-peer " + producer_name + "," + hostname)
|
||||
|
||||
|
||||
def neigbors_in_schedule(name, schedule):
|
||||
def neighbors_in_schedule(name, schedule):
|
||||
index = schedule.index(name)
|
||||
result = []
|
||||
num = len(schedule)
|
||||
@@ -71,6 +74,9 @@ try:
|
||||
for nodeId in range(0, producerNodes):
|
||||
specificNodeosArgs[nodeId] = auto_bp_peer_args
|
||||
|
||||
specificNodeosArgs[5] = specificNodeosArgs[5] + ' --p2p-server-address ext-ip0:9999'
|
||||
specificNodeosArgs[10] = specificNodeosArgs[10] + ' --p2p-server-address ""'
|
||||
|
||||
TestHelper.printSystemInfo("BEGIN")
|
||||
cluster.launch(
|
||||
prodCount=producerCountInEachNode,
|
||||
@@ -113,7 +119,7 @@ try:
|
||||
|
||||
peers = peers.sort()
|
||||
name = "defproducer" + chr(ord('a') + nodeId)
|
||||
expected_peers = neigbors_in_schedule(name, scheduled_producers)
|
||||
expected_peers = neighbors_in_schedule(name, scheduled_producers)
|
||||
if peers != expected_peers:
|
||||
Utils.Print("ERROR: expect {} has connections to {}, got connections to {}".format(
|
||||
name, expected_peers, peers))
|
||||
|
||||
@@ -94,7 +94,7 @@ try:
|
||||
testSuccessful=False
|
||||
|
||||
Print("Configure and launch txn generators")
|
||||
targetTpsPerGenerator = 100
|
||||
targetTpsPerGenerator = 10
|
||||
testTrxGenDurationSec=60
|
||||
trxGeneratorCnt=1
|
||||
cluster.launchTrxGenerators(contractOwnerAcctName=cluster.eosioAccount.name, acctNamesList=[accounts[0].name,accounts[1].name],
|
||||
|
||||
@@ -103,7 +103,7 @@ try:
|
||||
waitForBlock(node0, blockNum, blockType=BlockType.lib)
|
||||
|
||||
Print("Configure and launch txn generators")
|
||||
targetTpsPerGenerator = 100
|
||||
targetTpsPerGenerator = 10
|
||||
testTrxGenDurationSec=60*60
|
||||
cluster.launchTrxGenerators(contractOwnerAcctName=cluster.eosioAccount.name, acctNamesList=[account1Name, account2Name],
|
||||
acctPrivKeysList=[account1PrivKey,account2PrivKey], nodeId=node0.nodeId,
|
||||
|
||||
Executable
+103
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import signal
|
||||
|
||||
from TestHarness import Cluster, TestHelper, Utils, WalletMgr
|
||||
|
||||
###############################################################
|
||||
# p2p_multiple_listen_test
|
||||
#
|
||||
# Test nodeos ability to listen on multiple ports for p2p
|
||||
#
|
||||
###############################################################
|
||||
|
||||
Print=Utils.Print
|
||||
errorExit=Utils.errorExit
|
||||
|
||||
args=TestHelper.parse_args({"-p","-n","-d","--keep-logs"
|
||||
,"--dump-error-details","-v"
|
||||
,"--leave-running","--unshared"})
|
||||
pnodes=args.p
|
||||
delay=args.d
|
||||
debug=args.v
|
||||
total_nodes=5
|
||||
dumpErrorDetails=args.dump_error_details
|
||||
|
||||
Utils.Debug=debug
|
||||
testSuccessful=False
|
||||
|
||||
cluster=Cluster(unshared=args.unshared, keepRunning=args.leave_running, keepLogs=args.keep_logs)
|
||||
walletMgr=WalletMgr(True)
|
||||
|
||||
try:
|
||||
TestHelper.printSystemInfo("BEGIN")
|
||||
|
||||
cluster.setWalletMgr(walletMgr)
|
||||
|
||||
Print(f'producing nodes: {pnodes}, delay between nodes launch: {delay} second{"s" if delay != 1 else ""}')
|
||||
|
||||
Print("Stand up cluster")
|
||||
specificArgs = {
|
||||
'0': '--agent-name node-00 --p2p-listen-endpoint 0.0.0.0:9876 --p2p-listen-endpoint 0.0.0.0:9779 --p2p-server-address ext-ip0:20000 --p2p-server-address ext-ip1:20001 --plugin eosio::net_api_plugin',
|
||||
'2': '--agent-name node-02 --p2p-peer-address localhost:9779 --plugin eosio::net_api_plugin',
|
||||
'4': '--agent-name node-04 --p2p-peer-address localhost:9876 --plugin eosio::net_api_plugin',
|
||||
}
|
||||
if cluster.launch(pnodes=pnodes, totalNodes=total_nodes, topo='line', delay=delay,
|
||||
specificExtraNodeosArgs=specificArgs) is False:
|
||||
errorExit("Failed to stand up eos cluster.")
|
||||
|
||||
# Be sure all nodes start out connected (bios node omitted from diagram for brevity)
|
||||
# node00 node01 node02 node03 node04
|
||||
# localhost:9876 -> localhost:9877 -> localhost:9878 -> localhost:9879 -> localhost:9880
|
||||
# localhost:9779 ^ | |
|
||||
# ^ +---------------------------+ |
|
||||
# +------------------------------------------------------------------------+
|
||||
cluster.waitOnClusterSync(blockAdvancing=5)
|
||||
# Shut down bios node, which is connected to all other nodes in all topologies
|
||||
cluster.biosNode.kill(signal.SIGTERM)
|
||||
# Shut down second node, interrupting the default connections between it and nodes 00 and 02
|
||||
cluster.getNode(1).kill(signal.SIGTERM)
|
||||
# Shut down the fourth node, interrupting the default connections between it and nodes 02 and 04
|
||||
cluster.getNode(3).kill(signal.SIGTERM)
|
||||
# Be sure all remaining nodes continue to sync via the two listen ports on node 00
|
||||
# node00 node01 node02 node03 node04
|
||||
# localhost:9876 offline localhost:9878 offline localhost:9880
|
||||
# localhost:9779 ^ | |
|
||||
# ^ +---------------------------+ |
|
||||
# +------------------------------------------------------------------------+
|
||||
cluster.waitOnClusterSync(blockAdvancing=5)
|
||||
connections = cluster.nodes[0].processUrllibRequest('net', 'connections')
|
||||
open_socket_count = 0
|
||||
for conn in connections['payload']:
|
||||
if conn['is_socket_open']:
|
||||
open_socket_count += 1
|
||||
if conn['last_handshake']['agent'] == 'node-02':
|
||||
assert conn['last_handshake']['p2p_address'].split()[0] == 'localhost:9878', f"Connected node is listening on '{conn['last_handshake']['p2p_address'].split()[0]}' instead of port 9878"
|
||||
elif conn['last_handshake']['agent'] == 'node-04':
|
||||
assert conn['last_handshake']['p2p_address'].split()[0] == 'localhost:9880', f"Connected node is listening on '{conn['last_handshake']['p2p_address'].split()[0]}' instead of port 9880"
|
||||
assert open_socket_count == 2, 'Node 0 is expected to have only two open sockets'
|
||||
|
||||
connections = cluster.nodes[2].processUrllibRequest('net', 'connections')
|
||||
open_socket_count = 0
|
||||
for conn in connections['payload']:
|
||||
if conn['is_socket_open']:
|
||||
open_socket_count += 1
|
||||
assert conn['last_handshake']['agent'] == 'node-00', f"Connected node identifed as '{conn['last_handshake']['agent']}' instead of node-00"
|
||||
assert conn['last_handshake']['p2p_address'].split()[0] == 'ext-ip0:20000', f"Connected node is advertising '{conn['last_handshake']['p2p_address'].split()[0]}' instead of ext-ip0:20000"
|
||||
assert open_socket_count == 1, 'Node 2 is expected to have only one open socket'
|
||||
|
||||
connections = cluster.nodes[4].processUrllibRequest('net', 'connections')
|
||||
open_socket_count = 0
|
||||
for conn in connections['payload']:
|
||||
if conn['is_socket_open']:
|
||||
open_socket_count += 1
|
||||
assert conn['last_handshake']['agent'] == 'node-00', f"Connected node identifed as '{conn['last_handshake']['agent']}' instead of node-00"
|
||||
assert conn['last_handshake']['p2p_address'].split()[0] == 'ext-ip1:20001', f"Connected node is advertising '{conn['last_handshake']['p2p_address'].split()[0]} 'instead of ext-ip1:20001"
|
||||
assert open_socket_count == 1, 'Node 4 is expected to have only one open socket'
|
||||
|
||||
testSuccessful=True
|
||||
finally:
|
||||
TestHelper.shutdown(cluster, walletMgr, testSuccessful=testSuccessful, dumpErrorDetails=dumpErrorDetails)
|
||||
|
||||
exitCode = 0 if testSuccessful else 1
|
||||
exit(exitCode)
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import errno
|
||||
import pathlib
|
||||
import shutil
|
||||
import signal
|
||||
import socket
|
||||
import time
|
||||
|
||||
from TestHarness import Node, TestHelper, Utils
|
||||
|
||||
###############################################################
|
||||
# p2p_no_listen_test
|
||||
#
|
||||
# Test nodeos disabling p2p
|
||||
#
|
||||
###############################################################
|
||||
|
||||
Print=Utils.Print
|
||||
errorExit=Utils.errorExit
|
||||
|
||||
args=TestHelper.parse_args({"--keep-logs","-v","--leave-running","--unshared"})
|
||||
debug=args.v
|
||||
|
||||
Utils.Debug=debug
|
||||
testSuccessful=False
|
||||
|
||||
try:
|
||||
TestHelper.printSystemInfo("BEGIN")
|
||||
|
||||
cmd = [
|
||||
Utils.EosServerPath,
|
||||
'-e',
|
||||
'-p',
|
||||
'eosio',
|
||||
'--p2p-listen-endpoint',
|
||||
'',
|
||||
'--plugin',
|
||||
'eosio::chain_api_plugin',
|
||||
'--config-dir',
|
||||
Utils.ConfigDir,
|
||||
'--data-dir',
|
||||
Utils.DataDir,
|
||||
'--http-server-address',
|
||||
'localhost:8888'
|
||||
]
|
||||
node = Node('localhost', '8888', '00', data_dir=pathlib.Path(Utils.DataDir),
|
||||
config_dir=pathlib.Path(Utils.ConfigDir), cmd=cmd)
|
||||
|
||||
time.sleep(1)
|
||||
if not node.verifyAlive():
|
||||
raise RuntimeError
|
||||
time.sleep(10)
|
||||
node.waitForBlock(5)
|
||||
|
||||
s = socket.socket()
|
||||
err = s.connect_ex(('localhost',9876))
|
||||
assert err == errno.ECONNREFUSED, 'Connection to port 9876 must be refused'
|
||||
|
||||
testSuccessful=True
|
||||
finally:
|
||||
Utils.ShuttingDown=True
|
||||
|
||||
if not args.leave_running:
|
||||
node.kill(signal.SIGTERM)
|
||||
|
||||
if not (args.leave_running or args.keep_logs or not testSuccessful):
|
||||
shutil.rmtree(Utils.DataPath, ignore_errors=True)
|
||||
|
||||
if testSuccessful:
|
||||
Utils.Print("Test succeeded.")
|
||||
else:
|
||||
Utils.Print("Test failed.")
|
||||
|
||||
exitCode = 0 if testSuccessful else 1
|
||||
exit(exitCode)
|
||||
@@ -11,7 +11,7 @@
|
||||
"context_free_discount_net_usage_den": 100,
|
||||
"max_block_cpu_usage": 500000,
|
||||
"target_block_cpu_usage_pct": 500,
|
||||
"max_transaction_cpu_usage": 150000,
|
||||
"max_transaction_cpu_usage": 475000,
|
||||
"min_transaction_cpu_usage": 0,
|
||||
"max_transaction_lifetime": 3600,
|
||||
"deferred_trx_expiration_window": 600,
|
||||
|
||||
@@ -124,7 +124,7 @@ class PerformanceTestBasic:
|
||||
|
||||
def configureValidationNodes():
|
||||
validationNodeSpecificNodeosStr = ""
|
||||
if self.nodeosVers == "v2":
|
||||
if "v2" in self.nodeosVers:
|
||||
validationNodeSpecificNodeosStr += '--plugin eosio::history_api_plugin --filter-on "*" '
|
||||
else:
|
||||
#If prodsEnableTraceApi, then Cluster configures all nodes with trace_api_plugin so no need to duplicate here
|
||||
@@ -139,7 +139,8 @@ class PerformanceTestBasic:
|
||||
apiNodeSpecificNodeosStr = ""
|
||||
apiNodeSpecificNodeosStr += "--plugin eosio::chain_api_plugin "
|
||||
apiNodeSpecificNodeosStr += "--plugin eosio::net_api_plugin "
|
||||
apiNodeSpecificNodeosStr += f"--read-only-threads {self.apiNodesReadOnlyThreadCount} "
|
||||
if "v4" in self.nodeosVers:
|
||||
apiNodeSpecificNodeosStr += f"--read-only-threads {self.apiNodesReadOnlyThreadCount} "
|
||||
if apiNodeSpecificNodeosStr:
|
||||
self.specificExtraNodeosArgs.update({f"{nodeId}" : apiNodeSpecificNodeosStr for nodeId in self._apiNodeIds})
|
||||
|
||||
@@ -148,8 +149,8 @@ class PerformanceTestBasic:
|
||||
if self.apiNodeCount > 0:
|
||||
configureApiNodes()
|
||||
|
||||
assert self.nodeosVers != "v1" and self.nodeosVers != "v0", f"nodeos version {Utils.getNodeosVersion().split('.')[0]} is unsupported by performance test"
|
||||
if self.nodeosVers == "v2":
|
||||
assert "v1" not in self.nodeosVers and "v0" not in self.nodeosVers, f"nodeos version {Utils.getNodeosVersion()} is unsupported by performance test"
|
||||
if "v2" in self.nodeosVers:
|
||||
self.writeTrx = lambda trxDataFile, blockNum, trx: [trxDataFile.write(f"{trx['trx']['id']},{blockNum},{trx['cpu_usage_us']},{trx['net_usage_words']}\n")]
|
||||
self.createBlockData = lambda block, blockTransactionTotal, blockNetTotal, blockCpuTotal: log_reader.blockData(blockId=block["payload"]["id"], blockNum=block['payload']['block_num'], transactions=blockTransactionTotal, net=blockNetTotal, cpu=blockCpuTotal, producer=block["payload"]["producer"], status=block["payload"]["confirmed"], _timestamp=block["payload"]["timestamp"])
|
||||
self.updateTrxDict = lambda blockNum, transaction, trxDict: trxDict.update(dict([(transaction['trx']['id'], log_reader.trxData(blockNum, transaction['cpu_usage_us'], transaction['net_usage_words']))]))
|
||||
@@ -286,7 +287,7 @@ class PerformanceTestBasic:
|
||||
|
||||
def isOnBlockTransaction(self, transaction):
|
||||
# v2 history does not include onblock
|
||||
if self.clusterConfig.nodeosVers == "v2":
|
||||
if "v2" in self.clusterConfig.nodeosVers:
|
||||
return False
|
||||
else:
|
||||
if transaction['actions'][0]['account'] != 'eosio' or transaction['actions'][0]['action'] != 'onblock':
|
||||
@@ -642,8 +643,8 @@ class PerformanceTestBasic:
|
||||
httpPluginArgs = HttpPluginArgs(httpMaxBytesInFlightMb=args.http_max_bytes_in_flight_mb, httpMaxInFlightRequests=args.http_max_in_flight_requests,
|
||||
httpMaxResponseTimeMs=args.http_max_response_time_ms, httpThreads=args.http_threads)
|
||||
netPluginArgs = NetPluginArgs(netThreads=args.net_threads, maxClients=0)
|
||||
nodeosVers=Utils.getNodeosVersion().split('.')[0]
|
||||
resourceMonitorPluginArgs = ResourceMonitorPluginArgs(resourceMonitorNotShutdownOnThresholdExceeded=not nodeosVers == "v2")
|
||||
nodeosVers=Utils.getNodeosVersion()
|
||||
resourceMonitorPluginArgs = ResourceMonitorPluginArgs(resourceMonitorNotShutdownOnThresholdExceeded=not "v2" in nodeosVers)
|
||||
ENA = PerformanceTestBasic.ClusterConfig.ExtraNodeosArgs
|
||||
extraNodeosArgs = ENA(chainPluginArgs=chainPluginArgs, httpPluginArgs=httpPluginArgs, producerPluginArgs=producerPluginArgs, netPluginArgs=netPluginArgs,
|
||||
resourceMonitorPluginArgs=resourceMonitorPluginArgs)
|
||||
|
||||
@@ -100,6 +100,12 @@ def startCluster():
|
||||
specificExtraNodeosArgs={}
|
||||
# producer nodes will be mapped to 0 through pnodes-1, so the number pnodes is the no-producing API node
|
||||
specificExtraNodeosArgs[pnodes]=" --plugin eosio::net_api_plugin"
|
||||
specificExtraNodeosArgs[pnodes]+=" --read-only-write-window-time-us "
|
||||
specificExtraNodeosArgs[pnodes]+=" 10000 "
|
||||
specificExtraNodeosArgs[pnodes]+=" --read-only-read-window-time-us "
|
||||
specificExtraNodeosArgs[pnodes]+=" 490000 "
|
||||
specificExtraNodeosArgs[pnodes]+=" --eos-vm-oc-cache-size-mb "
|
||||
specificExtraNodeosArgs[pnodes]+=" 1 " # set small so there is churn
|
||||
specificExtraNodeosArgs[pnodes]+=" --read-only-threads "
|
||||
specificExtraNodeosArgs[pnodes]+=str(args.read_only_threads)
|
||||
if args.eos_vm_oc_enable:
|
||||
|
||||
+10
-6
@@ -154,16 +154,20 @@ int main(int argc, char* argv[]) {
|
||||
// validate after streaming, so that invalid entry is included in the output
|
||||
uint32_t this_block_num = 0;
|
||||
if( result_document[1].HasMember("this_block") && result_document[1]["this_block"].IsObject() ) {
|
||||
if( result_document[1]["this_block"].HasMember("block_num") && result_document[1]["this_block"]["block_num"].IsUint() ) {
|
||||
this_block_num = result_document[1]["this_block"]["block_num"].GetUint();
|
||||
const auto& this_block = result_document[1]["this_block"];
|
||||
if( this_block.HasMember("block_num") && this_block["block_num"].IsUint() ) {
|
||||
this_block_num = this_block["block_num"].GetUint();
|
||||
}
|
||||
std::string this_block_id;
|
||||
if( result_document[1]["this_block"].HasMember("block_id") && result_document[1]["this_block"]["block_id"].IsString() ) {
|
||||
this_block_id = result_document[1]["this_block"]["block_id"].GetString();
|
||||
if( this_block.HasMember("block_id") && this_block["block_id"].IsString() ) {
|
||||
this_block_id = this_block["block_id"].GetString();
|
||||
}
|
||||
std::string prev_block_id;
|
||||
if( result_document[1]["prev_block"].HasMember("block_id") && result_document[1]["prev_block"]["block_id"].IsString() ) {
|
||||
prev_block_id = result_document[1]["prev_block"]["block_id"].GetString();
|
||||
if( result_document[1].HasMember("prev_block") && result_document[1]["prev_block"].IsObject() ) {
|
||||
const auto& prev_block = result_document[1]["prev_block"];
|
||||
if ( prev_block.HasMember("block_id") && prev_block["block_id"].IsString() ) {
|
||||
prev_block_id = prev_block["block_id"].GetString();
|
||||
}
|
||||
}
|
||||
if( !irreversible_only && !this_block_id.empty() && !prev_block_id.empty() ) {
|
||||
// verify forks were sent
|
||||
|
||||
@@ -50,6 +50,13 @@ testSuccessful=False
|
||||
WalletdName=Utils.EosWalletName
|
||||
shipTempDir=None
|
||||
|
||||
def getLatestSnapshot(nodeId):
|
||||
snapshotDir = os.path.join(Utils.getNodeDataDir(nodeId), "snapshots")
|
||||
snapshotDirContents = os.listdir(snapshotDir)
|
||||
assert len(snapshotDirContents) > 0
|
||||
snapshotDirContents.sort()
|
||||
return os.path.join(snapshotDir, snapshotDirContents[-1])
|
||||
|
||||
try:
|
||||
TestHelper.printSystemInfo("BEGIN")
|
||||
|
||||
@@ -63,7 +70,7 @@ try:
|
||||
|
||||
shipNodeNum = 1
|
||||
specificExtraNodeosArgs={}
|
||||
specificExtraNodeosArgs[shipNodeNum]="--plugin eosio::state_history_plugin --disable-replay-opts --trace-history --chain-state-history --plugin eosio::net_api_plugin "
|
||||
specificExtraNodeosArgs[shipNodeNum]="--plugin eosio::state_history_plugin --disable-replay-opts --trace-history --chain-state-history --plugin eosio::net_api_plugin --plugin eosio::producer_api_plugin "
|
||||
# producer nodes will be mapped to 0 through totalProducerNodes-1, so the number totalProducerNodes will be the non-producing node
|
||||
specificExtraNodeosArgs[totalProducerNodes]="--plugin eosio::test_control_api_plugin "
|
||||
|
||||
@@ -115,13 +122,18 @@ try:
|
||||
trans=node.regproducer(cluster.defProducerAccounts[prod], "http://mysite.com", 0, waitForTransBlock=False, exitOnError=True)
|
||||
|
||||
# create accounts via eosio as otherwise a bid is needed
|
||||
transferAmount="100000000.0000 {0}".format(CORE_SYMBOL)
|
||||
for account in accounts:
|
||||
Print(f"Create new account {account.name} via {cluster.eosioAccount.name} with private key: {account.activePrivateKey}")
|
||||
trans=nonProdNode.createInitializeAccount(account, cluster.eosioAccount, stakedDeposit=0, waitForTransBlock=True, stakeNet=10000, stakeCPU=10000, buyRAM=10000000, exitOnError=True)
|
||||
transferAmount="100000000.0000 {0}".format(CORE_SYMBOL)
|
||||
trans=nonProdNode.createInitializeAccount(account, cluster.eosioAccount, stakedDeposit=0, waitForTransBlock=False, stakeNet=10000, stakeCPU=10000, buyRAM=10000000, exitOnError=True)
|
||||
nonProdNode.waitForTransBlockIfNeeded(trans, True, exitOnError=True)
|
||||
for account in accounts:
|
||||
Print(f"Transfer funds {transferAmount} from account {cluster.eosioAccount.name} to {account.name}")
|
||||
nonProdNode.transferFunds(cluster.eosioAccount, account, transferAmount, "test transfer", waitForTransBlock=False)
|
||||
trans=nonProdNode.transferFunds(cluster.eosioAccount, account, transferAmount, "test transfer", waitForTransBlock=False)
|
||||
nonProdNode.waitForTransBlockIfNeeded(trans, True, exitOnError=True)
|
||||
for account in accounts:
|
||||
trans=nonProdNode.delegatebw(account, 20000000.0000, 20000000.0000, waitForTransBlock=False, exitOnError=True)
|
||||
nonProdNode.waitForTransBlockIfNeeded(trans, True, exitOnError=True)
|
||||
|
||||
# *** vote using accounts ***
|
||||
|
||||
@@ -142,6 +154,19 @@ try:
|
||||
cluster.waitOnClusterSync(blockAdvancing=3)
|
||||
Print("Shutdown unneeded bios node")
|
||||
cluster.biosNode.kill(signal.SIGTERM)
|
||||
|
||||
Print("Configure and launch txn generators")
|
||||
targetTpsPerGenerator = 10
|
||||
testTrxGenDurationSec=60*60
|
||||
numTrxGenerators=2
|
||||
cluster.launchTrxGenerators(contractOwnerAcctName=cluster.eosioAccount.name, acctNamesList=[accounts[0].name, accounts[1].name],
|
||||
acctPrivKeysList=[accounts[0].activePrivateKey,accounts[1].activePrivateKey], nodeId=prodNode1.nodeId,
|
||||
tpsPerGenerator=targetTpsPerGenerator, numGenerators=numTrxGenerators, durationSec=testTrxGenDurationSec,
|
||||
waitToComplete=False)
|
||||
|
||||
status = cluster.waitForTrxGeneratorsSpinup(nodeId=prodNode1.nodeId, numGenerators=numTrxGenerators)
|
||||
assert status is not None and status is not False, "ERROR: Failed to spinup Transaction Generators"
|
||||
|
||||
prodNode0.waitForProducer("defproducerc")
|
||||
|
||||
block_range = 350
|
||||
@@ -219,9 +244,61 @@ try:
|
||||
block_num += 1
|
||||
assert block_num-1 == end_block_num, f"{block_num-1} != {end_block_num}"
|
||||
|
||||
Print("Generate snapshot")
|
||||
shipNode.createSnapshot()
|
||||
|
||||
Print("Shutdown state_history_plugin nodeos")
|
||||
shipNode.kill(signal.SIGTERM)
|
||||
|
||||
Print("Shutdown bridge node")
|
||||
nonProdNode.kill(signal.SIGTERM)
|
||||
|
||||
Print("Test starting ship from snapshot")
|
||||
Utils.rmNodeDataDir(shipNodeNum)
|
||||
isRelaunchSuccess = shipNode.relaunch(chainArg=" --snapshot {}".format(getLatestSnapshot(shipNodeNum)))
|
||||
assert isRelaunchSuccess, "relaunch from snapshot failed"
|
||||
|
||||
afterSnapshotBlockNum = shipNode.getBlockNum()
|
||||
|
||||
Print("Verify we can stream from ship after start from a snapshot with no incoming trxs")
|
||||
start_block_num = afterSnapshotBlockNum
|
||||
block_range = 0
|
||||
end_block_num = start_block_num + block_range
|
||||
cmd = f"{shipClient} --start-block-num {start_block_num} --end-block-num {end_block_num} --fetch-block --fetch-traces --fetch-deltas"
|
||||
if Utils.Debug: Utils.Print(f"cmd: {cmd}")
|
||||
clients = []
|
||||
files = []
|
||||
starts = []
|
||||
for i in range(0, args.num_clients):
|
||||
start = time.perf_counter()
|
||||
outFile = open(f"{shipClientFilePrefix}{i}_snapshot.out", "w")
|
||||
errFile = open(f"{shipClientFilePrefix}{i}_snapshot.err", "w")
|
||||
Print(f"Start client {i}")
|
||||
popen=Utils.delayedCheckOutput(cmd, stdout=outFile, stderr=errFile)
|
||||
starts.append(time.perf_counter())
|
||||
clients.append((popen, cmd))
|
||||
files.append((outFile, errFile))
|
||||
Print(f"Client {i} started, Ship node head is: {shipNode.getBlockNum()}")
|
||||
|
||||
Print(f"Stopping all {args.num_clients} clients")
|
||||
for index, (popen, _), (out, err), start in zip(range(len(clients)), clients, files, starts):
|
||||
popen.wait()
|
||||
Print(f"Stopped client {index}. Ran for {time.perf_counter() - start:.3f} seconds.")
|
||||
out.close()
|
||||
err.close()
|
||||
outFile = open(f"{shipClientFilePrefix}{index}_snapshot.out", "r")
|
||||
data = json.load(outFile)
|
||||
block_num = start_block_num
|
||||
for i in data:
|
||||
# fork can cause block numbers to be repeated
|
||||
this_block_num = i['get_blocks_result_v0']['this_block']['block_num']
|
||||
if this_block_num < block_num:
|
||||
block_num = this_block_num
|
||||
assert block_num == this_block_num, f"{block_num} != {this_block_num}"
|
||||
assert isinstance(i['get_blocks_result_v0']['deltas'], str) # verify deltas in result
|
||||
block_num += 1
|
||||
assert block_num-1 == end_block_num, f"{block_num-1} != {end_block_num}"
|
||||
|
||||
testSuccessful = True
|
||||
finally:
|
||||
TestHelper.shutdown(cluster, walletMgr, testSuccessful=testSuccessful, dumpErrorDetails=dumpErrorDetails)
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <test_utils.hpp>
|
||||
#include <eosio/producer_plugin/producer_plugin.hpp>
|
||||
#include <eosio/testing/tester.hpp>
|
||||
#include <eosio/chain/block.hpp>
|
||||
#include <eosio/chain/config.hpp>
|
||||
#include <eosio/chain/types.hpp>
|
||||
#include <eosio/chain/controller.hpp>
|
||||
#include <eosio/chain/genesis_state.hpp>
|
||||
#include <eosio/chain/thread_utils.hpp>
|
||||
#include <eosio/chain/transaction.hpp>
|
||||
#include <eosio/chain/transaction_metadata.hpp>
|
||||
#include <eosio/chain/trace.hpp>
|
||||
#include <eosio/chain/wasm_interface_collection.hpp>
|
||||
#include <eosio/chain/name.hpp>
|
||||
#include <eosio/chain/application.hpp>
|
||||
|
||||
#include <contracts.hpp>
|
||||
|
||||
#include <fc/scoped_exit.hpp>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
namespace {
|
||||
using namespace eosio;
|
||||
using namespace eosio::chain;
|
||||
using namespace eosio::test_utils;
|
||||
|
||||
auto make_unique_trx() {
|
||||
static uint64_t nextid = 0;
|
||||
++nextid;
|
||||
account_name creator = config::system_account_name;
|
||||
signed_transaction trx;
|
||||
trx.expiration = fc::time_point_sec{fc::time_point::now() + fc::seconds( nextid % 50 == 0 ? 0 : 60 )}; // fail some transactions via expired
|
||||
if( nextid % 10 == 0 ) {
|
||||
// fail some for authorization (read-only transaction should not have authorization)
|
||||
trx.actions.emplace_back( vector<permission_level>{{creator, config::active_name}}, testit{nextid} );
|
||||
} else {
|
||||
vector<permission_level> no_auth{};
|
||||
trx.actions.emplace_back( no_auth, testit{nextid} );
|
||||
}
|
||||
return std::make_shared<packed_transaction>( std::move(trx) );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(read_only_trxs)
|
||||
|
||||
enum class app_init_status { failed, succeeded };
|
||||
|
||||
void test_configs_common(std::vector<const char*>& specific_args, app_init_status expected_status) {
|
||||
fc::temp_directory temp;
|
||||
appbase::scoped_app app;
|
||||
auto temp_dir_str = temp.path().string();
|
||||
|
||||
fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug);
|
||||
std::vector<const char*> argv =
|
||||
{"test", "--data-dir", temp_dir_str.c_str(), "--config-dir", temp_dir_str.c_str()};
|
||||
argv.insert( argv.end(), specific_args.begin(), specific_args.end() );
|
||||
|
||||
// app->initialize() returns a boolean. BOOST_CHECK_EQUAL cannot compare
|
||||
// a boolean with a app_init_status directly
|
||||
bool rc = (expected_status == app_init_status::succeeded) ? true : false;
|
||||
bool result = false;
|
||||
try {
|
||||
result = app->initialize<producer_plugin>( argv.size(), (char**) &argv[0]);
|
||||
} catch(...) {}
|
||||
BOOST_CHECK_EQUAL( result, rc );
|
||||
}
|
||||
|
||||
// --read-only-thread not allowed on producer node
|
||||
BOOST_AUTO_TEST_CASE(read_only_on_producer) {
|
||||
std::vector<const char*> specific_args = {"-p", "eosio", "-e", "--read-only-threads", "2" };
|
||||
test_configs_common(specific_args, app_init_status::failed);
|
||||
}
|
||||
|
||||
// read_window_time must be greater than max_transaction_time + 10ms
|
||||
BOOST_AUTO_TEST_CASE(invalid_read_window_time) {
|
||||
std::vector<const char*> specific_args = { "--read-only-threads", "2", "--max-transaction-time", "10", "--read-only-write-window-time-us", "50000", "--read-only-read-window-time-us", "20000" }; // 20000 not greater than --max-transaction-time (10ms) + 10000us (minimum margin)
|
||||
test_configs_common(specific_args, app_init_status::failed);
|
||||
}
|
||||
|
||||
// if --read-only-threads is not configured, read-only trx related configs should
|
||||
// not be checked
|
||||
BOOST_AUTO_TEST_CASE(not_check_configs_if_no_read_only_threads) {
|
||||
std::vector<const char*> specific_args = { "--max-transaction-time", "10", "--read-only-write-window-time-us", "50000", "--read-only-read-window-time-us", "20000" }; // 20000 not greater than --max-transaction-time (10ms) + 10000us (minimum margin)
|
||||
test_configs_common(specific_args, app_init_status::succeeded);
|
||||
}
|
||||
|
||||
void test_trxs_common(std::vector<const char*>& specific_args, bool test_disable_tierup = false) {
|
||||
try {
|
||||
fc::scoped_exit<std::function<void()>> on_exit = []() {
|
||||
chain::wasm_interface_collection::test_disable_tierup = false;
|
||||
};
|
||||
chain::wasm_interface_collection::test_disable_tierup = test_disable_tierup;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
fc::temp_directory temp;
|
||||
appbase::scoped_app app;
|
||||
auto temp_dir_str = temp.path().string();
|
||||
producer_plugin::set_test_mode(true);
|
||||
|
||||
std::atomic<size_t> next_calls = 0;
|
||||
std::atomic<size_t> num_get_account_calls = 0;
|
||||
std::atomic<size_t> num_posts = 0;
|
||||
std::atomic<size_t> trace_with_except = 0;
|
||||
std::atomic<bool> trx_match = true;
|
||||
const size_t num_pushes = 4242;
|
||||
|
||||
{
|
||||
std::promise<std::tuple<producer_plugin*, chain_plugin*>> plugin_promise;
|
||||
std::future<std::tuple<producer_plugin*, chain_plugin*>> plugin_fut = plugin_promise.get_future();
|
||||
std::thread app_thread( [&]() {
|
||||
try {
|
||||
fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug);
|
||||
std::vector<const char*> argv = {"test", "--data-dir", temp_dir_str.c_str(), "--config-dir", temp_dir_str.c_str()};
|
||||
argv.insert(argv.end(), specific_args.begin(), specific_args.end());
|
||||
app->initialize<chain_plugin, producer_plugin>(argv.size(), (char**)&argv[0]);
|
||||
app->find_plugin<chain_plugin>()->chain();
|
||||
app->startup();
|
||||
plugin_promise.set_value({app->find_plugin<producer_plugin>(), app->find_plugin<chain_plugin>()});
|
||||
app->exec();
|
||||
return;
|
||||
} FC_LOG_AND_DROP()
|
||||
BOOST_CHECK(!"app threw exception see logged error");
|
||||
} );
|
||||
fc::scoped_exit<std::function<void()>> on_except = [&](){
|
||||
if (app_thread.joinable())
|
||||
app_thread.join();
|
||||
};
|
||||
|
||||
auto[prod_plug, chain_plug] = plugin_fut.get();
|
||||
|
||||
activate_protocol_features_set_bios_contract(app, chain_plug);
|
||||
|
||||
for( size_t i = 1; i <= num_pushes; ++i ) {
|
||||
auto ptrx = i % 3 == 0 ? make_unique_trx() : make_bios_ro_trx(chain_plug->chain());
|
||||
app->executor().post( priority::low, exec_queue::read_only, [&chain_plug=chain_plug, &num_get_account_calls]() {
|
||||
chain_plug->get_read_only_api(fc::seconds(90)).get_account(chain_apis::read_only::get_account_params{.account_name=config::system_account_name}, fc::time_point::now()+fc::seconds(90));
|
||||
++num_get_account_calls;
|
||||
});
|
||||
app->executor().post( priority::low, exec_queue::read_only, [ptrx, &next_calls, &num_posts, &trace_with_except, &trx_match, &app]() {
|
||||
++num_posts;
|
||||
bool return_failure_traces = true;
|
||||
app->get_method<plugin_interface::incoming::methods::transaction_async>()(ptrx,
|
||||
false, // api_trx
|
||||
transaction_metadata::trx_type::read_only, // trx_type
|
||||
return_failure_traces,
|
||||
[ptrx, &next_calls, &trace_with_except, &trx_match, return_failure_traces]
|
||||
(const next_function_variant<transaction_trace_ptr>& result) {
|
||||
if( !std::holds_alternative<fc::exception_ptr>( result ) && !std::get<chain::transaction_trace_ptr>( result )->except ) {
|
||||
if( std::get<chain::transaction_trace_ptr>( result )->id != ptrx->id() ) {
|
||||
elog( "trace not for trx ${id}: ${t}",
|
||||
("id", ptrx->id())("t", fc::json::to_pretty_string(*std::get<chain::transaction_trace_ptr>(result))) );
|
||||
trx_match = false;
|
||||
}
|
||||
} else if( !return_failure_traces && !std::holds_alternative<fc::exception_ptr>( result ) && std::get<chain::transaction_trace_ptr>( result )->except ) {
|
||||
elog( "trace with except ${e}",
|
||||
("e", fc::json::to_pretty_string( *std::get<chain::transaction_trace_ptr>( result ) )) );
|
||||
++trace_with_except;
|
||||
}
|
||||
++next_calls;
|
||||
});
|
||||
});
|
||||
app->executor().post( priority::low, exec_queue::read_only, [&chain_plug=chain_plug]() {
|
||||
chain_plug->get_read_only_api(fc::seconds(90)).get_consensus_parameters(chain_apis::read_only::get_consensus_parameters_params{}, fc::time_point::now()+fc::seconds(90));
|
||||
});
|
||||
}
|
||||
|
||||
// Wait long enough such that all transactions are executed
|
||||
auto start = fc::time_point::now();
|
||||
auto hard_deadline = start + fc::seconds(10); // To protect against waiting forever
|
||||
while ( (next_calls < num_pushes || num_get_account_calls < num_pushes) && fc::time_point::now() < hard_deadline ){
|
||||
std::this_thread::sleep_for( 100ms );
|
||||
}
|
||||
|
||||
app->quit();
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL( trace_with_except, 0 ); // should not have any traces with except in it
|
||||
BOOST_CHECK_EQUAL( num_pushes, num_posts );
|
||||
BOOST_CHECK_EQUAL( num_pushes, next_calls.load() );
|
||||
BOOST_CHECK_EQUAL( num_pushes, num_get_account_calls.load() );
|
||||
BOOST_CHECK( trx_match.load() ); // trace should match the transaction
|
||||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
// test read-only trxs on main thread (no --read-only-threads)
|
||||
BOOST_AUTO_TEST_CASE(no_read_only_threads) {
|
||||
std::vector<const char*> specific_args = { "-p", "eosio", "-e", "--abi-serializer-max-time-ms=999" };
|
||||
test_trxs_common(specific_args);
|
||||
}
|
||||
|
||||
// test read-only trxs on 1 threads (with --read-only-threads)
|
||||
BOOST_AUTO_TEST_CASE(with_1_read_only_threads) {
|
||||
std::vector<const char*> specific_args = { "-p", "eosio", "-e",
|
||||
"--read-only-threads=1",
|
||||
"--max-transaction-time=10",
|
||||
"--abi-serializer-max-time-ms=999",
|
||||
"--read-only-write-window-time-us=100000",
|
||||
"--read-only-read-window-time-us=40000" };
|
||||
test_trxs_common(specific_args);
|
||||
}
|
||||
|
||||
// test read-only trxs on 3 threads (with --read-only-threads)
|
||||
BOOST_AUTO_TEST_CASE(with_3_read_only_threads) {
|
||||
std::vector<const char*> specific_args = { "-p", "eosio", "-e",
|
||||
"--read-only-threads=3",
|
||||
"--max-transaction-time=10",
|
||||
"--abi-serializer-max-time-ms=999",
|
||||
"--read-only-write-window-time-us=100000",
|
||||
"--read-only-read-window-time-us=40000" };
|
||||
test_trxs_common(specific_args);
|
||||
}
|
||||
|
||||
// test read-only trxs on 3 threads (with --read-only-threads)
|
||||
BOOST_AUTO_TEST_CASE(with_3_read_only_threads_no_tierup) {
|
||||
std::vector<const char*> specific_args = { "-p", "eosio", "-e",
|
||||
"--read-only-threads=3",
|
||||
"--max-transaction-time=10",
|
||||
"--abi-serializer-max-time-ms=999",
|
||||
"--read-only-write-window-time-us=100000",
|
||||
"--read-only-read-window-time-us=40000" };
|
||||
test_trxs_common(specific_args, true);
|
||||
}
|
||||
|
||||
// test read-only trxs on 8 separate threads (with --read-only-threads)
|
||||
BOOST_AUTO_TEST_CASE(with_8_read_only_threads) {
|
||||
std::vector<const char*> specific_args = { "-p", "eosio", "-e",
|
||||
"--read-only-threads=8",
|
||||
"--eos-vm-oc-enable=none",
|
||||
"--max-transaction-time=10",
|
||||
"--abi-serializer-max-time-ms=999",
|
||||
"--read-only-write-window-time-us=10000",
|
||||
"--read-only-read-window-time-us=400000" };
|
||||
test_trxs_common(specific_args);
|
||||
}
|
||||
|
||||
// test read-only trxs on 8 separate threads (with --read-only-threads)
|
||||
BOOST_AUTO_TEST_CASE(with_8_read_only_threads_no_tierup) {
|
||||
std::vector<const char*> specific_args = { "-p", "eosio", "-e",
|
||||
"--read-only-threads=8",
|
||||
"--eos-vm-oc-enable=none",
|
||||
"--max-transaction-time=10",
|
||||
"--abi-serializer-max-time-ms=999",
|
||||
"--read-only-write-window-time-us=10000",
|
||||
"--read-only-read-window-time-us=400000" };
|
||||
test_trxs_common(specific_args, true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
@@ -61,15 +61,19 @@ BOOST_AUTO_TEST_CASE(snapshot_scheduler_test) {
|
||||
std::future<std::tuple<producer_plugin*, chain_plugin*>> plugin_fut = plugin_promise.get_future();
|
||||
|
||||
std::thread app_thread([&]() {
|
||||
fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug);
|
||||
std::vector<const char*> argv =
|
||||
{"test", "--data-dir", temp.c_str(), "--config-dir", temp.c_str(),
|
||||
"-p", "eosio", "-e"};
|
||||
app->initialize<chain_plugin, producer_plugin>(argv.size(), (char**) &argv[0]);
|
||||
app->startup();
|
||||
plugin_promise.set_value(
|
||||
{app->find_plugin<producer_plugin>(), app->find_plugin<chain_plugin>()});
|
||||
app->exec();
|
||||
try {
|
||||
fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug);
|
||||
std::vector<const char*> argv =
|
||||
{"test", "--data-dir", temp.c_str(), "--config-dir", temp.c_str(),
|
||||
"-p", "eosio", "-e"};
|
||||
app->initialize<chain_plugin, producer_plugin>(argv.size(), (char**) &argv[0]);
|
||||
app->startup();
|
||||
plugin_promise.set_value(
|
||||
{app->find_plugin<producer_plugin>(), app->find_plugin<chain_plugin>()});
|
||||
app->exec();
|
||||
return;
|
||||
} FC_LOG_AND_DROP()
|
||||
BOOST_CHECK(!"app threw exception see logged error");
|
||||
});
|
||||
|
||||
auto [prod_plug, chain_plug] = plugin_fut.get();
|
||||
|
||||
+127
-12
@@ -41,7 +41,15 @@ FC_REFLECT(act_sig, (sig) )
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(abi_tests)
|
||||
|
||||
#ifdef NDEBUG
|
||||
fc::microseconds max_serialization_time = fc::seconds(1); // some test machines are very slow
|
||||
#else
|
||||
fc::microseconds max_serialization_time = fc::microseconds::maximum(); // don't check in debug builds
|
||||
#endif
|
||||
|
||||
static fc::time_point get_deadline() {
|
||||
return fc::time_point::now().safe_add(max_serialization_time);
|
||||
}
|
||||
|
||||
// verify that round trip conversion, via bytes, reproduces the exact same data
|
||||
fc::variant verify_byte_round_trip_conversion( const abi_serializer& abis, const type_name& type, const fc::variant& var )
|
||||
@@ -53,8 +61,8 @@ fc::variant verify_byte_round_trip_conversion( const abi_serializer& abis, const
|
||||
auto var2 = abis.binary_to_variant(type, bytes, abi_serializer::create_yield_function( max_serialization_time ));
|
||||
auto var3 = abis.binary_to_variant(type, b, max_serialization_time);
|
||||
|
||||
std::string r2 = fc::json::to_string(var2, fc::time_point::now() + max_serialization_time);
|
||||
std::string r3 = fc::json::to_string(var3, fc::time_point::now() + max_serialization_time);
|
||||
std::string r2 = fc::json::to_string(var2, get_deadline());
|
||||
std::string r3 = fc::json::to_string(var3, get_deadline());
|
||||
BOOST_TEST( r2 == r3 );
|
||||
|
||||
auto bytes2 = abis.variant_to_binary(type, var2, abi_serializer::create_yield_function( max_serialization_time ));
|
||||
@@ -74,9 +82,9 @@ void verify_round_trip_conversion( const abi_serializer& abis, const type_name&
|
||||
auto b = abis.variant_to_binary(type, var, max_serialization_time);
|
||||
BOOST_REQUIRE_EQUAL(fc::to_hex(b), hex);
|
||||
auto var2 = abis.binary_to_variant(type, bytes, abi_serializer::create_yield_function( max_serialization_time ));
|
||||
BOOST_REQUIRE_EQUAL(fc::json::to_string(var2, fc::time_point::now() + max_serialization_time), expected_json);
|
||||
BOOST_REQUIRE_EQUAL(fc::json::to_string(var2, get_deadline()), expected_json);
|
||||
auto var3 = abis.binary_to_variant(type, b, max_serialization_time );
|
||||
BOOST_REQUIRE_EQUAL(fc::json::to_string(var3, fc::time_point::now() + max_serialization_time), expected_json);
|
||||
BOOST_REQUIRE_EQUAL(fc::json::to_string(var3, get_deadline()), expected_json);
|
||||
auto bytes2 = abis.variant_to_binary(type, var2, abi_serializer::create_yield_function( max_serialization_time ));
|
||||
BOOST_REQUIRE_EQUAL(fc::to_hex(bytes2), hex);
|
||||
auto b2 = abis.variant_to_binary(type, var3, max_serialization_time);
|
||||
@@ -115,8 +123,8 @@ fc::variant verify_type_round_trip_conversion( const abi_serializer& abis, const
|
||||
fc::variant var3;
|
||||
abi_serializer::to_variant(obj2, var3, get_resolver(), max_serialization_time);
|
||||
|
||||
std::string r2 = fc::json::to_string(var2, fc::time_point::now() + max_serialization_time);
|
||||
std::string r3 = fc::json::to_string(var3, fc::time_point::now() + max_serialization_time);
|
||||
std::string r2 = fc::json::to_string(var2, get_deadline());
|
||||
std::string r3 = fc::json::to_string(var3, get_deadline());
|
||||
BOOST_TEST( r2 == r3 );
|
||||
|
||||
auto bytes2 = abis.variant_to_binary(type, var2, abi_serializer::create_yield_function( max_serialization_time ));
|
||||
@@ -1972,6 +1980,113 @@ BOOST_AUTO_TEST_CASE(abi_type_loop)
|
||||
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
BOOST_AUTO_TEST_CASE(abi_std_optional)
|
||||
{ try {
|
||||
const char* repeat_abi = R"=====(
|
||||
{
|
||||
"version": "eosio::abi/1.2",
|
||||
"types": [],
|
||||
"structs": [
|
||||
{
|
||||
"name": "fees",
|
||||
"base": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "gas_price",
|
||||
"type": "uint64?"
|
||||
},
|
||||
{
|
||||
"name": "miner_cut",
|
||||
"type": "uint32?"
|
||||
},
|
||||
{
|
||||
"name": "bridge_fee",
|
||||
"type": "uint32?"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"name": "fees",
|
||||
"type": "fees",
|
||||
"ricardian_contract": ""
|
||||
}
|
||||
],
|
||||
"tables": [],
|
||||
"ricardian_clauses": [],
|
||||
"variants": [],
|
||||
"action_results": []
|
||||
}
|
||||
)=====";
|
||||
|
||||
abi_serializer abis(fc::json::from_string(repeat_abi).as<abi_def>(), abi_serializer::create_yield_function( max_serialization_time ));
|
||||
{
|
||||
// check conversion when all optional members are provided
|
||||
std::string test_data = R"=====(
|
||||
{
|
||||
"gas_price" : "42",
|
||||
"miner_cut" : "2",
|
||||
"bridge_fee" : "2"
|
||||
}
|
||||
)=====";
|
||||
|
||||
auto var = fc::json::from_string(test_data);
|
||||
verify_byte_round_trip_conversion(abis, "fees", var);
|
||||
}
|
||||
|
||||
{
|
||||
// check conversion when the first optional member is missing
|
||||
std::string test_data = R"=====(
|
||||
{
|
||||
"miner_cut" : "2",
|
||||
"bridge_fee" : "2"
|
||||
}
|
||||
)=====";
|
||||
|
||||
auto var = fc::json::from_string(test_data);
|
||||
verify_byte_round_trip_conversion(abis, "fees", var);
|
||||
}
|
||||
|
||||
{
|
||||
// check conversion when the second optional member is missing
|
||||
std::string test_data = R"=====(
|
||||
{
|
||||
"gas_price" : "42",
|
||||
"bridge_fee" : "2"
|
||||
}
|
||||
)=====";
|
||||
|
||||
auto var = fc::json::from_string(test_data);
|
||||
verify_byte_round_trip_conversion(abis, "fees", var);
|
||||
}
|
||||
|
||||
{
|
||||
// check conversion when the last optional member is missing
|
||||
std::string test_data = R"=====(
|
||||
{
|
||||
"gas_price" : "42",
|
||||
"miner_cut" : "2",
|
||||
}
|
||||
)=====";
|
||||
|
||||
auto var = fc::json::from_string(test_data);
|
||||
verify_byte_round_trip_conversion(abis, "fees", var);
|
||||
}
|
||||
|
||||
{
|
||||
// check conversion when all optional members are missing
|
||||
std::string test_data = R"=====(
|
||||
{
|
||||
}
|
||||
)=====";
|
||||
|
||||
auto var = fc::json::from_string(test_data);
|
||||
verify_byte_round_trip_conversion(abis, "fees", var);
|
||||
}
|
||||
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
BOOST_AUTO_TEST_CASE(abi_type_redefine)
|
||||
{ try {
|
||||
// inifinite loop in types
|
||||
@@ -3127,7 +3242,7 @@ BOOST_AUTO_TEST_CASE(abi_to_variant__add_action__good_return_value)
|
||||
mutable_variant_object mvo;
|
||||
eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_yield_function(max_serialization_time), fc::microseconds{});
|
||||
eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx);
|
||||
std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time);
|
||||
std::string res = fc::json::to_string(mvo, get_deadline());
|
||||
|
||||
BOOST_CHECK_EQUAL(res, expected_json);
|
||||
}
|
||||
@@ -3135,7 +3250,7 @@ BOOST_AUTO_TEST_CASE(abi_to_variant__add_action__good_return_value)
|
||||
mutable_variant_object mvo;
|
||||
eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_depth_yield_function(), max_serialization_time);
|
||||
eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx);
|
||||
std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time);
|
||||
std::string res = fc::json::to_string(mvo, get_deadline());
|
||||
|
||||
BOOST_CHECK_EQUAL(res, expected_json);
|
||||
}
|
||||
@@ -3162,7 +3277,7 @@ BOOST_AUTO_TEST_CASE(abi_to_variant__add_action__bad_return_value)
|
||||
mutable_variant_object mvo;
|
||||
eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_yield_function(max_serialization_time), fc::microseconds{});
|
||||
eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx);
|
||||
std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time);
|
||||
std::string res = fc::json::to_string(mvo, get_deadline());
|
||||
|
||||
BOOST_CHECK_EQUAL(res, expected_json);
|
||||
}
|
||||
@@ -3170,7 +3285,7 @@ BOOST_AUTO_TEST_CASE(abi_to_variant__add_action__bad_return_value)
|
||||
mutable_variant_object mvo;
|
||||
eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_depth_yield_function(), max_serialization_time);
|
||||
eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx);
|
||||
std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time);
|
||||
std::string res = fc::json::to_string(mvo, get_deadline());
|
||||
|
||||
BOOST_CHECK_EQUAL(res, expected_json);
|
||||
}
|
||||
@@ -3207,7 +3322,7 @@ BOOST_AUTO_TEST_CASE(abi_to_variant__add_action__no_return_value)
|
||||
mutable_variant_object mvo;
|
||||
eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_yield_function(max_serialization_time), fc::microseconds{});
|
||||
eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx);
|
||||
std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time);
|
||||
std::string res = fc::json::to_string(mvo, get_deadline());
|
||||
|
||||
BOOST_CHECK_EQUAL(res, expected_json);
|
||||
}
|
||||
@@ -3215,7 +3330,7 @@ BOOST_AUTO_TEST_CASE(abi_to_variant__add_action__no_return_value)
|
||||
mutable_variant_object mvo;
|
||||
eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_depth_yield_function(), max_serialization_time);
|
||||
eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx);
|
||||
std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time);
|
||||
std::string res = fc::json::to_string(mvo, get_deadline());
|
||||
|
||||
BOOST_CHECK_EQUAL(res, expected_json);
|
||||
}
|
||||
|
||||
@@ -144,4 +144,45 @@ BOOST_AUTO_TEST_CASE( decompressed_size_under_limit ) try {
|
||||
|
||||
} FC_LOG_AND_RETHROW()
|
||||
|
||||
// verify accepted_block signals validated blocks
|
||||
BOOST_AUTO_TEST_CASE( signal_validated_blocks ) try {
|
||||
tester chain;
|
||||
tester validator;
|
||||
|
||||
block_state_ptr accepted_bsp;
|
||||
auto c = chain.control->accepted_block.connect([&](const block_state_ptr& b) {
|
||||
BOOST_CHECK(b);
|
||||
BOOST_CHECK(chain.control->fetch_block_state_by_id(b->id) == b);
|
||||
BOOST_CHECK(chain.control->fetch_block_state_by_number(b->block_num) == b); // verify it can be found (has to be validated)
|
||||
BOOST_CHECK(chain.control->fetch_block_by_id(b->id) == b->block);
|
||||
BOOST_CHECK(chain.control->fetch_block_by_number(b->block_num) == b->block);
|
||||
BOOST_REQUIRE(chain.control->fetch_block_header_by_number(b->block_num));
|
||||
BOOST_CHECK(chain.control->fetch_block_header_by_number(b->block_num)->calculate_id() == b->id);
|
||||
BOOST_REQUIRE(chain.control->fetch_block_header_by_id(b->id));
|
||||
BOOST_CHECK(chain.control->fetch_block_header_by_id(b->id)->calculate_id() == b->id);
|
||||
accepted_bsp = b;
|
||||
});
|
||||
block_state_ptr validated_bsp;
|
||||
auto c2 = validator.control->accepted_block.connect([&](const block_state_ptr& b) {
|
||||
BOOST_CHECK(b);
|
||||
BOOST_CHECK(validator.control->fetch_block_state_by_id(b->id) == b);
|
||||
BOOST_CHECK(validator.control->fetch_block_state_by_number(b->block_num) == b); // verify it can be found (has to be validated)
|
||||
BOOST_CHECK(validator.control->fetch_block_by_id(b->id) == b->block);
|
||||
BOOST_CHECK(validator.control->fetch_block_by_number(b->block_num) == b->block);
|
||||
BOOST_REQUIRE(validator.control->fetch_block_header_by_number(b->block_num));
|
||||
BOOST_CHECK(validator.control->fetch_block_header_by_number(b->block_num)->calculate_id() == b->id);
|
||||
BOOST_REQUIRE(validator.control->fetch_block_header_by_id(b->id));
|
||||
BOOST_CHECK(validator.control->fetch_block_header_by_id(b->id)->calculate_id() == b->id);
|
||||
validated_bsp = b;
|
||||
});
|
||||
|
||||
chain.produce_blocks(1);
|
||||
validator.push_block(accepted_bsp->block);
|
||||
|
||||
auto trace_ptr = chain.create_account("hello"_n);
|
||||
chain.produce_block();
|
||||
validator.push_block(accepted_bsp->block);
|
||||
|
||||
} FC_LOG_AND_RETHROW()
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -412,6 +412,14 @@ BOOST_AUTO_TEST_CASE( replace_deferred_test ) try {
|
||||
cfg.disable_all_subjective_mitigations = true;
|
||||
c.init( cfg );
|
||||
|
||||
transaction_trace_ptr trace;
|
||||
auto h = c.control->applied_transaction.connect( [&](std::tuple<const transaction_trace_ptr&, const packed_transaction_ptr&> x) {
|
||||
auto& t = std::get<0>(x);
|
||||
if( t && !eosio::chain::is_onblock(*t)) {
|
||||
trace = t;
|
||||
}
|
||||
} );
|
||||
|
||||
BOOST_CHECK_EQUAL( c.control->get_resource_limits_manager().get_account_ram_usage( "alice"_n ), alice_ram_usage0 );
|
||||
|
||||
c.push_action( "test"_n, "defercall"_n, "alice"_n, fc::mutable_variant_object()
|
||||
@@ -448,6 +456,8 @@ BOOST_AUTO_TEST_CASE( replace_deferred_test ) try {
|
||||
|
||||
dtrxs = c.get_scheduled_transactions();
|
||||
BOOST_CHECK_EQUAL( dtrxs.size(), 0 );
|
||||
// must be equal before builtin_protocol_feature_t::replace_deferred to support replay of blocks before activation
|
||||
BOOST_CHECK( first_dtrx_id.str() == trace->id.str() );
|
||||
|
||||
c.produce_block();
|
||||
|
||||
@@ -507,6 +517,13 @@ BOOST_AUTO_TEST_CASE( replace_deferred_test ) try {
|
||||
BOOST_CHECK_EQUAL( dtrxs.size(), 1 );
|
||||
BOOST_CHECK_EQUAL( first_dtrx_id2, dtrxs[0] );
|
||||
|
||||
c.produce_block();
|
||||
|
||||
dtrxs = c.get_scheduled_transactions();
|
||||
BOOST_CHECK_EQUAL( dtrxs.size(), 0 );
|
||||
// Not equal after builtin_protocol_feature_t::replace_deferred activated
|
||||
BOOST_CHECK( first_dtrx_id2.str() != trace->id.str() );
|
||||
|
||||
} FC_LOG_AND_RETHROW()
|
||||
|
||||
BOOST_AUTO_TEST_CASE( no_duplicate_deferred_id_test ) try {
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
#pragma once
|
||||
|
||||
#include <eosio/testing/tester.hpp>
|
||||
#include <eosio/chain_plugin/chain_plugin.hpp>
|
||||
#include <eosio/chain/application.hpp>
|
||||
#include <eosio/chain/types.hpp>
|
||||
#include <eosio/chain/trace.hpp>
|
||||
#include <eosio/chain/config.hpp>
|
||||
#include <eosio/chain/transaction.hpp>
|
||||
#include <eosio/chain/controller.hpp>
|
||||
|
||||
#include <fc/exception/exception.hpp>
|
||||
|
||||
#include <contracts.hpp>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
namespace eosio::test_utils {
|
||||
|
||||
using namespace eosio::chain;
|
||||
using namespace eosio::chain::literals;
|
||||
|
||||
struct testit {
|
||||
uint64_t id;
|
||||
explicit testit(uint64_t id = 0)
|
||||
:id(id){}
|
||||
static account_name get_account() {
|
||||
return chain::config::system_account_name;
|
||||
}
|
||||
static action_name get_name() {
|
||||
return "testit"_n;
|
||||
}
|
||||
};
|
||||
|
||||
// Corresponds to the reqactivated action of the bios contract.
|
||||
// See libraries/testing/contracts/eosio.bios/eosio.bios.hpp
|
||||
struct reqactivated {
|
||||
chain::digest_type feature_digest;
|
||||
|
||||
explicit reqactivated(const chain::digest_type& fd)
|
||||
:feature_digest(fd){};
|
||||
|
||||
static account_name get_account() {
|
||||
return chain::config::system_account_name;
|
||||
}
|
||||
static action_name get_name() {
|
||||
return "reqactivated"_n;
|
||||
}
|
||||
};
|
||||
|
||||
// Create a read-only trx that works with bios reqactivated action
|
||||
auto make_bios_ro_trx(eosio::chain::controller& control) {
|
||||
const auto& pfm = control.get_protocol_feature_manager();
|
||||
static auto feature_digest = pfm.get_builtin_digest(builtin_protocol_feature_t::replace_deferred);
|
||||
|
||||
signed_transaction trx;
|
||||
trx.expiration = fc::time_point_sec{fc::time_point::now() + fc::seconds(30)};
|
||||
vector<permission_level> no_auth{};
|
||||
trx.actions.emplace_back( no_auth, reqactivated{*feature_digest} );
|
||||
return std::make_shared<packed_transaction>( std::move(trx) );
|
||||
}
|
||||
|
||||
// Push an input transaction to controller and return trx trace
|
||||
// If account is eosio then signs with the default private key
|
||||
auto push_input_trx(appbase::scoped_app& app, eosio::chain::controller& control, account_name account, signed_transaction& trx) {
|
||||
trx.expiration = fc::time_point_sec{fc::time_point::now() + fc::seconds(30)};
|
||||
trx.set_reference_block( control.head_block_id() );
|
||||
if (account == config::system_account_name) {
|
||||
auto default_priv_key = private_key_type::regenerate<fc::ecc::private_key_shim>(fc::sha256::hash(std::string("nathan")));
|
||||
trx.sign(default_priv_key, control.get_chain_id());
|
||||
} else {
|
||||
trx.sign(testing::tester::get_private_key(account, "active"), control.get_chain_id());
|
||||
}
|
||||
auto ptrx = std::make_shared<packed_transaction>( trx, packed_transaction::compression_type::zlib );
|
||||
|
||||
auto trx_promise = std::make_shared<std::promise<transaction_trace_ptr>>();
|
||||
std::future<transaction_trace_ptr> trx_future = trx_promise->get_future();
|
||||
|
||||
app->executor().post( priority::low, exec_queue::read_write, [&ptrx, &app, trx_promise]() {
|
||||
app->get_method<plugin_interface::incoming::methods::transaction_async>()(ptrx,
|
||||
false, // api_trx
|
||||
transaction_metadata::trx_type::input, // trx_type
|
||||
true, // return_failure_traces
|
||||
[trx_promise](const next_function_variant<transaction_trace_ptr>& result) {
|
||||
if( std::holds_alternative<fc::exception_ptr>( result ) ) {
|
||||
try {
|
||||
std::get<fc::exception_ptr>(result)->dynamic_rethrow_exception();
|
||||
} catch(...) {
|
||||
trx_promise->set_exception(std::current_exception());
|
||||
}
|
||||
} else if ( std::get<chain::transaction_trace_ptr>( result )->except ) {
|
||||
try {
|
||||
std::get<chain::transaction_trace_ptr>(result)->except->dynamic_rethrow_exception();
|
||||
} catch(...) {
|
||||
trx_promise->set_exception(std::current_exception());
|
||||
}
|
||||
} else {
|
||||
trx_promise->set_value(std::get<chain::transaction_trace_ptr>(result));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (trx_future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout)
|
||||
throw std::runtime_error("failed to execute trx: " + ptrx->get_transaction().actions.at(0).name.to_string() + " to account: " + account.to_string());
|
||||
|
||||
return trx_future.get();
|
||||
}
|
||||
|
||||
// Push setcode trx to controller and return trx trace
|
||||
auto set_code(appbase::scoped_app& app, eosio::chain::controller& control, account_name account, const vector<uint8_t>& wasm) {
|
||||
signed_transaction trx;
|
||||
trx.actions.emplace_back(std::vector<permission_level>{{account, config::active_name}},
|
||||
chain::setcode{
|
||||
.account = account,
|
||||
.vmtype = 0,
|
||||
.vmversion = 0,
|
||||
.code = bytes(wasm.begin(), wasm.end())
|
||||
});
|
||||
return push_input_trx(app, control, account, trx);
|
||||
}
|
||||
|
||||
void activate_protocol_features_set_bios_contract(appbase::scoped_app& app, chain_plugin* chain_plug) {
|
||||
using namespace appbase;
|
||||
|
||||
auto feature_set = std::make_shared<std::atomic<bool>>(false);
|
||||
// has to execute when pending block is not null
|
||||
for (int tries = 0; tries < 100; ++tries) {
|
||||
app->executor().post( priority::high, exec_queue::read_write, [&chain_plug=chain_plug, feature_set](){
|
||||
try {
|
||||
if (!chain_plug->chain().is_building_block() || *feature_set)
|
||||
return;
|
||||
const auto& pfm = chain_plug->chain().get_protocol_feature_manager();
|
||||
auto preactivate_feature_digest = pfm.get_builtin_digest(builtin_protocol_feature_t::preactivate_feature);
|
||||
BOOST_CHECK( preactivate_feature_digest );
|
||||
chain_plug->chain().preactivate_feature( *preactivate_feature_digest, false );
|
||||
std::vector<builtin_protocol_feature_t> pfs{
|
||||
builtin_protocol_feature_t::only_link_to_existing_permission,
|
||||
builtin_protocol_feature_t::replace_deferred,
|
||||
builtin_protocol_feature_t::no_duplicate_deferred_id,
|
||||
builtin_protocol_feature_t::fix_linkauth_restriction,
|
||||
builtin_protocol_feature_t::disallow_empty_producer_schedule,
|
||||
builtin_protocol_feature_t::restrict_action_to_self,
|
||||
builtin_protocol_feature_t::only_bill_first_authorizer,
|
||||
builtin_protocol_feature_t::forward_setcode,
|
||||
builtin_protocol_feature_t::get_sender,
|
||||
builtin_protocol_feature_t::ram_restrictions,
|
||||
builtin_protocol_feature_t::webauthn_key,
|
||||
builtin_protocol_feature_t::wtmsig_block_signatures };
|
||||
for (const auto t : pfs) {
|
||||
auto feature_digest = pfm.get_builtin_digest(t);
|
||||
BOOST_CHECK( feature_digest );
|
||||
chain_plug->chain().preactivate_feature( *feature_digest, false );
|
||||
}
|
||||
*feature_set = true;
|
||||
return;
|
||||
} FC_LOG_AND_DROP()
|
||||
BOOST_CHECK(!"exception setting protocol features");
|
||||
});
|
||||
if (*feature_set)
|
||||
break;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
|
||||
// Wait for next block
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds(config::block_interval_ms) );
|
||||
|
||||
auto r = set_code(app, chain_plug->chain(), config::system_account_name, testing::contracts::eosio_bios_wasm());
|
||||
BOOST_CHECK(r->receipt && r->receipt->status == transaction_receipt_header::executed);
|
||||
}
|
||||
|
||||
|
||||
} // namespace eosio::test_utils
|
||||
|
||||
FC_REFLECT( eosio::test_utils::testit, (id) )
|
||||
FC_REFLECT( eosio::test_utils::reqactivated, (feature_digest) )
|
||||
Reference in New Issue
Block a user