Merge branch 'develop' into fair-queuing-3-wip
# Conflicts: # nano/lib/stats_enums.hpp # nano/node/CMakeLists.txt
This commit is contained in:
commit
852347e723
205 changed files with 6445 additions and 8159 deletions
|
|
@ -3,6 +3,11 @@ on:
|
|||
schedule:
|
||||
- cron: "0 0 * * 3,6"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
is_release_build:
|
||||
description: 'Is this a release build?'
|
||||
required: false
|
||||
default: 'false'
|
||||
|
||||
env:
|
||||
artifact: 1
|
||||
|
|
@ -11,28 +16,27 @@ jobs:
|
|||
prepare_build:
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
CI_TAG: ${{ steps.tag_set.outputs.CI_TAG }}
|
||||
TAG_CREATED: ${{ steps.tag_set.outputs.tag_created }}
|
||||
ci_tag: ${{ steps.set_vars.outputs.ci_tag }}
|
||||
tag_created: ${{ steps.set_vars.outputs.tag_created }}
|
||||
steps:
|
||||
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 #v3.1.0
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
- name: Set the tag and version
|
||||
id: tag_set
|
||||
run: |
|
||||
output_var_file="variable_list.txt"
|
||||
ci/actions/generate_next_git_tag.sh -c -o "${output_var_file}"
|
||||
CI_TAG=$(grep 'build_tag' ${output_var_file} | cut -d= -f2)
|
||||
echo "CI_TAG=${CI_TAG}" >> $GITHUB_OUTPUT
|
||||
TAG_CREATED=$(grep 'tag_created' ${output_var_file} | cut -d= -f2)
|
||||
echo "TAG_CREATED=${TAG_CREATED}" >> $GITHUB_OUTPUT
|
||||
ci/actions/generate_next_git_tag.sh -c
|
||||
env:
|
||||
GITHUB_ACTOR: ${{ github.actor }}
|
||||
IS_RELEASE_BUILD: ${{ github.event.inputs.is_release_build || 'false' }}
|
||||
- name: Set output
|
||||
id: set_vars
|
||||
run: |
|
||||
echo "ci_tag=$CI_TAG" >> $GITHUB_OUTPUT
|
||||
echo "tag_created=$TAG_CREATED" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
osx_job:
|
||||
needs: prepare_build
|
||||
if: ${{ needs.prepare_build.outputs.TAG_CREATED == 'true' }}
|
||||
if: ${{ needs.prepare_build.outputs.tag_created == 'true' }}
|
||||
runs-on: macOS-14
|
||||
timeout-minutes: 90
|
||||
strategy:
|
||||
|
|
@ -43,7 +47,7 @@ jobs:
|
|||
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 #v3.1.0
|
||||
with:
|
||||
submodules: "recursive"
|
||||
ref: ${{ needs.prepare_build.outputs.CI_TAG }}
|
||||
ref: ${{ needs.prepare_build.outputs.ci_tag }}
|
||||
repository: ${{ github.repository }}
|
||||
- name: Prepare
|
||||
run: ci/prepare/macos/prepare.sh
|
||||
|
|
@ -51,12 +55,12 @@ jobs:
|
|||
run: ci/build-deploy.sh "/tmp/qt/lib/cmake/Qt5";
|
||||
env:
|
||||
NETWORK: ${{ matrix.network }}
|
||||
CI_TAG: ${{ needs.prepare_build.outputs.CI_TAG }}
|
||||
CI_TAG: ${{ needs.prepare_build.outputs.ci_tag }}
|
||||
- name: Deploy Artifact
|
||||
run: ci/actions/deploy.sh
|
||||
env:
|
||||
NETWORK: ${{ matrix.network }}
|
||||
TAG: ${{ needs.prepare_build.outputs.CI_TAG }}
|
||||
TAG: ${{ needs.prepare_build.outputs.ci_tag }}
|
||||
S3_BUCKET_NAME: ${{ vars.S3_BUCKET_NAME }}
|
||||
S3_BUILD_DIRECTORY: ${{ vars.S3_BUILD_DIRECTORY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
|
|
@ -66,7 +70,7 @@ jobs:
|
|||
|
||||
linux_job:
|
||||
needs: prepare_build
|
||||
if: ${{ needs.prepare_build.outputs.TAG_CREATED == 'true' }}
|
||||
if: ${{ needs.prepare_build.outputs.tag_created == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
strategy:
|
||||
|
|
@ -76,7 +80,7 @@ jobs:
|
|||
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 #v3.1.0
|
||||
with:
|
||||
submodules: "recursive"
|
||||
ref: ${{ needs.prepare_build.outputs.CI_TAG }}
|
||||
ref: ${{ needs.prepare_build.outputs.ci_tag }}
|
||||
repository: ${{ github.repository }}
|
||||
- name: Prepare
|
||||
run: sudo -E ci/prepare/linux/prepare.sh
|
||||
|
|
@ -84,13 +88,13 @@ jobs:
|
|||
run: ci/build-deploy.sh "/usr/lib/x86_64-linux-gnu/cmake/Qt5"
|
||||
env:
|
||||
NETWORK: ${{ matrix.network }}
|
||||
CI_TAG: ${{ needs.prepare_build.outputs.CI_TAG }}
|
||||
CI_TAG: ${{ needs.prepare_build.outputs.ci_tag }}
|
||||
|
||||
- name: Deploy Artifact
|
||||
run: ci/actions/deploy.sh
|
||||
env:
|
||||
NETWORK: ${{ matrix.network }}
|
||||
TAG: ${{ needs.prepare_build.outputs.CI_TAG }}
|
||||
TAG: ${{ needs.prepare_build.outputs.ci_tag }}
|
||||
S3_BUCKET_NAME: ${{ vars.S3_BUCKET_NAME }}
|
||||
S3_BUILD_DIRECTORY: ${{ vars.S3_BUILD_DIRECTORY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
|
|
@ -100,7 +104,7 @@ jobs:
|
|||
|
||||
linux_rpm_job:
|
||||
needs: prepare_build
|
||||
if: ${{ needs.prepare_build.outputs.TAG_CREATED == 'true' }}
|
||||
if: ${{ needs.prepare_build.outputs.tag_created == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
strategy:
|
||||
|
|
@ -125,7 +129,7 @@ jobs:
|
|||
docker run -v ${GITHUB_WORKSPACE}:/workspace -v ${GITHUB_WORKSPACE}/artifacts:/root/rpmbuild \
|
||||
local/nano-env:rhel /bin/bash -c " \
|
||||
NETWORK=${{ matrix.network }} \
|
||||
TAG=${{ needs.prepare_build.outputs.CI_TAG }} \
|
||||
TAG=${{ needs.prepare_build.outputs.ci_tag }} \
|
||||
REPO_TO_BUILD=${{ github.repository }} \
|
||||
RPM_RELEASE=1 \
|
||||
ci/build-rhel.sh"
|
||||
|
|
@ -135,7 +139,7 @@ jobs:
|
|||
env:
|
||||
LINUX_RPM: 1
|
||||
NETWORK: ${{ matrix.network }}
|
||||
# TAG: ${{ needs.prepare_build.outputs.CI_TAG }} # (not used in the deploy script if LINUX_RPM==1 )
|
||||
# TAG: ${{ needs.prepare_build.outputs.ci_tag }} # (not used in the deploy script if LINUX_RPM==1 )
|
||||
S3_BUCKET_NAME: ${{ vars.S3_BUCKET_NAME }}
|
||||
S3_BUILD_DIRECTORY: ${{ vars.S3_BUILD_DIRECTORY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
|
|
@ -145,39 +149,37 @@ jobs:
|
|||
|
||||
linux_docker_job:
|
||||
needs: prepare_build
|
||||
if: ${{ needs.prepare_build.outputs.TAG_CREATED == 'true' }}
|
||||
if: ${{ needs.prepare_build.outputs.tag_created == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
strategy:
|
||||
matrix:
|
||||
network: ["TEST", "BETA", "LIVE"]
|
||||
env:
|
||||
CI_TAG: ${{ needs.prepare_build.outputs.ci_tag }}
|
||||
DOCKER_REGISTRY: ${{ vars.DOCKER_REGISTRY }}
|
||||
DOCKER_USER: ${{ vars.DOCKER_USER }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
IS_RELEASE_BUILD: ${{ github.event.inputs.is_release_build || 'false' }}
|
||||
steps:
|
||||
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 #v3.1.0
|
||||
with:
|
||||
submodules: "recursive"
|
||||
ref: ${{ needs.prepare_build.outputs.CI_TAG }}
|
||||
ref: ${{ needs.prepare_build.outputs.ci_tag }}
|
||||
repository: ${{ github.repository }}
|
||||
- name: Build Docker
|
||||
run: ci/actions/linux/docker-build.sh
|
||||
env:
|
||||
NETWORK: ${{ matrix.network }}
|
||||
CI_TAG: ${{ needs.prepare_build.outputs.CI_TAG }}
|
||||
DOCKER_REGISTRY: ${{ vars.DOCKER_REGISTRY }}
|
||||
- name: Check if secrets.DOCKER_PASSWORD exists
|
||||
run: echo "DOCKER_PASSWORD_EXISTS=${{ secrets.DOCKER_PASSWORD != '' }}" >> $GITHUB_ENV
|
||||
- name: Deploy Docker Hub
|
||||
if: env.DOCKER_PASSWORD_EXISTS == 'true'
|
||||
if: env.DOCKER_PASSWORD != ''
|
||||
run: ci/actions/linux/docker-deploy.sh
|
||||
env:
|
||||
CI_TAG: ${{ needs.prepare_build.outputs.CI_TAG }}
|
||||
NETWORK: ${{ matrix.network }}
|
||||
DOCKER_REGISTRY: ${{ vars.DOCKER_REGISTRY }}
|
||||
DOCKER_USER: ${{ vars.DOCKER_USER }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Deploy Docker (ghcr.io)
|
||||
run: ci/actions/linux/ghcr-deploy.sh
|
||||
env:
|
||||
CI_TAG: ${{ needs.prepare_build.outputs.CI_TAG }}
|
||||
NETWORK: ${{ matrix.network }}
|
||||
DOCKER_REGISTRY: ghcr.io
|
||||
DOCKER_USER: ${{ github.repository_owner }}
|
||||
|
|
@ -186,7 +188,7 @@ jobs:
|
|||
|
||||
windows_job:
|
||||
needs: prepare_build
|
||||
if: ${{ needs.prepare_build.outputs.TAG_CREATED == 'true' }}
|
||||
if: ${{ needs.prepare_build.outputs.tag_created == 'true' }}
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 90
|
||||
strategy:
|
||||
|
|
@ -196,7 +198,7 @@ jobs:
|
|||
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 #v3.1.0
|
||||
with:
|
||||
submodules: "recursive"
|
||||
ref: ${{ needs.prepare_build.outputs.CI_TAG }}
|
||||
ref: ${{ needs.prepare_build.outputs.ci_tag }}
|
||||
repository: ${{ github.repository }}
|
||||
- name: Prepare
|
||||
run: ci/prepare/windows/prepare.ps1
|
||||
|
|
@ -209,7 +211,7 @@ jobs:
|
|||
run: ci/actions/windows/deploy.ps1
|
||||
env:
|
||||
NETWORK: ${{ matrix.network }}
|
||||
TAG: ${{ needs.prepare_build.outputs.CI_TAG }}
|
||||
TAG: ${{ needs.prepare_build.outputs.ci_tag }}
|
||||
S3_BUCKET_NAME: ${{ vars.S3_BUCKET_NAME }}
|
||||
S3_BUILD_DIRECTORY: ${{ vars.S3_BUILD_DIRECTORY }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
|
|
@ -97,7 +97,6 @@ set_property(
|
|||
block_arrival
|
||||
block_processor
|
||||
block_uniquer
|
||||
confirmation_height_processor
|
||||
dropped_elections,
|
||||
election_winner_details
|
||||
gap_cache
|
||||
|
|
|
|||
|
|
@ -1,220 +1,224 @@
|
|||
#!/bin/bash
|
||||
|
||||
# This script creates the next tag for the current branch by incrementing the version_pre_release by 1
|
||||
# A new tag is only created if a new commit has been detected compared to the previous tag.
|
||||
# The tag has the following format V${current_version_major}.${current_version_minor}${branch_name}
|
||||
# ${branch_name} is converted to "DB" if the script operates on develop branch. (e.g first tag for V26: V26.0DB1)
|
||||
# if -c flag is provided, version_pre_release in CMakeLists.txt is incremented and a new tag is created and pushed to origin
|
||||
# if -o is provided, "build_tag" , "version_pre_release" and "tag_created" are written to file
|
||||
# If it executes on a release-branch :
|
||||
# --> if there is no new commit, the same tag is generated again
|
||||
# --> If there is a new commit compared to the previous tag, we would increment the minor version by 1 and build the new binaries & docker images
|
||||
# Script Description
|
||||
|
||||
#!/bin/bash
|
||||
# Purpose:
|
||||
# This script generates a new Git tag based on the current branch and previously generated tags.
|
||||
# It creates a new tag only if there's a new commit compared to the previous tag.
|
||||
|
||||
# Tag Format:
|
||||
# General: V{MAJOR}.{MINOR}{tag_suffix}{increment}
|
||||
# For releases/v branch: V{MAJOR}.{MINOR}
|
||||
|
||||
# Options:
|
||||
# $IS_RELEASE_BUILD : Indicates a release build. In this case, {tag_suffix} is ignored.
|
||||
# New commit: Increments the {MINOR} version.
|
||||
# -s {tag_suffix} : overwrites tag_suffix derived from the branch name. Derived tag_suffixes are :
|
||||
# DB for develop branch (e.g. V26.0DB1)
|
||||
# RC for releases/v branch (e.g. V26.0RC1)
|
||||
# {branch_name} for other branches (e.g. V26.0current_git_branch1)
|
||||
# -c : Create and push the tag to origin.
|
||||
# -o {output} : Write results to the specified output file.
|
||||
|
||||
set -e
|
||||
set -x
|
||||
set -x
|
||||
output=""
|
||||
create=false
|
||||
tag_created="false"
|
||||
push_tag=false
|
||||
is_release_build=${IS_RELEASE_BUILD:-false}
|
||||
tag_suffix=""
|
||||
|
||||
while getopts ":o:c" opt; do
|
||||
case ${opt} in
|
||||
o )
|
||||
output=$OPTARG
|
||||
;;
|
||||
c )
|
||||
create=true
|
||||
;;
|
||||
\? )
|
||||
echo "Invalid Option: -$OPTARG" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
: )
|
||||
echo "Invalid Option: -$OPTARG requires an argument" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
while getopts ":o:cs:" opt; do
|
||||
case ${opt} in
|
||||
o)
|
||||
output=$OPTARG
|
||||
;;
|
||||
c)
|
||||
push_tag=true
|
||||
;;
|
||||
s)
|
||||
tag_suffix=$OPTARG
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid Option: -$OPTARG" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
:)
|
||||
echo "Invalid Option: -$OPTARG requires an argument" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND -1))
|
||||
shift $((OPTIND - 1))
|
||||
|
||||
is_release_build() {
|
||||
[[ $is_release_branch == true && $is_release_build == true ]]
|
||||
}
|
||||
|
||||
is_release_branch_and_release_tag_exists() {
|
||||
[[ $is_release_branch == true && $exists_tag_current_release == true ]]
|
||||
}
|
||||
|
||||
get_tag_suffix() {
|
||||
local branch_name=$1
|
||||
local version_major=$2
|
||||
local tag_suffix=${branch_name//[^a-zA-Z0-9]/_}
|
||||
local existing_suffix=$1
|
||||
local branch_name=$2
|
||||
|
||||
if [[ "$branch_name" == "develop" ]]; then
|
||||
tag_suffix="DB"
|
||||
# If tag_suffix is already provided, return it
|
||||
if [[ -n "$existing_suffix" ]]; then
|
||||
echo "$existing_suffix"
|
||||
return
|
||||
fi
|
||||
|
||||
echo $tag_suffix
|
||||
# Replace non-alphanumeric characters with underscores
|
||||
local new_tag_suffix=${branch_name//[^a-zA-Z0-9]/_}
|
||||
|
||||
# Specific rules for certain branch names
|
||||
if [[ "$branch_name" == "develop" ]]; then
|
||||
new_tag_suffix="DB"
|
||||
elif [[ "$branch_name" =~ ^releases/v[0-9]+ ]]; then
|
||||
new_tag_suffix="RC"
|
||||
fi
|
||||
echo $new_tag_suffix
|
||||
}
|
||||
|
||||
get_next_tag_number() {
|
||||
local last_tag_number=$1
|
||||
local increment=$2
|
||||
echo $((last_tag_number + increment))
|
||||
}
|
||||
|
||||
get_next_minor_version() {
|
||||
local current_minor=$1
|
||||
local increment=$2
|
||||
echo $((current_minor + increment))
|
||||
}
|
||||
|
||||
get_new_release_tag() {
|
||||
local version_major=$1
|
||||
local next_minor=$2
|
||||
echo "V${version_major}.${next_minor}"
|
||||
}
|
||||
|
||||
get_new_other_tag() {
|
||||
local base_version=$1
|
||||
local next_tag_number=$2
|
||||
echo "${base_version}${next_tag_number}"
|
||||
}
|
||||
|
||||
update_output_file() {
|
||||
update_output() {
|
||||
#Responsible for either writing to file (-o flag) or to $GITHUB_ENV (when run from a workflow)
|
||||
local new_tag=$1
|
||||
local next_number=$2
|
||||
local tag_created=$3
|
||||
local tag_type=$4
|
||||
local tag_created=$2
|
||||
|
||||
if [[ -n "$output" ]]; then
|
||||
echo "build_tag =$new_tag" > $output
|
||||
echo "$tag_type =$next_number" >> $output
|
||||
echo "tag_created =$tag_created" >> $output
|
||||
# Output to the specified file if -o is used
|
||||
echo "CI_TAG=${new_tag}" >"$output"
|
||||
echo "TAG_CREATED=${tag_created}" >>"$output"
|
||||
elif [[ $GITHUB_ACTIONS == 'true' ]]; then
|
||||
# Set environment variables if -o is not used
|
||||
echo "CI_TAG=${new_tag}" >>$GITHUB_ENV
|
||||
echo "TAG_CREATED=${tag_created}" >>$GITHUB_ENV
|
||||
else
|
||||
echo "Not running in a GitHub Actions environment. No action taken for CI_TAG, CI_TAG_NUMBER, TAG_CREATED."
|
||||
fi
|
||||
}
|
||||
|
||||
update_cmake_lists() {
|
||||
local tag_type=$1
|
||||
local next_number=$2
|
||||
local tag_types=("$@") # Array of tag types
|
||||
local variable_to_update=""
|
||||
|
||||
if [[ "$tag_type" == "version_pre_release" ]]; then
|
||||
variable_to_update="CPACK_PACKAGE_VERSION_PRE_RELEASE"
|
||||
elif [[ "$tag_type" == "version_minor" ]]; then
|
||||
variable_to_update="CPACK_PACKAGE_VERSION_MINOR"
|
||||
fi
|
||||
for tag_type in "${tag_types[@]}"; do
|
||||
case "$tag_type" in
|
||||
"version_pre_release")
|
||||
variable_to_update="CPACK_PACKAGE_VERSION_PRE_RELEASE"
|
||||
new_tag_number=${tag_next_suffix_number}
|
||||
;;
|
||||
"version_minor")
|
||||
variable_to_update="CPACK_PACKAGE_VERSION_MINOR"
|
||||
new_tag_number=${tag_next_minor_number}
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -n "$variable_to_update" ]]; then
|
||||
echo "Update ${variable_to_update} to $next_number"
|
||||
sed -i.bak "s/set(${variable_to_update} \"[0-9]*\")/set(${variable_to_update} \"${next_number}\")/g" CMakeLists.txt
|
||||
rm CMakeLists.txt.bak
|
||||
fi
|
||||
if [[ -n "$variable_to_update" ]]; then
|
||||
echo "Update ${variable_to_update} to $new_tag_number"
|
||||
sed -i.bak "s/set(${variable_to_update} \"[0-9]*\")/set(${variable_to_update} \"${new_tag_number}\")/g" CMakeLists.txt
|
||||
rm CMakeLists.txt.bak
|
||||
fi
|
||||
done
|
||||
git add CMakeLists.txt
|
||||
}
|
||||
|
||||
function create_commit() {
|
||||
git diff --cached --quiet
|
||||
local has_changes=$? # store exit status of the last command
|
||||
local has_changes=$? # store exit status of the last command
|
||||
if [[ $has_changes -eq 0 ]]; then # no changes
|
||||
echo "No changes to commit"
|
||||
echo "false"
|
||||
else # changes detected
|
||||
git commit -m "Update CMakeLists.txt"
|
||||
git commit -m "Update CMakeLists.txt" >/dev/null 2>&1
|
||||
echo "true"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Fetch all existing tags
|
||||
git fetch --tags -f
|
||||
|
||||
# Fetch the last commit hash of the current branch
|
||||
current_branch_name=$(git rev-parse --abbrev-ref HEAD)
|
||||
current_commit_hash=$(git rev-parse HEAD)
|
||||
|
||||
# Fetch branch name
|
||||
branch_name=$(git rev-parse --abbrev-ref HEAD)
|
||||
|
||||
# Determine if it's a release branch or not
|
||||
is_release_branch=$(echo "$branch_name" | grep -q "releases/v$current_version_major" && echo true || echo false)
|
||||
|
||||
|
||||
# Fetch major and minor version numbers from CMakeLists.txt
|
||||
current_version_major=$(grep "CPACK_PACKAGE_VERSION_MAJOR" CMakeLists.txt | grep -o "[0-9]\+")
|
||||
current_version_minor=$(grep "CPACK_PACKAGE_VERSION_MINOR" CMakeLists.txt | grep -o "[0-9]\+")
|
||||
declare -a cmake_versions_to_update
|
||||
|
||||
# Initialize tag suffix and next number and increment
|
||||
tag_suffix=""
|
||||
next_number=0
|
||||
increment=1
|
||||
|
||||
if [[ $is_release_branch == true ]]; then
|
||||
|
||||
tag_type="version_minor"
|
||||
# Find existing tags for the release branch
|
||||
existing_release_tags=$(git tag --list "V${current_version_major}.*" | grep -E "V${current_version_major}\.[0-9]+$" || true)
|
||||
|
||||
# Check if any tag exists for the release branch
|
||||
if [[ -z "$existing_release_tags" ]]; then
|
||||
# No tag exists yet, use current minor version without incrementing
|
||||
tag_created="true"
|
||||
increment=0
|
||||
else
|
||||
# Some tags already exist
|
||||
# Get the commit hash of the latest tag
|
||||
last_tag=$(echo "$existing_release_tags" | sort -V | tail -n1)
|
||||
last_tag_commit_hash=$(git rev-list -n 1 $last_tag)
|
||||
|
||||
if [[ "$current_commit_hash" == "$last_tag_commit_hash" ]]; then
|
||||
# The commit hash of the HEAD is the same as the last tag, hence no new commits. No need to increment
|
||||
tag_created="true"
|
||||
increment=0
|
||||
else
|
||||
# There is a new commit, hence increment the minor version by 1
|
||||
tag_created="true"
|
||||
increment=1
|
||||
fi
|
||||
fi
|
||||
next_number=$(get_next_minor_version $current_version_minor $increment)
|
||||
new_tag=$(get_new_release_tag $current_version_major $next_number)
|
||||
is_release_branch=$(echo "$current_branch_name" | grep -q "releases/v$current_version_major" && echo true || echo false)
|
||||
tag_current_release="V${current_version_major}.${current_version_minor}"
|
||||
exists_tag_current_release=$(git tag --list "${tag_current_release}" | grep -qE "${tag_current_release}$" && echo true || echo false)
|
||||
|
||||
# Determine the tag type and base version format
|
||||
if is_release_build; then
|
||||
cmake_versions_to_update+=("version_minor")
|
||||
tag_base="${tag_current_release}"
|
||||
else
|
||||
# Non-release branches handling
|
||||
tag_type="version_pre_release"
|
||||
|
||||
tag_suffix=$(get_tag_suffix $branch_name $current_version_major)
|
||||
base_version="V${current_version_major}.${current_version_minor}${tag_suffix}"
|
||||
existing_tags=$(git tag --list "${base_version}*" | grep -E "${base_version}[0-9]+$" || true)
|
||||
|
||||
if [[ -n "$existing_tags" ]]; then
|
||||
last_tag=$(echo "$existing_tags" | sort -V | tail -n1)
|
||||
last_tag_number=$(echo "$last_tag" | awk -F"${tag_suffix}" '{print $2}')
|
||||
last_tag_commit_hash=$(git rev-list -n 2 $last_tag | tail -n 1) #ignore the commit that updates the version_pre_release
|
||||
|
||||
if [[ "$current_commit_hash" == "$last_tag_commit_hash" ]]; then
|
||||
echo "No new commits since the last tag. No new tag will be created."
|
||||
tag_created="false"
|
||||
else
|
||||
tag_created="true"
|
||||
next_number=$(get_next_tag_number $last_tag_number $increment)
|
||||
fi
|
||||
else
|
||||
tag_created="true"
|
||||
next_number=1 #replace the default 99
|
||||
if is_release_branch_and_release_tag_exists; then
|
||||
# Make sure RC builds have release_build_minor_version + 1
|
||||
current_version_minor=$((current_version_minor + 1))
|
||||
cmake_versions_to_update+=("version_minor")
|
||||
fi
|
||||
new_tag=$(get_new_other_tag $base_version $next_number)
|
||||
cmake_versions_to_update+=("version_pre_release")
|
||||
tag_suffix=$(get_tag_suffix "$tag_suffix" "$current_branch_name")
|
||||
tag_base="V${current_version_major}.${current_version_minor}${tag_suffix}"
|
||||
fi
|
||||
tag_next_suffix_number=1 # Will be overwritten if a previous tag exists
|
||||
tag_next_minor_number=${current_version_minor} # Default value if no previous tag exists
|
||||
|
||||
# Fetch existing tags based on the base version
|
||||
existing_tags=$(git tag --list "${tag_base}*" | grep -E "${tag_base}[0-9]*$" || true)
|
||||
should_create_tag="true"
|
||||
|
||||
# Get next tag if a previous tag exists:
|
||||
if [[ -n "$existing_tags" ]]; then
|
||||
most_recent_tag=$(echo "$existing_tags" | sort -V | tail -n1)
|
||||
|
||||
if is_release_build; then
|
||||
# Increment the minor version for release builds (-r flag is set) or RC builds if the release tag exists
|
||||
tag_next_minor_number=$((current_version_minor + 1))
|
||||
else
|
||||
tag_next_minor_number=${current_version_minor}
|
||||
fi
|
||||
|
||||
# Increment the suffix number based on the existing tags
|
||||
if [[ -n "$tag_suffix" && -n "$most_recent_tag" ]]; then
|
||||
tag_max_suffix_number=$(echo "$most_recent_tag" | awk -F"${tag_suffix}" '{print $2}')
|
||||
tag_next_suffix_number=$((tag_max_suffix_number + 1))
|
||||
fi
|
||||
# Else if no previous tag matching tag_base exists, use default values set above
|
||||
fi
|
||||
|
||||
update_output_file $new_tag $next_number $tag_created $tag_type
|
||||
# Check if the current commit is included in the last tag
|
||||
tags_containing_current_commit=$(git tag --contains "$current_commit_hash")
|
||||
if [[ -n "$most_recent_tag" ]] && echo "$tags_containing_current_commit" | grep -q "$most_recent_tag"; then
|
||||
should_create_tag="false"
|
||||
fi
|
||||
|
||||
# Generate the new tag name
|
||||
if is_release_build; then
|
||||
# tag_suffix is ignored for release builds
|
||||
new_tag="V${current_version_major}.${tag_next_minor_number}"
|
||||
else
|
||||
new_tag="${tag_base}${tag_next_suffix_number}"
|
||||
fi
|
||||
|
||||
update_output $new_tag $should_create_tag
|
||||
|
||||
# Skip tag creation if no new commits
|
||||
if [[ "$tag_created" == "true" ]]; then
|
||||
echo "$new_tag"
|
||||
if [[ "$should_create_tag" == "true" ]]; then
|
||||
echo "Tag '$new_tag' ready to be created"
|
||||
else
|
||||
echo "No new commits. Tag '$new_tag' will not be created."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ $create == true ]]; then
|
||||
if [[ $push_tag == true ]]; then
|
||||
# Stash current changes
|
||||
git config user.name "${GITHUB_ACTOR}"
|
||||
git config user.email "${GITHUB_ACTOR}@users.noreply.github.com"
|
||||
|
||||
# Update variable in CMakeLists.txt
|
||||
update_cmake_lists "$tag_type" "$next_number"
|
||||
update_cmake_lists "${cmake_versions_to_update[@]}"
|
||||
|
||||
commit_made=$(create_commit)
|
||||
|
||||
|
|
@ -222,15 +226,17 @@ if [[ $create == true ]]; then
|
|||
git push origin "$new_tag" -f
|
||||
echo "The tag $new_tag has been created and pushed."
|
||||
|
||||
# If it's a release branch, also push the commit to the branch
|
||||
if [[ $is_release_branch == true ]]; then
|
||||
git push origin "$branch_name" -f
|
||||
echo "The commit has been pushed to the $branch_name branch."
|
||||
fi
|
||||
# If it's a release build, also push the commit to the branch
|
||||
if is_release_build; then
|
||||
git push origin "$current_branch_name" -f
|
||||
echo "The commit has been pushed to the $current_branch_name branch."
|
||||
|
||||
# Only reset local branch if a commit was made and it's not a "releases" branch.
|
||||
if [[ "$commit_made" == "true" && $is_release_branch == false ]]; then
|
||||
elif [[ "$commit_made" == "true" ]]; then
|
||||
# Resets the last commit on non-release branches after tagging, keeping the current branch clean.
|
||||
git reset --hard HEAD~1
|
||||
echo "The commit used for the tag does not exist on any branch."
|
||||
fi
|
||||
fi
|
||||
|
||||
else
|
||||
echo "Tag was not created. Run the script with -c option to create and push the tag"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@ fi
|
|||
|
||||
if [[ "$NETWORK" = "LIVE" ]]; then
|
||||
echo "Live"
|
||||
network_tag_suffix=''
|
||||
if [[ "$IS_RELEASE_BUILD" = "true" ]]; then
|
||||
network_tag_suffix=''
|
||||
else
|
||||
network_tag_suffix='-nightly'
|
||||
fi
|
||||
network="live"
|
||||
elif [[ "$NETWORK" = "BETA" ]]; then
|
||||
echo "Beta"
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ add_executable(
|
|||
bootstrap_ascending.cpp
|
||||
bootstrap_server.cpp
|
||||
cli.cpp
|
||||
confirmation_height.cpp
|
||||
confirmation_solicitor.cpp
|
||||
confirming_set.cpp
|
||||
conflicts.cpp
|
||||
difficulty.cpp
|
||||
distributed_work.cpp
|
||||
|
|
@ -25,6 +25,7 @@ add_executable(
|
|||
frontiers_confirmation.cpp
|
||||
ipc.cpp
|
||||
ledger.cpp
|
||||
ledger_confirm.cpp
|
||||
locks.cpp
|
||||
logging.cpp
|
||||
message.cpp
|
||||
|
|
@ -39,6 +40,7 @@ add_executable(
|
|||
processor_service.cpp
|
||||
rep_crawler.cpp
|
||||
peer_container.cpp
|
||||
rep_weight_store.cpp
|
||||
scheduler_buckets.cpp
|
||||
request_aggregator.cpp
|
||||
signal_manager.cpp
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/confirming_set.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/scheduler/component.hpp>
|
||||
#include <nano/node/scheduler/manual.hpp>
|
||||
|
|
@ -147,7 +149,7 @@ TEST (active_transactions, confirm_frontier)
|
|||
std::shared_ptr<nano::election> election2;
|
||||
ASSERT_TIMELY (5s, election2 = node2.active.election (send->qualified_root ()));
|
||||
ASSERT_TIMELY (5s, nano::test::confirmed (node2, { send }));
|
||||
ASSERT_TIMELY_EQ (5s, node2.ledger.cache.cemented_count, 2);
|
||||
ASSERT_TIMELY_EQ (5s, node2.ledger.cemented_count (), 2);
|
||||
ASSERT_TIMELY (5s, node2.active.empty ());
|
||||
ASSERT_GT (election2->confirmation_request_count, 0u);
|
||||
}
|
||||
|
|
@ -234,7 +236,7 @@ TEST (active_transactions, keep_local)
|
|||
// ASSERT_EQ (1, node.scheduler.size ());
|
||||
}
|
||||
|
||||
TEST (active_transactions, inactive_votes_cache)
|
||||
TEST (inactive_votes_cache, basic)
|
||||
{
|
||||
nano::test::system system (1);
|
||||
auto & node = *system.nodes[0];
|
||||
|
|
@ -258,7 +260,7 @@ TEST (active_transactions, inactive_votes_cache)
|
|||
/**
|
||||
* This test case confirms that a non final vote cannot cause an election to become confirmed
|
||||
*/
|
||||
TEST (active_transactions, inactive_votes_cache_non_final)
|
||||
TEST (inactive_votes_cache, non_final)
|
||||
{
|
||||
nano::test::system system (1);
|
||||
auto & node = *system.nodes[0];
|
||||
|
|
@ -284,7 +286,7 @@ TEST (active_transactions, inactive_votes_cache_non_final)
|
|||
ASSERT_FALSE (election->confirmed ());
|
||||
}
|
||||
|
||||
TEST (active_transactions, inactive_votes_cache_fork)
|
||||
TEST (inactive_votes_cache, fork)
|
||||
{
|
||||
nano::test::system system{ 1 };
|
||||
auto & node = *system.nodes[0];
|
||||
|
|
@ -324,7 +326,7 @@ TEST (active_transactions, inactive_votes_cache_fork)
|
|||
ASSERT_EQ (1, node.stats.count (nano::stat::type::election, nano::stat::detail::vote_cached));
|
||||
}
|
||||
|
||||
TEST (active_transactions, inactive_votes_cache_existing_vote)
|
||||
TEST (inactive_votes_cache, existing_vote)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::node_config node_config = system.default_config ();
|
||||
|
|
@ -364,12 +366,13 @@ TEST (active_transactions, inactive_votes_cache_existing_vote)
|
|||
ASSERT_EQ (send->hash (), last_vote1.hash);
|
||||
ASSERT_EQ (nano::vote::timestamp_min * 1, last_vote1.timestamp);
|
||||
// Attempt to change vote with inactive_votes_cache
|
||||
nano::unique_lock<nano::mutex> active_lock (node.active.mutex);
|
||||
node.vote_cache.vote (send->hash (), vote1);
|
||||
auto cache = node.vote_cache.find (send->hash ());
|
||||
ASSERT_TRUE (cache);
|
||||
ASSERT_EQ (1, cache->voters ().size ());
|
||||
cache->fill (election);
|
||||
node.vote_cache.insert (vote1);
|
||||
auto cached = node.vote_cache.find (send->hash ());
|
||||
ASSERT_EQ (1, cached.size ());
|
||||
for (auto const & cached_vote : cached)
|
||||
{
|
||||
node.active.vote (cached_vote);
|
||||
}
|
||||
// Check that election data is not changed
|
||||
ASSERT_EQ (2, election->votes ().size ());
|
||||
auto last_vote2 (election->votes ()[key.pub]);
|
||||
|
|
@ -379,7 +382,7 @@ TEST (active_transactions, inactive_votes_cache_existing_vote)
|
|||
ASSERT_EQ (0, node.stats.count (nano::stat::type::election, nano::stat::detail::vote_cached));
|
||||
}
|
||||
|
||||
TEST (active_transactions, inactive_votes_cache_multiple_votes)
|
||||
TEST (inactive_votes_cache, multiple_votes)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::node_config node_config = system.default_config ();
|
||||
|
|
@ -425,8 +428,7 @@ TEST (active_transactions, inactive_votes_cache_multiple_votes)
|
|||
auto vote2 = nano::test::make_vote (nano::dev::genesis_key, { send1 }, 0, 0);
|
||||
node.vote_processor.vote (vote2, std::make_shared<nano::transport::inproc::channel> (node, node));
|
||||
|
||||
ASSERT_TIMELY (5s, node.vote_cache.find (send1->hash ()));
|
||||
ASSERT_TIMELY_EQ (5s, node.vote_cache.find (send1->hash ())->voters ().size (), 2);
|
||||
ASSERT_TIMELY_EQ (5s, node.vote_cache.find (send1->hash ()).size (), 2);
|
||||
ASSERT_EQ (1, node.vote_cache.size ());
|
||||
node.scheduler.priority.activate (nano::dev::genesis_key.pub, node.store.tx_begin_read ());
|
||||
std::shared_ptr<nano::election> election;
|
||||
|
|
@ -435,7 +437,7 @@ TEST (active_transactions, inactive_votes_cache_multiple_votes)
|
|||
ASSERT_EQ (2, node.stats.count (nano::stat::type::election, nano::stat::detail::vote_cached));
|
||||
}
|
||||
|
||||
TEST (active_transactions, inactive_votes_cache_election_start)
|
||||
TEST (inactive_votes_cache, election_start)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::node_config node_config = system.default_config ();
|
||||
|
|
@ -484,9 +486,9 @@ TEST (active_transactions, inactive_votes_cache_election_start)
|
|||
ASSERT_EQ (nano::block_status::progress, node.process (send2));
|
||||
ASSERT_EQ (nano::block_status::progress, node.process (open1));
|
||||
ASSERT_EQ (nano::block_status::progress, node.process (open2));
|
||||
ASSERT_TIMELY_EQ (5s, 5, node.ledger.cache.block_count);
|
||||
ASSERT_TIMELY_EQ (5s, 5, node.ledger.block_count ());
|
||||
ASSERT_TRUE (node.active.empty ());
|
||||
ASSERT_EQ (1, node.ledger.cache.cemented_count);
|
||||
ASSERT_EQ (1, node.ledger.cemented_count ());
|
||||
// These blocks will be processed later
|
||||
auto send3 = send_block_builder.make_block ()
|
||||
.previous (send2->hash ())
|
||||
|
|
@ -508,7 +510,7 @@ TEST (active_transactions, inactive_votes_cache_election_start)
|
|||
node.vote_processor.vote (vote1, std::make_shared<nano::transport::inproc::channel> (node, node));
|
||||
ASSERT_TIMELY_EQ (5s, node.vote_cache.size (), 3);
|
||||
ASSERT_TRUE (node.active.empty ());
|
||||
ASSERT_EQ (1, node.ledger.cache.cemented_count);
|
||||
ASSERT_EQ (1, node.ledger.cemented_count ());
|
||||
|
||||
// 2 votes are required to start election (dev network)
|
||||
auto vote2 = nano::test::make_vote (key2, { open1, open2, send4 });
|
||||
|
|
@ -521,21 +523,20 @@ TEST (active_transactions, inactive_votes_cache_election_start)
|
|||
auto vote0 = nano::test::make_final_vote (nano::dev::genesis_key, { open1, open2, send4 });
|
||||
node.vote_processor.vote (vote0, std::make_shared<nano::transport::inproc::channel> (node, node));
|
||||
ASSERT_TIMELY_EQ (5s, 0, node.active.size ());
|
||||
ASSERT_TIMELY_EQ (5s, 5, node.ledger.cache.cemented_count);
|
||||
ASSERT_TIMELY_EQ (5s, 5, node.ledger.cemented_count ());
|
||||
ASSERT_TRUE (nano::test::confirmed (node, { send1, send2, open1, open2 }));
|
||||
|
||||
// A late block arrival also checks the inactive votes cache
|
||||
ASSERT_TRUE (node.active.empty ());
|
||||
auto send4_cache (node.vote_cache.find (send4->hash ()));
|
||||
ASSERT_TRUE (send4_cache);
|
||||
ASSERT_EQ (3, send4_cache->voters ().size ());
|
||||
ASSERT_EQ (3, send4_cache.size ());
|
||||
node.process_active (send3);
|
||||
// An election is started for send6 but does not
|
||||
ASSERT_FALSE (node.block_confirmed_or_being_confirmed (send3->hash ()));
|
||||
// send7 cannot be voted on but an election should be started from inactive votes
|
||||
ASSERT_FALSE (node.ledger.dependents_confirmed (node.store.tx_begin_read (), *send4));
|
||||
node.process_active (send4);
|
||||
ASSERT_TIMELY_EQ (5s, 7, node.ledger.cache.cemented_count);
|
||||
ASSERT_TIMELY_EQ (5s, 7, node.ledger.cemented_count ());
|
||||
}
|
||||
|
||||
namespace nano
|
||||
|
|
@ -582,19 +583,19 @@ TEST (active_transactions, vote_replays)
|
|||
|
||||
// First vote is not a replay and confirms the election, second vote should be a replay since the election has confirmed but not yet removed
|
||||
auto vote_send1 = nano::test::make_final_vote (nano::dev::genesis_key, { send1 });
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_send1));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_send1));
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_send1).at (send1->hash ()));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_send1).at (send1->hash ()));
|
||||
|
||||
// Wait until the election is removed, at which point the vote is still a replay since it's been recently confirmed
|
||||
ASSERT_TIMELY_EQ (5s, node.active.size (), 1);
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_send1));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_send1).at (send1->hash ()));
|
||||
|
||||
// Open new account
|
||||
auto vote_open1 = nano::test::make_final_vote (nano::dev::genesis_key, { open1 });
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_open1));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_open1));
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_open1).at (open1->hash ()));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_open1).at (open1->hash ()));
|
||||
ASSERT_TIMELY (5s, node.active.empty ());
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_open1));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_open1).at (open1->hash ()));
|
||||
ASSERT_EQ (nano::Gxrb_ratio, node.ledger.weight (key.pub));
|
||||
|
||||
// send 1 raw to key to key
|
||||
|
|
@ -615,27 +616,27 @@ TEST (active_transactions, vote_replays)
|
|||
// vote2_send2 is a non final vote with little weight, vote1_send2 is the vote that confirms the election
|
||||
auto vote1_send2 = nano::test::make_final_vote (nano::dev::genesis_key, { send2 });
|
||||
auto vote2_send2 = nano::test::make_vote (key, { send2 }, 0, 0);
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote2_send2)); // this vote cannot confirm the election
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote2_send2).at (send2->hash ())); // this vote cannot confirm the election
|
||||
ASSERT_EQ (1, node.active.size ());
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote2_send2)); // this vote cannot confirm the election
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote2_send2).at (send2->hash ())); // this vote cannot confirm the election
|
||||
ASSERT_EQ (1, node.active.size ());
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote1_send2)); // this vote confirms the election
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote1_send2).at (send2->hash ())); // this vote confirms the election
|
||||
|
||||
// this should still return replay, either because the election is still in the AEC or because it is recently confirmed
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote1_send2));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote1_send2).at (send2->hash ()));
|
||||
ASSERT_TIMELY (5s, node.active.empty ());
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote1_send2));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote2_send2));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote1_send2).at (send2->hash ()));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote2_send2).at (send2->hash ()));
|
||||
|
||||
// Removing blocks as recently confirmed makes every vote indeterminate
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (node.active.mutex);
|
||||
node.active.recently_confirmed.clear ();
|
||||
}
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_send1));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_open1));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote1_send2));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote2_send2));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_send1).at (send1->hash ()));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_open1).at (open1->hash ()));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote1_send2).at (send2->hash ()));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote2_send2).at (send2->hash ()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -828,8 +829,8 @@ TEST (active_transactions, fork_filter_cleanup)
|
|||
// how about node1 picking up "send1" from node2? we know it does because we assert at
|
||||
// the end that it is within node1's AEC, but why node1.block_count doesn't increase?
|
||||
//
|
||||
ASSERT_TIMELY_EQ (5s, node2.ledger.cache.block_count, 2);
|
||||
ASSERT_TIMELY_EQ (5s, node1.ledger.cache.block_count, 2);
|
||||
ASSERT_TIMELY_EQ (5s, node2.ledger.block_count (), 2);
|
||||
ASSERT_TIMELY_EQ (5s, node1.ledger.block_count (), 2);
|
||||
|
||||
// Block is erased from the duplicate filter
|
||||
ASSERT_TIMELY (5s, node1.network.publish_filter.apply (send_block_bytes.data (), send_block_bytes.size ()));
|
||||
|
|
@ -889,7 +890,7 @@ TEST (active_transactions, fork_replacement_tally)
|
|||
auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send, open });
|
||||
node1.vote_processor.vote (vote, std::make_shared<nano::transport::inproc::channel> (node1, node1));
|
||||
}
|
||||
ASSERT_TIMELY_EQ (5s, node1.ledger.cache.cemented_count, 1 + 2 * reps_count);
|
||||
ASSERT_TIMELY_EQ (5s, node1.ledger.cemented_count (), 1 + 2 * reps_count);
|
||||
|
||||
nano::keypair key;
|
||||
auto send_last = builder.make_block ()
|
||||
|
|
@ -981,8 +982,7 @@ TEST (active_transactions, fork_replacement_tally)
|
|||
node1.vote_processor.vote (vote, std::make_shared<nano::transport::inproc::channel> (node1, node1));
|
||||
node1.vote_processor.flush ();
|
||||
// ensure vote arrives before the block
|
||||
ASSERT_TIMELY (5s, node1.vote_cache.find (send_last->hash ()));
|
||||
ASSERT_TIMELY_EQ (5s, 1, node1.vote_cache.find (send_last->hash ())->size ());
|
||||
ASSERT_TIMELY_EQ (5s, 1, node1.vote_cache.find (send_last->hash ()).size ());
|
||||
node1.network.publish_filter.clear ();
|
||||
node2.network.flood_block (send_last);
|
||||
ASSERT_TIMELY (5s, node1.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::in) > 1);
|
||||
|
|
@ -1050,8 +1050,8 @@ TEST (active_transactions, confirm_new)
|
|||
// Let node2 know about the block
|
||||
ASSERT_TIMELY (5s, node2.block (send->hash ()));
|
||||
// Wait confirmation
|
||||
ASSERT_TIMELY (5s, node1.ledger.cache.cemented_count == 2);
|
||||
ASSERT_TIMELY (5s, node2.ledger.cache.cemented_count == 2);
|
||||
ASSERT_TIMELY (5s, node1.ledger.cemented_count () == 2);
|
||||
ASSERT_TIMELY (5s, node2.ledger.cemented_count () == 2);
|
||||
}
|
||||
|
||||
// Ensures votes are tallied on election::publish even if no vote is inserted through inactive_votes_cache
|
||||
|
|
@ -1244,7 +1244,7 @@ TEST (active_transactions, activate_inactive)
|
|||
ASSERT_NE (nullptr, election);
|
||||
election->force_confirm ();
|
||||
|
||||
ASSERT_TIMELY (5s, !node.confirmation_height_processor.is_processing_added_block (send2->hash ()));
|
||||
ASSERT_TIMELY (5s, !node.confirming_set.exists (send2->hash ()));
|
||||
ASSERT_TIMELY (5s, node.block_confirmed (send2->hash ()));
|
||||
ASSERT_TIMELY (5s, node.block_confirmed (send->hash ()));
|
||||
|
||||
|
|
@ -1536,7 +1536,7 @@ TEST (active_transactions, allow_limited_overflow)
|
|||
{
|
||||
// Non-final vote, so it stays in the AEC without getting confirmed
|
||||
auto vote = nano::test::make_vote (nano::dev::genesis_key, { block });
|
||||
node.vote_cache.vote (block->hash (), vote);
|
||||
node.vote_cache.insert (vote);
|
||||
}
|
||||
|
||||
// Ensure active elections overfill AEC only up to normal + hinted limit
|
||||
|
|
@ -1574,7 +1574,7 @@ TEST (active_transactions, allow_limited_overflow_adapt)
|
|||
{
|
||||
// Non-final vote, so it stays in the AEC without getting confirmed
|
||||
auto vote = nano::test::make_vote (nano::dev::genesis_key, { block });
|
||||
node.vote_cache.vote (block->hash (), vote);
|
||||
node.vote_cache.insert (vote);
|
||||
}
|
||||
|
||||
// Ensure hinted election amount is bounded by hinted limit
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <nano/lib/work.hpp>
|
||||
#include <nano/node/common.hpp>
|
||||
#include <nano/node/make_store.hpp>
|
||||
#include <nano/secure/common.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/secure/utility.hpp>
|
||||
#include <nano/store/account.hpp>
|
||||
|
|
@ -295,15 +296,15 @@ TEST (block_store, add_pending)
|
|||
ASSERT_TRUE (!store->init_error ());
|
||||
nano::keypair key1;
|
||||
nano::pending_key key2 (0, 0);
|
||||
nano::pending_info pending1;
|
||||
auto transaction (store->tx_begin_write ());
|
||||
ASSERT_TRUE (store->pending.get (transaction, key2, pending1));
|
||||
ASSERT_FALSE (store->pending.get (transaction, key2));
|
||||
nano::pending_info pending1;
|
||||
store->pending.put (transaction, key2, pending1);
|
||||
nano::pending_info pending2;
|
||||
ASSERT_FALSE (store->pending.get (transaction, key2, pending2));
|
||||
std::optional<nano::pending_info> pending2;
|
||||
ASSERT_TRUE (pending2 = store->pending.get (transaction, key2));
|
||||
ASSERT_EQ (pending1, pending2);
|
||||
store->pending.del (transaction, key2);
|
||||
ASSERT_TRUE (store->pending.get (transaction, key2, pending2));
|
||||
ASSERT_FALSE (store->pending.get (transaction, key2));
|
||||
}
|
||||
|
||||
TEST (block_store, pending_iterator)
|
||||
|
|
@ -379,7 +380,7 @@ TEST (block_store, genesis)
|
|||
nano::logger logger;
|
||||
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
||||
ASSERT_TRUE (!store->init_error ());
|
||||
nano::ledger_cache ledger_cache;
|
||||
nano::ledger_cache ledger_cache{ store->rep_weight };
|
||||
auto transaction (store->tx_begin_write ());
|
||||
store->initialize (transaction, ledger_cache, nano::dev::constants);
|
||||
nano::account_info info;
|
||||
|
|
@ -479,7 +480,6 @@ TEST (block_store, frontier_retrieval)
|
|||
nano::account account1{};
|
||||
nano::account_info info1 (0, 0, 0, 0, 0, 0, nano::epoch::epoch_0);
|
||||
auto transaction (store->tx_begin_write ());
|
||||
store->confirmation_height.put (transaction, account1, { 0, nano::block_hash (0) });
|
||||
store->account.put (transaction, account1, info1);
|
||||
nano::account_info info2;
|
||||
store->account.get (transaction, account1, info2);
|
||||
|
|
@ -603,10 +603,6 @@ TEST (block_store, latest_find)
|
|||
nano::account account2 (3);
|
||||
nano::block_hash hash2 (4);
|
||||
auto transaction (store->tx_begin_write ());
|
||||
store->confirmation_height.put (transaction, account1, { 0, nano::block_hash (0) });
|
||||
store->account.put (transaction, account1, { hash1, account1, hash1, 100, 0, 300, nano::epoch::epoch_0 });
|
||||
store->confirmation_height.put (transaction, account2, { 0, nano::block_hash (0) });
|
||||
store->account.put (transaction, account2, { hash2, account2, hash2, 200, 0, 400, nano::epoch::epoch_0 });
|
||||
auto first (store->account.begin (transaction));
|
||||
auto second (store->account.begin (transaction));
|
||||
++second;
|
||||
|
|
@ -767,7 +763,6 @@ TEST (block_store, latest_exists)
|
|||
nano::account two (2);
|
||||
nano::account_info info;
|
||||
auto transaction (store->tx_begin_write ());
|
||||
store->confirmation_height.put (transaction, two, { 0, nano::block_hash (0) });
|
||||
store->account.put (transaction, two, info);
|
||||
nano::account one (1);
|
||||
ASSERT_FALSE (store->account.exists (transaction, one));
|
||||
|
|
@ -785,7 +780,6 @@ TEST (block_store, large_iteration)
|
|||
nano::account account;
|
||||
nano::random_pool::generate_block (account.bytes.data (), account.bytes.size ());
|
||||
accounts1.insert (account);
|
||||
store->confirmation_height.put (transaction, account, { 0, nano::block_hash (0) });
|
||||
store->account.put (transaction, account, nano::account_info ());
|
||||
}
|
||||
std::unordered_set<nano::account> accounts2;
|
||||
|
|
@ -820,11 +814,6 @@ TEST (block_store, frontier)
|
|||
auto transaction (store->tx_begin_write ());
|
||||
nano::block_hash hash (100);
|
||||
nano::account account (200);
|
||||
ASSERT_TRUE (store->frontier.get (transaction, hash).is_zero ());
|
||||
store->frontier.put (transaction, hash, account);
|
||||
ASSERT_EQ (account, store->frontier.get (transaction, hash));
|
||||
store->frontier.del (transaction, hash);
|
||||
ASSERT_TRUE (store->frontier.get (transaction, hash).is_zero ());
|
||||
}
|
||||
|
||||
TEST (block_store, block_replace)
|
||||
|
|
@ -893,7 +882,6 @@ TEST (block_store, account_count)
|
|||
auto transaction (store->tx_begin_write ());
|
||||
ASSERT_EQ (0, store->account.count (transaction));
|
||||
nano::account account (200);
|
||||
store->confirmation_height.put (transaction, account, { 0, nano::block_hash (0) });
|
||||
store->account.put (transaction, account, nano::account_info ());
|
||||
}
|
||||
auto transaction (store->tx_begin_read ());
|
||||
|
|
@ -906,9 +894,10 @@ TEST (block_store, cemented_count_cache)
|
|||
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
||||
ASSERT_TRUE (!store->init_error ());
|
||||
auto transaction (store->tx_begin_write ());
|
||||
nano::ledger_cache ledger_cache;
|
||||
store->initialize (transaction, ledger_cache, nano::dev::constants);
|
||||
ASSERT_EQ (1, ledger_cache.cemented_count);
|
||||
nano::stats stats;
|
||||
nano::ledger ledger (*store, stats, nano::dev::constants);
|
||||
store->initialize (transaction, ledger.cache, nano::dev::constants);
|
||||
ASSERT_EQ (1, ledger.cemented_count ());
|
||||
}
|
||||
|
||||
TEST (block_store, block_random)
|
||||
|
|
@ -916,7 +905,7 @@ TEST (block_store, block_random)
|
|||
nano::logger logger;
|
||||
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
|
||||
{
|
||||
nano::ledger_cache ledger_cache;
|
||||
nano::ledger_cache ledger_cache{ store->rep_weight };
|
||||
auto transaction (store->tx_begin_write ());
|
||||
store->initialize (transaction, ledger_cache, nano::dev::constants);
|
||||
}
|
||||
|
|
@ -943,7 +932,7 @@ TEST (block_store, pruned_random)
|
|||
block->sideband_set ({});
|
||||
auto hash1 (block->hash ());
|
||||
{
|
||||
nano::ledger_cache ledger_cache;
|
||||
nano::ledger_cache ledger_cache{ store->rep_weight };
|
||||
auto transaction (store->tx_begin_write ());
|
||||
store->initialize (transaction, ledger_cache, nano::dev::constants);
|
||||
store->pruned.put (transaction, hash1);
|
||||
|
|
@ -973,7 +962,7 @@ TEST (block_store, state_block)
|
|||
|
||||
block1->sideband_set ({});
|
||||
{
|
||||
nano::ledger_cache ledger_cache;
|
||||
nano::ledger_cache ledger_cache{ store->rep_weight };
|
||||
auto transaction (store->tx_begin_write ());
|
||||
store->initialize (transaction, ledger_cache, nano::dev::constants);
|
||||
ASSERT_EQ (nano::block_type::state, block1->type ());
|
||||
|
|
@ -1401,6 +1390,42 @@ TEST (mdb_block_store, upgrade_v21_v22)
|
|||
// Testing the upgrade code worked
|
||||
check_correct_state ();
|
||||
}
|
||||
|
||||
TEST (mdb_block_store, upgrade_v23_v24)
|
||||
{
|
||||
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
||||
{
|
||||
// Direct lmdb operations are used to simulate the old ledger format so this test will not work on RocksDB
|
||||
GTEST_SKIP ();
|
||||
}
|
||||
|
||||
auto path (nano::unique_path () / "data.ldb");
|
||||
nano::logger logger;
|
||||
nano::stats stats;
|
||||
auto const check_correct_state = [&] () {
|
||||
nano::store::lmdb::component store (logger, path, nano::dev::constants);
|
||||
auto transaction (store.tx_begin_write ());
|
||||
ASSERT_EQ (store.version.get (transaction), store.version_current);
|
||||
MDB_dbi frontiers_handle{ 0 };
|
||||
ASSERT_EQ (MDB_NOTFOUND, mdb_dbi_open (store.env.tx (transaction), "frontiers", 0, &frontiers_handle));
|
||||
};
|
||||
|
||||
// Testing current version doesn't contain the frontiers table
|
||||
check_correct_state ();
|
||||
|
||||
// Setting the database to its 23st version state
|
||||
{
|
||||
nano::store::lmdb::component store (logger, path, nano::dev::constants);
|
||||
auto transaction (store.tx_begin_write ());
|
||||
store.version.put (transaction, 23);
|
||||
MDB_dbi frontiers_handle{ 0 };
|
||||
ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "frontiers", MDB_CREATE, &frontiers_handle));
|
||||
ASSERT_EQ (store.version.get (transaction), 23);
|
||||
}
|
||||
|
||||
// Testing the upgrade code worked
|
||||
check_correct_state ();
|
||||
}
|
||||
}
|
||||
|
||||
namespace nano::store::rocksdb
|
||||
|
|
@ -1450,6 +1475,48 @@ TEST (rocksdb_block_store, upgrade_v21_v22)
|
|||
}
|
||||
}
|
||||
|
||||
// Tests that the new rep_weight table gets filled with all
|
||||
// existing representatives
|
||||
TEST (mdb_block_store, upgrade_v22_to_v23)
|
||||
{
|
||||
nano::logger logger;
|
||||
auto const path = nano::unique_path ();
|
||||
nano::account rep_a{ 123 };
|
||||
nano::account rep_b{ 456 };
|
||||
// Setting the database to its 22nd version state
|
||||
{
|
||||
auto store{ nano::make_store (logger, path, nano::dev::constants) };
|
||||
auto txn{ store->tx_begin_write () };
|
||||
|
||||
// Add three accounts referencing two representatives
|
||||
nano::account_info info1{};
|
||||
info1.representative = rep_a;
|
||||
info1.balance = 1000;
|
||||
store->account.put (txn, 1, info1);
|
||||
|
||||
nano::account_info info2{};
|
||||
info2.representative = rep_a;
|
||||
info2.balance = 500;
|
||||
store->account.put (txn, 2, info2);
|
||||
|
||||
nano::account_info info3{};
|
||||
info3.representative = rep_b;
|
||||
info3.balance = 42;
|
||||
store->account.put (txn, 3, info3);
|
||||
|
||||
store->version.put (txn, 22);
|
||||
}
|
||||
|
||||
// Testing the upgrade code worked
|
||||
auto store{ nano::make_store (logger, path, nano::dev::constants) };
|
||||
auto txn (store->tx_begin_read ());
|
||||
ASSERT_EQ (store->version.get (txn), store->version_current);
|
||||
|
||||
// The rep_weight table should contain all reps now
|
||||
ASSERT_EQ (1500, store->rep_weight.get (txn, rep_a));
|
||||
ASSERT_EQ (42, store->rep_weight.get (txn, rep_b));
|
||||
}
|
||||
|
||||
TEST (mdb_block_store, upgrade_backup)
|
||||
{
|
||||
if (nano::rocksdb_config::using_rocksdb_in_tests ())
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/bootstrap/block_deserializer.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_frontier.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_lazy.hpp>
|
||||
|
|
@ -295,7 +296,6 @@ TEST (bootstrap_processor, process_none)
|
|||
|
||||
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint (), false);
|
||||
ASSERT_TIMELY (5s, done);
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
// Bootstrap can pull one basic block
|
||||
|
|
@ -319,7 +319,6 @@ TEST (bootstrap_processor, process_one)
|
|||
ASSERT_NE (node0->latest (nano::dev::genesis_key.pub), node1->latest (nano::dev::genesis_key.pub));
|
||||
node1->bootstrap_initiator.bootstrap (node0->network.endpoint (), false);
|
||||
ASSERT_TIMELY_EQ (10s, node1->latest (nano::dev::genesis_key.pub), node0->latest (nano::dev::genesis_key.pub));
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, process_two)
|
||||
|
|
@ -340,7 +339,6 @@ TEST (bootstrap_processor, process_two)
|
|||
ASSERT_NE (node1->latest (nano::dev::genesis_key.pub), node0->latest (nano::dev::genesis_key.pub)); // nodes should be out of sync here
|
||||
node1->bootstrap_initiator.bootstrap (node0->network.endpoint (), false); // bootstrap triggered
|
||||
ASSERT_TIMELY_EQ (5s, node1->latest (nano::dev::genesis_key.pub), node0->latest (nano::dev::genesis_key.pub)); // nodes should sync up
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
// Bootstrap can pull universal blocks
|
||||
|
|
@ -386,7 +384,6 @@ TEST (bootstrap_processor, process_state)
|
|||
ASSERT_NE (node1->latest (nano::dev::genesis_key.pub), block2->hash ());
|
||||
node1->bootstrap_initiator.bootstrap (node0->network.endpoint (), false);
|
||||
ASSERT_TIMELY_EQ (5s, node1->latest (nano::dev::genesis_key.pub), block2->hash ());
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, process_new)
|
||||
|
|
@ -425,7 +422,6 @@ TEST (bootstrap_processor, process_new)
|
|||
auto node3 = system.make_disconnected_node ();
|
||||
node3->bootstrap_initiator.bootstrap (node1->network.endpoint (), false);
|
||||
ASSERT_TIMELY_EQ (5s, node3->balance (key2.pub), amount);
|
||||
node3->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, pull_diamond)
|
||||
|
|
@ -477,7 +473,6 @@ TEST (bootstrap_processor, pull_diamond)
|
|||
auto node1 = system.make_disconnected_node ();
|
||||
node1->bootstrap_initiator.bootstrap (node0->network.endpoint (), false);
|
||||
ASSERT_TIMELY_EQ (5s, node1->balance (nano::dev::genesis_key.pub), 100);
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, DISABLED_pull_requeue_network_error)
|
||||
|
|
@ -516,7 +511,6 @@ TEST (bootstrap_processor, DISABLED_pull_requeue_network_error)
|
|||
++attempt->pulling;
|
||||
node1->bootstrap_initiator.connections->pulls.emplace_back (nano::dev::genesis_key.pub, send1->hash (), nano::dev::genesis->hash (), attempt->incremental_id);
|
||||
node1->bootstrap_initiator.connections->request_pull (lock);
|
||||
node2->stop ();
|
||||
}
|
||||
ASSERT_TIMELY (5s, attempt == nullptr || attempt->requeued_pulls == 1);
|
||||
ASSERT_EQ (0, node1->stats.count (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_failed_account, nano::stat::dir::in)); // Requeue is not increasing failed attempts
|
||||
|
|
@ -585,7 +579,6 @@ TEST (bootstrap_processor, push_diamond)
|
|||
auto node2 = system.add_node (config, flags);
|
||||
node1->bootstrap_initiator.bootstrap (node2->network.endpoint (), false);
|
||||
ASSERT_TIMELY_EQ (5s, node2->balance (nano::dev::genesis_key.pub), 100);
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, push_diamond_pruning)
|
||||
|
|
@ -659,6 +652,7 @@ TEST (bootstrap_processor, push_diamond_pruning)
|
|||
|
||||
{
|
||||
auto transaction (node1->store.tx_begin_write ());
|
||||
node1->ledger.confirm (transaction, open->hash ());
|
||||
ASSERT_EQ (1, node1->ledger.pruning_action (transaction, send1->hash (), 2));
|
||||
ASSERT_EQ (1, node1->ledger.pruning_action (transaction, open->hash (), 1));
|
||||
ASSERT_TRUE (node1->ledger.block_exists (transaction, nano::dev::genesis->hash ()));
|
||||
|
|
@ -668,15 +662,14 @@ TEST (bootstrap_processor, push_diamond_pruning)
|
|||
ASSERT_TRUE (node1->store.pruned.exists (transaction, open->hash ()));
|
||||
ASSERT_TRUE (node1->ledger.block_exists (transaction, send2->hash ()));
|
||||
ASSERT_TRUE (node1->ledger.block_exists (transaction, receive->hash ()));
|
||||
ASSERT_EQ (2, node1->ledger.cache.pruned_count);
|
||||
ASSERT_EQ (5, node1->ledger.cache.block_count);
|
||||
ASSERT_EQ (2, node1->ledger.pruned_count ());
|
||||
ASSERT_EQ (5, node1->ledger.block_count ());
|
||||
}
|
||||
|
||||
// 2nd bootstrap
|
||||
node1->bootstrap_initiator.bootstrap (node0->network.endpoint (), false);
|
||||
ASSERT_TIMELY_EQ (5s, node0->balance (nano::dev::genesis_key.pub), 100);
|
||||
ASSERT_TIMELY_EQ (5s, node1->balance (nano::dev::genesis_key.pub), 100);
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, push_one)
|
||||
|
|
@ -699,7 +692,6 @@ TEST (bootstrap_processor, push_one)
|
|||
|
||||
node1->bootstrap_initiator.bootstrap (node0->network.endpoint (), false);
|
||||
ASSERT_TIMELY_EQ (5s, node0->balance (nano::dev::genesis_key.pub), genesis_balance - 100);
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, lazy_hash)
|
||||
|
|
@ -774,7 +766,6 @@ TEST (bootstrap_processor, lazy_hash)
|
|||
}
|
||||
// Check processed blocks
|
||||
ASSERT_TIMELY (10s, node1->balance (key2.pub) != 0);
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, lazy_hash_bootstrap_id)
|
||||
|
|
@ -849,7 +840,6 @@ TEST (bootstrap_processor, lazy_hash_bootstrap_id)
|
|||
}
|
||||
// Check processed blocks
|
||||
ASSERT_TIMELY (10s, node1->balance (key2.pub) != 0);
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, lazy_hash_pruning)
|
||||
|
|
@ -984,25 +974,24 @@ TEST (bootstrap_processor, lazy_hash_pruning)
|
|||
ASSERT_TIMELY (5s, node1->block_confirmed (change1->hash ()));
|
||||
ASSERT_TIMELY (5s, node1->block_confirmed (change2->hash ()));
|
||||
ASSERT_TIMELY (5s, node1->active.empty ());
|
||||
ASSERT_EQ (5, node1->ledger.cache.block_count);
|
||||
ASSERT_EQ (5, node1->ledger.cache.cemented_count);
|
||||
ASSERT_EQ (5, node1->ledger.block_count ());
|
||||
ASSERT_EQ (5, node1->ledger.cemented_count ());
|
||||
|
||||
// Pruning action
|
||||
node1->ledger_pruning (2, false);
|
||||
ASSERT_EQ (9, node0->ledger.cache.block_count);
|
||||
ASSERT_EQ (0, node0->ledger.cache.pruned_count);
|
||||
ASSERT_EQ (5, node1->ledger.cache.block_count);
|
||||
ASSERT_EQ (3, node1->ledger.cache.pruned_count);
|
||||
ASSERT_EQ (9, node0->ledger.block_count ());
|
||||
ASSERT_EQ (0, node0->ledger.pruned_count ());
|
||||
ASSERT_EQ (5, node1->ledger.block_count ());
|
||||
ASSERT_EQ (3, node1->ledger.pruned_count ());
|
||||
|
||||
// Start lazy bootstrap with last block in chain known
|
||||
nano::test::establish_tcp (system, *node1, node0->network.endpoint ());
|
||||
node1->bootstrap_initiator.bootstrap_lazy (receive3->hash (), true);
|
||||
|
||||
// Check processed blocks
|
||||
ASSERT_TIMELY_EQ (5s, node1->ledger.cache.block_count, 9);
|
||||
ASSERT_TIMELY_EQ (5s, node1->ledger.block_count (), 9);
|
||||
ASSERT_TIMELY (5s, node1->balance (key2.pub) != 0);
|
||||
ASSERT_TIMELY (5s, !node1->bootstrap_initiator.in_progress ());
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, lazy_max_pull_count)
|
||||
|
|
@ -1104,7 +1093,6 @@ TEST (bootstrap_processor, lazy_max_pull_count)
|
|||
node1->bootstrap_initiator.bootstrap_lazy (change3->hash ());
|
||||
// Check processed blocks
|
||||
ASSERT_TIMELY (10s, node1->block (change3->hash ()));
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, lazy_unclear_state_link)
|
||||
|
|
@ -1173,7 +1161,6 @@ TEST (bootstrap_processor, lazy_unclear_state_link)
|
|||
node2->bootstrap_initiator.bootstrap_lazy (receive->hash ());
|
||||
ASSERT_TIMELY (5s, nano::test::exists (*node2, { send1, send2, open, receive }));
|
||||
ASSERT_EQ (0, node2->stats.count (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_failed_account, nano::stat::dir::in));
|
||||
node2->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, lazy_unclear_state_link_not_existing)
|
||||
|
|
@ -1232,7 +1219,6 @@ TEST (bootstrap_processor, lazy_unclear_state_link_not_existing)
|
|||
ASSERT_TIMELY (15s, !node2->bootstrap_initiator.in_progress ());
|
||||
ASSERT_TIMELY (15s, nano::test::block_or_pruned_all_exists (*node2, { send1, open, send2 }));
|
||||
ASSERT_EQ (1, node2->stats.count (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_failed_account, nano::stat::dir::in));
|
||||
node2->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, lazy_destinations)
|
||||
|
|
@ -1311,7 +1297,6 @@ TEST (bootstrap_processor, lazy_destinations)
|
|||
ASSERT_TIMELY (5s, node2->ledger.block_or_pruned_exists (send2->hash ()));
|
||||
ASSERT_FALSE (node2->ledger.block_or_pruned_exists (open->hash ()));
|
||||
ASSERT_FALSE (node2->ledger.block_or_pruned_exists (state_open->hash ()));
|
||||
node2->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, lazy_pruning_missing_block)
|
||||
|
|
@ -1385,14 +1370,14 @@ TEST (bootstrap_processor, lazy_pruning_missing_block)
|
|||
// Confirm last block to prune previous
|
||||
ASSERT_TRUE (nano::test::start_elections (system, *node1, { send1, send2, open, state_open }, true));
|
||||
ASSERT_TIMELY (5s, nano::test::confirmed (*node1, { send2, open, state_open }));
|
||||
ASSERT_EQ (5, node1->ledger.cache.block_count);
|
||||
ASSERT_EQ (5, node1->ledger.cache.cemented_count);
|
||||
ASSERT_EQ (5, node1->ledger.block_count ());
|
||||
ASSERT_EQ (5, node1->ledger.cemented_count ());
|
||||
|
||||
// Pruning action, send1 should get pruned
|
||||
ASSERT_EQ (0, node1->ledger.cache.pruned_count);
|
||||
ASSERT_EQ (0, node1->ledger.pruned_count ());
|
||||
node1->ledger_pruning (2, false);
|
||||
ASSERT_EQ (1, node1->ledger.cache.pruned_count);
|
||||
ASSERT_EQ (5, node1->ledger.cache.block_count);
|
||||
ASSERT_EQ (1, node1->ledger.pruned_count ());
|
||||
ASSERT_EQ (5, node1->ledger.block_count ());
|
||||
ASSERT_TRUE (node1->ledger.store.pruned.exists (node1->ledger.store.tx_begin_read (), send1->hash ()));
|
||||
ASSERT_TRUE (nano::test::exists (*node1, { send2, open, state_open }));
|
||||
|
||||
|
|
@ -1408,7 +1393,7 @@ TEST (bootstrap_processor, lazy_pruning_missing_block)
|
|||
ASSERT_TIMELY (5s, lazy_attempt->stopped || lazy_attempt->requeued_pulls >= 4);
|
||||
|
||||
// Some blocks cannot be retrieved from pruned node
|
||||
ASSERT_EQ (1, node2->ledger.cache.block_count);
|
||||
ASSERT_EQ (1, node2->ledger.block_count ());
|
||||
ASSERT_TRUE (nano::test::block_or_pruned_none_exists (*node2, { send1, send2, open, state_open }));
|
||||
{
|
||||
auto transaction (node2->store.tx_begin_read ());
|
||||
|
|
@ -1417,10 +1402,9 @@ TEST (bootstrap_processor, lazy_pruning_missing_block)
|
|||
|
||||
// Insert missing block
|
||||
node2->process_active (send1);
|
||||
ASSERT_TIMELY_EQ (5s, 3, node2->ledger.cache.block_count);
|
||||
ASSERT_TIMELY_EQ (5s, 3, node2->ledger.block_count ());
|
||||
ASSERT_TIMELY (5s, nano::test::exists (*node2, { send1, send2 }));
|
||||
ASSERT_TRUE (nano::test::block_or_pruned_none_exists (*node2, { open, state_open }));
|
||||
node2->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, lazy_cancel)
|
||||
|
|
@ -1455,7 +1439,6 @@ TEST (bootstrap_processor, lazy_cancel)
|
|||
}
|
||||
// Cancel failing lazy bootstrap
|
||||
ASSERT_TIMELY (10s, !node1->bootstrap_initiator.in_progress ());
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, wallet_lazy_frontier)
|
||||
|
|
@ -1536,7 +1519,6 @@ TEST (bootstrap_processor, wallet_lazy_frontier)
|
|||
}
|
||||
// Check processed blocks
|
||||
ASSERT_TIMELY (10s, node1->ledger.block_or_pruned_exists (receive2->hash ()));
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
TEST (bootstrap_processor, wallet_lazy_pending)
|
||||
|
|
@ -1683,7 +1665,6 @@ TEST (bootstrap_processor, multiple_attempts)
|
|||
ASSERT_TIMELY (10s, node2->balance (key2.pub) != 0);
|
||||
// Check attempts finish
|
||||
ASSERT_TIMELY_EQ (5s, node2->bootstrap_initiator.attempts.size (), 0);
|
||||
node2->stop ();
|
||||
}
|
||||
|
||||
TEST (frontier_req_response, DISABLED_destruction)
|
||||
|
|
@ -1995,7 +1976,6 @@ TEST (bulk, genesis)
|
|||
node2->bootstrap_initiator.bootstrap (node1->network.endpoint (), false);
|
||||
ASSERT_TIMELY_EQ (10s, node2->latest (nano::dev::genesis_key.pub), node1->latest (nano::dev::genesis_key.pub));
|
||||
ASSERT_EQ (node2->latest (nano::dev::genesis_key.pub), node1->latest (nano::dev::genesis_key.pub));
|
||||
node2->stop ();
|
||||
}
|
||||
|
||||
TEST (bulk, offline_send)
|
||||
|
|
@ -2035,7 +2015,6 @@ TEST (bulk, offline_send)
|
|||
ASSERT_TIMELY_EQ (5s, node2->balance (nano::dev::genesis_key.pub), std::numeric_limits<nano::uint128_t>::max () - amount);
|
||||
// Receiving send block
|
||||
ASSERT_TIMELY_EQ (5s, node2->balance (key2.pub), amount);
|
||||
node2->stop ();
|
||||
}
|
||||
|
||||
TEST (bulk, genesis_pruning)
|
||||
|
|
@ -2072,18 +2051,18 @@ TEST (bulk, genesis_pruning)
|
|||
|
||||
ASSERT_TRUE (nano::test::start_elections (system, *node1, { send1 }, true));
|
||||
ASSERT_TIMELY (5s, node1->active.active (send2->qualified_root ()));
|
||||
ASSERT_EQ (0, node1->ledger.cache.pruned_count);
|
||||
ASSERT_EQ (0, node1->ledger.pruned_count ());
|
||||
|
||||
ASSERT_TRUE (nano::test::start_elections (system, *node1, { send2 }, true));
|
||||
ASSERT_TIMELY (5s, node1->active.active (send3->qualified_root ()));
|
||||
ASSERT_EQ (0, node1->ledger.cache.pruned_count);
|
||||
ASSERT_EQ (0, node1->ledger.pruned_count ());
|
||||
|
||||
ASSERT_TRUE (nano::test::start_elections (system, *node1, { send3 }, true));
|
||||
ASSERT_TIMELY (5s, nano::test::confirmed (*node1, { send3 }));
|
||||
|
||||
node1->ledger_pruning (2, false);
|
||||
ASSERT_EQ (2, node1->ledger.cache.pruned_count);
|
||||
ASSERT_EQ (4, node1->ledger.cache.block_count);
|
||||
ASSERT_EQ (2, node1->ledger.pruned_count ());
|
||||
ASSERT_EQ (4, node1->ledger.block_count ());
|
||||
ASSERT_TRUE (node1->ledger.store.pruned.exists (node1->ledger.store.tx_begin_read (), send1->hash ()));
|
||||
ASSERT_FALSE (nano::test::exists (*node1, { send1 }));
|
||||
ASSERT_TRUE (node1->ledger.store.pruned.exists (node1->ledger.store.tx_begin_read (), send2->hash ()));
|
||||
|
|
@ -2099,7 +2078,7 @@ TEST (bulk, genesis_pruning)
|
|||
ASSERT_TIMELY (5s, !node2->bootstrap_initiator.in_progress ());
|
||||
|
||||
// node2 still missing blocks
|
||||
ASSERT_EQ (1, node2->ledger.cache.block_count);
|
||||
ASSERT_EQ (1, node2->ledger.block_count ());
|
||||
{
|
||||
auto transaction (node2->store.tx_begin_write ());
|
||||
node2->unchecked.clear ();
|
||||
|
|
@ -2108,13 +2087,12 @@ TEST (bulk, genesis_pruning)
|
|||
// Insert pruned blocks
|
||||
node2->process_active (send1);
|
||||
node2->process_active (send2);
|
||||
ASSERT_TIMELY_EQ (5s, 3, node2->ledger.cache.block_count);
|
||||
ASSERT_TIMELY_EQ (5s, 3, node2->ledger.block_count ());
|
||||
|
||||
// New bootstrap to sync up everything
|
||||
ASSERT_TIMELY_EQ (5s, node2->bootstrap_initiator.connections->connections_count, 0);
|
||||
node2->bootstrap_initiator.bootstrap (node1->network.endpoint (), false);
|
||||
ASSERT_TIMELY_EQ (5s, node2->latest (nano::dev::genesis_key.pub), node1->latest (nano::dev::genesis_key.pub));
|
||||
node2->stop ();
|
||||
}
|
||||
|
||||
TEST (bulk_pull_account, basics)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <nano/lib/tomlconfig.hpp>
|
||||
#include <nano/node/bootstrap_ascending/service.hpp>
|
||||
#include <nano/node/make_store.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/test_common/system.hpp>
|
||||
#include <nano/test_common/testutil.hpp>
|
||||
|
||||
|
|
@ -251,7 +252,7 @@ TEST (bootstrap_ascending, trace_base)
|
|||
// std::cerr << "--------------- Start ---------------\n";
|
||||
ASSERT_EQ (nano::block_status::progress, node0.process (send1));
|
||||
ASSERT_EQ (nano::block_status::progress, node0.process (receive1));
|
||||
ASSERT_EQ (node1.store.pending.begin (node1.store.tx_begin_read (), nano::pending_key{ key.pub, 0 }), node1.store.pending.end ());
|
||||
ASSERT_EQ (node1.ledger.receivable_end (), node1.ledger.receivable_upper_bound (node1.store.tx_begin_read (), key.pub, 0));
|
||||
// std::cerr << "node0: " << node0.network.endpoint () << std::endl;
|
||||
// std::cerr << "node1: " << node1.network.endpoint () << std::endl;
|
||||
ASSERT_TIMELY (10s, node1.block (receive1->hash ()) != nullptr);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,8 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/confirmation_solicitor.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/transport/inproc.hpp>
|
||||
#include <nano/test_common/network.hpp>
|
||||
#include <nano/test_common/system.hpp>
|
||||
|
|
|
|||
280
nano/core_test/confirming_set.cpp
Normal file
280
nano/core_test/confirming_set.cpp
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/lib/logging.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/confirming_set.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/make_store.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/test_common/ledger.hpp>
|
||||
#include <nano/test_common/system.hpp>
|
||||
#include <nano/test_common/testutil.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <latch>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
TEST (confirming_set, construction)
|
||||
{
|
||||
auto ctx = nano::test::context::ledger_empty ();
|
||||
nano::write_database_queue write_queue{ false };
|
||||
nano::confirming_set confirming_set (ctx.ledger (), write_queue);
|
||||
}
|
||||
|
||||
TEST (confirming_set, add_exists)
|
||||
{
|
||||
auto ctx = nano::test::context::ledger_send_receive ();
|
||||
nano::write_database_queue write_queue{ false };
|
||||
nano::confirming_set confirming_set (ctx.ledger (), write_queue);
|
||||
auto send = ctx.blocks ()[0];
|
||||
confirming_set.add (send->hash ());
|
||||
ASSERT_TRUE (confirming_set.exists (send->hash ()));
|
||||
}
|
||||
|
||||
TEST (confirming_set, process_one)
|
||||
{
|
||||
auto ctx = nano::test::context::ledger_send_receive ();
|
||||
nano::write_database_queue write_queue{ false };
|
||||
nano::confirming_set confirming_set (ctx.ledger (), write_queue);
|
||||
std::atomic<int> count = 0;
|
||||
std::mutex mutex;
|
||||
std::condition_variable condition;
|
||||
confirming_set.cemented_observers.add ([&] (auto const &) { ++count; condition.notify_all (); });
|
||||
confirming_set.add (ctx.blocks ()[0]->hash ());
|
||||
nano::test::start_stop_guard guard{ confirming_set };
|
||||
std::unique_lock lock{ mutex };
|
||||
ASSERT_TRUE (condition.wait_for (lock, 5s, [&] () { return count == 1; }));
|
||||
ASSERT_EQ (1, ctx.stats ().count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (2, ctx.ledger ().cemented_count ());
|
||||
}
|
||||
|
||||
TEST (confirming_set, process_multiple)
|
||||
{
|
||||
auto ctx = nano::test::context::ledger_send_receive ();
|
||||
nano::write_database_queue write_queue{ false };
|
||||
nano::confirming_set confirming_set (ctx.ledger (), write_queue);
|
||||
std::atomic<int> count = 0;
|
||||
std::mutex mutex;
|
||||
std::condition_variable condition;
|
||||
confirming_set.cemented_observers.add ([&] (auto const &) { ++count; condition.notify_all (); });
|
||||
confirming_set.add (ctx.blocks ()[0]->hash ());
|
||||
confirming_set.add (ctx.blocks ()[1]->hash ());
|
||||
nano::test::start_stop_guard guard{ confirming_set };
|
||||
std::unique_lock lock{ mutex };
|
||||
ASSERT_TRUE (condition.wait_for (lock, 5s, [&] () { return count == 2; }));
|
||||
ASSERT_EQ (2, ctx.stats ().count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (3, ctx.ledger ().cemented_count ());
|
||||
}
|
||||
|
||||
TEST (confirmation_callback, observer_callbacks)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::node_flags node_flags;
|
||||
nano::node_config node_config = system.default_config ();
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config, node_flags);
|
||||
|
||||
system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv);
|
||||
nano::block_hash latest (node->latest (nano::dev::genesis_key.pub));
|
||||
|
||||
nano::keypair key1;
|
||||
nano::block_builder builder;
|
||||
auto send = builder
|
||||
.send ()
|
||||
.previous (latest)
|
||||
.destination (key1.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (latest))
|
||||
.build ();
|
||||
auto send1 = builder
|
||||
.send ()
|
||||
.previous (send->hash ())
|
||||
.destination (key1.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (send->hash ()))
|
||||
.build ();
|
||||
|
||||
{
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1));
|
||||
}
|
||||
|
||||
node->confirming_set.add (send1->hash ());
|
||||
|
||||
// Callback is performed for all blocks that are confirmed
|
||||
ASSERT_TIMELY_EQ (5s, 2, node->ledger.stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out));
|
||||
|
||||
ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (3, node->ledger.cemented_count ());
|
||||
ASSERT_EQ (0, node->active.election_winner_details_size ());
|
||||
}
|
||||
|
||||
// The callback and confirmation history should only be updated after confirmation height is set (and not just after voting)
|
||||
TEST (confirmation_callback, confirmed_history)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::node_flags node_flags;
|
||||
node_flags.force_use_write_database_queue = true;
|
||||
node_flags.disable_ascending_bootstrap = true;
|
||||
nano::node_config node_config = system.default_config ();
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config, node_flags);
|
||||
|
||||
nano::block_hash latest (node->latest (nano::dev::genesis_key.pub));
|
||||
|
||||
nano::keypair key1;
|
||||
nano::block_builder builder;
|
||||
auto send = builder
|
||||
.send ()
|
||||
.previous (latest)
|
||||
.destination (key1.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (latest))
|
||||
.build ();
|
||||
{
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send));
|
||||
}
|
||||
|
||||
auto send1 = builder
|
||||
.send ()
|
||||
.previous (send->hash ())
|
||||
.destination (key1.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (send->hash ()))
|
||||
.build ();
|
||||
|
||||
node->process_active (send1);
|
||||
std::shared_ptr<nano::election> election;
|
||||
ASSERT_TIMELY (5s, election = nano::test::start_election (system, *node, send1->hash ()));
|
||||
{
|
||||
// The write guard prevents the confirmation height processor doing any writes
|
||||
auto write_guard = node->write_database_queue.wait (nano::writer::testing);
|
||||
|
||||
// Confirm send1
|
||||
election->force_confirm ();
|
||||
ASSERT_TIMELY_EQ (10s, node->active.size (), 0);
|
||||
ASSERT_EQ (0, node->active.recently_cemented.list ().size ());
|
||||
ASSERT_TRUE (node->active.empty ());
|
||||
|
||||
auto transaction = node->store.tx_begin_read ();
|
||||
ASSERT_FALSE (node->ledger.block_confirmed (transaction, send->hash ()));
|
||||
|
||||
ASSERT_TIMELY (10s, node->write_database_queue.contains (nano::writer::confirmation_height));
|
||||
|
||||
// Confirm that no inactive callbacks have been called when the confirmation height processor has already iterated over it, waiting to write
|
||||
ASSERT_EQ (0, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out));
|
||||
}
|
||||
|
||||
ASSERT_TIMELY (10s, !node->write_database_queue.contains (nano::writer::confirmation_height));
|
||||
|
||||
auto transaction = node->store.tx_begin_read ();
|
||||
ASSERT_TRUE (node->ledger.block_confirmed (transaction, send->hash ()));
|
||||
|
||||
ASSERT_TIMELY_EQ (10s, node->active.size (), 0);
|
||||
ASSERT_TIMELY_EQ (10s, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out), 1);
|
||||
|
||||
// Each block that's confirmed is in the recently_cemented history
|
||||
ASSERT_EQ (2, node->active.recently_cemented.list ().size ());
|
||||
ASSERT_TRUE (node->active.empty ());
|
||||
|
||||
// Confirm the callback is not called under this circumstance
|
||||
ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out));
|
||||
ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out));
|
||||
ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (3, node->ledger.cemented_count ());
|
||||
ASSERT_EQ (0, node->active.election_winner_details_size ());
|
||||
}
|
||||
|
||||
TEST (confirmation_callback, dependent_election)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::node_flags node_flags;
|
||||
node_flags.force_use_write_database_queue = true;
|
||||
nano::node_config node_config = system.default_config ();
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config, node_flags);
|
||||
|
||||
nano::block_hash latest (node->latest (nano::dev::genesis_key.pub));
|
||||
|
||||
nano::keypair key1;
|
||||
nano::block_builder builder;
|
||||
auto send = builder
|
||||
.send ()
|
||||
.previous (latest)
|
||||
.destination (key1.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (latest))
|
||||
.build ();
|
||||
auto send1 = builder
|
||||
.send ()
|
||||
.previous (send->hash ())
|
||||
.destination (key1.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (send->hash ()))
|
||||
.build ();
|
||||
auto send2 = builder
|
||||
.send ()
|
||||
.previous (send1->hash ())
|
||||
.destination (key1.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 3)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (send1->hash ()))
|
||||
.build ();
|
||||
{
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2));
|
||||
}
|
||||
|
||||
// This election should be confirmed as active_conf_height
|
||||
ASSERT_TRUE (nano::test::start_election (system, *node, send1->hash ()));
|
||||
// Start an election and confirm it
|
||||
auto election = nano::test::start_election (system, *node, send2->hash ());
|
||||
ASSERT_NE (nullptr, election);
|
||||
election->force_confirm ();
|
||||
|
||||
// Wait for blocks to be confirmed in ledger, callbacks will happen after
|
||||
ASSERT_TIMELY_EQ (5s, 3, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
// Once the item added to the confirming set no longer exists, callbacks have completed
|
||||
ASSERT_TIMELY (5s, !node->confirming_set.exists (send2->hash ()));
|
||||
|
||||
ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out));
|
||||
ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_conf_height, nano::stat::dir::out));
|
||||
ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out));
|
||||
ASSERT_EQ (4, node->ledger.cemented_count ());
|
||||
|
||||
ASSERT_EQ (0, node->active.election_winner_details_size ());
|
||||
}
|
||||
|
||||
TEST (confirmation_callback, election_winner_details_clearing_node_process_confirmed)
|
||||
{
|
||||
// Make sure election_winner_details is also cleared if the block never enters the confirmation height processor from node::process_confirmed
|
||||
nano::test::system system (1);
|
||||
auto node = system.nodes.front ();
|
||||
|
||||
nano::block_builder builder;
|
||||
auto send = builder
|
||||
.send ()
|
||||
.previous (nano::dev::genesis->hash ())
|
||||
.destination (nano::dev::genesis_key.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (nano::dev::genesis->hash ()))
|
||||
.build ();
|
||||
// Add to election_winner_details. Use an unrealistic iteration so that it should fall into the else case and do a cleanup
|
||||
node->active.add_election_winner_details (send->hash (), nullptr);
|
||||
nano::election_status election;
|
||||
election.winner = send;
|
||||
node->process_confirmed (election, 1000000);
|
||||
ASSERT_EQ (0, node->active.election_winner_details_size ());
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/scheduler/component.hpp>
|
||||
#include <nano/node/scheduler/priority.hpp>
|
||||
|
|
@ -98,7 +99,7 @@ TEST (conflicts, add_two)
|
|||
// create 2 new accounts, that receive 1 raw each, all blocks are force confirmed
|
||||
auto [send1, open1] = nano::test::setup_new_account (system, *node, 1, gk, key1, gk.pub, true);
|
||||
auto [send2, open2] = nano::test::setup_new_account (system, *node, 1, gk, key2, gk.pub, true);
|
||||
ASSERT_EQ (5, node->ledger.cache.cemented_count);
|
||||
ASSERT_EQ (5, node->ledger.cemented_count ());
|
||||
|
||||
// send 1 raw to account key3 from key1
|
||||
auto send_a = nano::state_block_builder ()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/scheduler/component.hpp>
|
||||
#include <nano/node/scheduler/priority.hpp>
|
||||
|
|
@ -71,7 +72,7 @@ TEST (election, quorum_minimum_flip_success)
|
|||
ASSERT_TIMELY_EQ (5s, election->blocks ().size (), 2);
|
||||
|
||||
auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send2->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote));
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote).at (send2->hash ()));
|
||||
|
||||
ASSERT_TIMELY (5s, election->confirmed ());
|
||||
auto const winner = election->winner ();
|
||||
|
|
@ -120,7 +121,7 @@ TEST (election, quorum_minimum_flip_fail)
|
|||
|
||||
// genesis generates a final vote for send2 but it should not be enough to reach quorum due to the online_weight_minimum being so high
|
||||
auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send2->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote));
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote).at (send2->hash ()));
|
||||
|
||||
// give the election some time before asserting it is not confirmed so that in case
|
||||
// it would be wrongfully confirmed, have that immediately fail instead of race
|
||||
|
|
@ -156,7 +157,7 @@ TEST (election, quorum_minimum_confirm_success)
|
|||
ASSERT_NE (nullptr, election);
|
||||
ASSERT_EQ (1, election->blocks ().size ());
|
||||
auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send1->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote));
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote).at (send1->hash ()));
|
||||
ASSERT_NE (nullptr, node1.block (send1->hash ()));
|
||||
ASSERT_TIMELY (5s, election->confirmed ());
|
||||
}
|
||||
|
|
@ -187,7 +188,7 @@ TEST (election, quorum_minimum_confirm_fail)
|
|||
ASSERT_EQ (1, election->blocks ().size ());
|
||||
|
||||
auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send1->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote));
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote).at (send1->hash ()));
|
||||
|
||||
// give the election a chance to confirm
|
||||
WAIT (1s);
|
||||
|
|
@ -237,20 +238,20 @@ TEST (election, quorum_minimum_update_weight_before_quorum_checks)
|
|||
.work (*system.work.generate (open1->hash ()))
|
||||
.build ();
|
||||
ASSERT_EQ (nano::block_status::progress, node1.process (send2));
|
||||
ASSERT_TIMELY_EQ (5s, node1.ledger.cache.block_count, 4);
|
||||
ASSERT_TIMELY_EQ (5s, node1.ledger.block_count (), 4);
|
||||
|
||||
node_config.peering_port = system.get_available_port ();
|
||||
auto & node2 = *system.add_node (node_config);
|
||||
|
||||
system.wallet (1)->insert_adhoc (key1.prv);
|
||||
ASSERT_TIMELY_EQ (10s, node2.ledger.cache.block_count, 4);
|
||||
ASSERT_TIMELY_EQ (10s, node2.ledger.block_count (), 4);
|
||||
|
||||
std::shared_ptr<nano::election> election;
|
||||
ASSERT_TIMELY (5s, (election = node1.active.election (send1->qualified_root ())) != nullptr);
|
||||
ASSERT_EQ (1, election->blocks ().size ());
|
||||
|
||||
auto vote1 = nano::test::make_final_vote (nano::dev::genesis_key, { send1->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1).at (send1->hash ()));
|
||||
|
||||
auto channel = node1.network.find_node_id (node2.get_node_id ());
|
||||
ASSERT_NE (channel, nullptr);
|
||||
|
|
@ -264,7 +265,7 @@ TEST (election, quorum_minimum_update_weight_before_quorum_checks)
|
|||
// Modify online_m for online_reps to more than is available, this checks that voting below updates it to current online reps.
|
||||
node1.online_reps.online_m = node_config.online_weight_minimum.number () + 20;
|
||||
}
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2));
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2).at (send1->hash ()));
|
||||
ASSERT_TIMELY (5s, election->confirmed ());
|
||||
ASSERT_NE (nullptr, node1.block (send1->hash ()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/scheduler/component.hpp>
|
||||
#include <nano/node/scheduler/priority.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
887
nano/core_test/ledger_confirm.cpp
Normal file
887
nano/core_test/ledger_confirm.cpp
Normal file
|
|
@ -0,0 +1,887 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/lib/logging.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/make_store.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/test_common/system.hpp>
|
||||
#include <nano/test_common/testutil.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
TEST (ledger_confirm, single)
|
||||
{
|
||||
auto amount (std::numeric_limits<nano::uint128_t>::max ());
|
||||
nano::test::system system;
|
||||
nano::node_flags node_flags;
|
||||
auto node = system.add_node (node_flags);
|
||||
nano::keypair key1;
|
||||
system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv);
|
||||
nano::block_hash latest1 (node->latest (nano::dev::genesis_key.pub));
|
||||
nano::block_builder builder;
|
||||
auto send1 = builder
|
||||
.state ()
|
||||
.account (nano::dev::genesis_key.pub)
|
||||
.previous (latest1)
|
||||
.representative (nano::dev::genesis_key.pub)
|
||||
.balance (amount - 100)
|
||||
.link (key1.pub)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (latest1))
|
||||
.build ();
|
||||
|
||||
// Check confirmation heights before, should be uninitialized (1 for genesis).
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (1, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height);
|
||||
ASSERT_EQ (nano::dev::genesis->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier);
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1));
|
||||
ASSERT_FALSE (node->ledger.block_confirmed (transaction, send1->hash ()));
|
||||
node->ledger.confirm (transaction, send1->hash ());
|
||||
ASSERT_TRUE (node->ledger.block_confirmed (transaction, send1->hash ()));
|
||||
ASSERT_EQ (2, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height);
|
||||
ASSERT_EQ (send1->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier);
|
||||
|
||||
// Rollbacks should fail as these blocks have been cemented
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, latest1));
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, send1->hash ()));
|
||||
ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (2, node->ledger.cemented_count ());
|
||||
}
|
||||
|
||||
TEST (ledger_confirm, multiple_accounts)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::node_flags node_flags;
|
||||
nano::node_config node_config = system.default_config ();
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config, node_flags);
|
||||
nano::keypair key1;
|
||||
nano::keypair key2;
|
||||
nano::keypair key3;
|
||||
nano::block_hash latest1 (system.nodes[0]->latest (nano::dev::genesis_key.pub));
|
||||
nano::block_builder builder;
|
||||
|
||||
// Send to all accounts
|
||||
auto send1 = builder
|
||||
.send ()
|
||||
.previous (latest1)
|
||||
.destination (key1.pub)
|
||||
.balance (node->online_reps.delta () + 300)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (latest1))
|
||||
.build ();
|
||||
auto send2 = builder
|
||||
.send ()
|
||||
.previous (send1->hash ())
|
||||
.destination (key2.pub)
|
||||
.balance (node->online_reps.delta () + 200)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (send1->hash ()))
|
||||
.build ();
|
||||
auto send3 = builder
|
||||
.send ()
|
||||
.previous (send2->hash ())
|
||||
.destination (key3.pub)
|
||||
.balance (node->online_reps.delta () + 100)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (send2->hash ()))
|
||||
.build ();
|
||||
|
||||
// Open all accounts
|
||||
auto open1 = builder
|
||||
.open ()
|
||||
.source (send1->hash ())
|
||||
.representative (nano::dev::genesis_key.pub)
|
||||
.account (key1.pub)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (key1.pub))
|
||||
.build ();
|
||||
auto open2 = builder
|
||||
.open ()
|
||||
.source (send2->hash ())
|
||||
.representative (nano::dev::genesis_key.pub)
|
||||
.account (key2.pub)
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*system.work.generate (key2.pub))
|
||||
.build ();
|
||||
auto open3 = builder
|
||||
.open ()
|
||||
.source (send3->hash ())
|
||||
.representative (nano::dev::genesis_key.pub)
|
||||
.account (key3.pub)
|
||||
.sign (key3.prv, key3.pub)
|
||||
.work (*system.work.generate (key3.pub))
|
||||
.build ();
|
||||
|
||||
// Send and receive various blocks to these accounts
|
||||
auto send4 = builder
|
||||
.send ()
|
||||
.previous (open1->hash ())
|
||||
.destination (key2.pub)
|
||||
.balance (50)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (open1->hash ()))
|
||||
.build ();
|
||||
auto send5 = builder
|
||||
.send ()
|
||||
.previous (send4->hash ())
|
||||
.destination (key2.pub)
|
||||
.balance (10)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (send4->hash ()))
|
||||
.build ();
|
||||
|
||||
auto receive1 = builder
|
||||
.receive ()
|
||||
.previous (open2->hash ())
|
||||
.source (send4->hash ())
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*system.work.generate (open2->hash ()))
|
||||
.build ();
|
||||
auto send6 = builder
|
||||
.send ()
|
||||
.previous (receive1->hash ())
|
||||
.destination (key3.pub)
|
||||
.balance (10)
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*system.work.generate (receive1->hash ()))
|
||||
.build ();
|
||||
auto receive2 = builder
|
||||
.receive ()
|
||||
.previous (send6->hash ())
|
||||
.source (send5->hash ())
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*system.work.generate (send6->hash ()))
|
||||
.build ();
|
||||
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send3));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open1));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open2));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open3));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send4));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send5));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive1));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send6));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive2));
|
||||
|
||||
// Check confirmation heights of all the accounts (except genesis) are uninitialized (0),
|
||||
// as we have any just added them to the ledger and not processed any live transactions yet.
|
||||
ASSERT_EQ (1, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height);
|
||||
ASSERT_EQ (nano::dev::genesis->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier);
|
||||
ASSERT_FALSE (node->store.confirmation_height.get (transaction, key1.pub));
|
||||
ASSERT_FALSE (node->store.confirmation_height.get (transaction, key2.pub));
|
||||
ASSERT_FALSE (node->store.confirmation_height.get (transaction, key3.pub));
|
||||
|
||||
// The nodes process a live receive which propagates across to all accounts
|
||||
auto receive3 = builder
|
||||
.receive ()
|
||||
.previous (open3->hash ())
|
||||
.source (send6->hash ())
|
||||
.sign (key3.prv, key3.pub)
|
||||
.work (*system.work.generate (open3->hash ()))
|
||||
.build ();
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive3));
|
||||
auto confirmed = node->ledger.confirm (transaction, receive3->hash ());
|
||||
ASSERT_EQ (10, confirmed.size ());
|
||||
ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (11, node->ledger.cemented_count ());
|
||||
|
||||
ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive3->hash ()));
|
||||
ASSERT_EQ (4, node->ledger.account_info (transaction, nano::dev::genesis_key.pub).value ().block_count);
|
||||
ASSERT_EQ (4, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height);
|
||||
ASSERT_EQ (send3->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier);
|
||||
ASSERT_EQ (3, node->ledger.account_info (transaction, key1.pub).value ().block_count);
|
||||
ASSERT_EQ (2, node->store.confirmation_height.get (transaction, key1.pub).value ().height);
|
||||
ASSERT_EQ (send4->hash (), node->store.confirmation_height.get (transaction, key1.pub).value ().frontier);
|
||||
ASSERT_EQ (4, node->ledger.account_info (transaction, key2.pub).value ().block_count);
|
||||
ASSERT_EQ (3, node->store.confirmation_height.get (transaction, key2.pub).value ().height);
|
||||
ASSERT_EQ (send6->hash (), node->store.confirmation_height.get (transaction, key2.pub).value ().frontier);
|
||||
ASSERT_EQ (2, node->ledger.account_info (transaction, key3.pub).value ().block_count);
|
||||
ASSERT_EQ (2, node->store.confirmation_height.get (transaction, key3.pub).value ().height);
|
||||
ASSERT_EQ (receive3->hash (), node->store.confirmation_height.get (transaction, key3.pub).value ().frontier);
|
||||
|
||||
// The accounts for key1 and key2 have 1 more block in the chain than is confirmed.
|
||||
// So this can be rolled back, but the one before that cannot. Check that this is the case
|
||||
ASSERT_FALSE (node->ledger.rollback (transaction, node->ledger.latest (transaction, key2.pub)));
|
||||
ASSERT_FALSE (node->ledger.rollback (transaction, node->ledger.latest (transaction, key1.pub)));
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, node->ledger.latest (transaction, key1.pub)));
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, node->ledger.latest (transaction, key2.pub)));
|
||||
|
||||
// Confirm the other latest can't be rolled back either
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, node->ledger.latest (transaction, key3.pub)));
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, node->ledger.latest (transaction, nano::dev::genesis_key.pub)));
|
||||
|
||||
// Attempt some others which have been cemented
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, open1->hash ()));
|
||||
ASSERT_TRUE (node->ledger.rollback (transaction, send2->hash ()));
|
||||
}
|
||||
|
||||
TEST (ledger_confirm, send_receive_between_2_accounts)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::node_flags node_flags;
|
||||
nano::node_config node_config = system.default_config ();
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config, node_flags);
|
||||
nano::keypair key1;
|
||||
nano::block_hash latest (node->latest (nano::dev::genesis_key.pub));
|
||||
|
||||
nano::block_builder builder;
|
||||
auto send1 = builder
|
||||
.send ()
|
||||
.previous (latest)
|
||||
.destination (key1.pub)
|
||||
.balance (node->online_reps.delta () + 2)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (latest))
|
||||
.build ();
|
||||
auto open1 = builder
|
||||
.open ()
|
||||
.source (send1->hash ())
|
||||
.representative (nano::dev::genesis_key.pub)
|
||||
.account (key1.pub)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (key1.pub))
|
||||
.build ();
|
||||
auto send2 = builder
|
||||
.send ()
|
||||
.previous (open1->hash ())
|
||||
.destination (nano::dev::genesis_key.pub)
|
||||
.balance (1000)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (open1->hash ()))
|
||||
.build ();
|
||||
auto send3 = builder
|
||||
.send ()
|
||||
.previous (send2->hash ())
|
||||
.destination (nano::dev::genesis_key.pub)
|
||||
.balance (900)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (send2->hash ()))
|
||||
.build ();
|
||||
auto send4 = builder
|
||||
.send ()
|
||||
.previous (send3->hash ())
|
||||
.destination (nano::dev::genesis_key.pub)
|
||||
.balance (500)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (send3->hash ()))
|
||||
.build ();
|
||||
auto receive1 = builder
|
||||
.receive ()
|
||||
.previous (send1->hash ())
|
||||
.source (send2->hash ())
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (send1->hash ()))
|
||||
.build ();
|
||||
auto receive2 = builder
|
||||
.receive ()
|
||||
.previous (receive1->hash ())
|
||||
.source (send3->hash ())
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (receive1->hash ()))
|
||||
.build ();
|
||||
auto receive3 = builder
|
||||
.receive ()
|
||||
.previous (receive2->hash ())
|
||||
.source (send4->hash ())
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (receive2->hash ()))
|
||||
.build ();
|
||||
auto send5 = builder
|
||||
.send ()
|
||||
.previous (receive3->hash ())
|
||||
.destination (key1.pub)
|
||||
.balance (node->online_reps.delta () + 1)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (receive3->hash ()))
|
||||
.build ();
|
||||
auto receive4 = builder
|
||||
.receive ()
|
||||
.previous (send4->hash ())
|
||||
.source (send5->hash ())
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (send4->hash ()))
|
||||
.build ();
|
||||
nano::keypair key2;
|
||||
auto send6 = builder
|
||||
.send ()
|
||||
.previous (send5->hash ())
|
||||
.destination (key2.pub)
|
||||
.balance (node->online_reps.delta () + 1)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (send5->hash ()))
|
||||
.build ();
|
||||
// Unpocketed send
|
||||
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open1));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive1));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send3));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send4));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive2));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive3));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send5));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send6));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive4));
|
||||
auto confirmed = node->ledger.confirm (transaction, receive4->hash ());
|
||||
ASSERT_EQ (10, confirmed.size ());
|
||||
ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (11, node->ledger.cemented_count ());
|
||||
|
||||
ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive4->hash ()));
|
||||
ASSERT_EQ (7, node->ledger.account_info (transaction, nano::dev::genesis_key.pub).value ().block_count);
|
||||
ASSERT_EQ (6, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height);
|
||||
ASSERT_EQ (send5->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier);
|
||||
|
||||
ASSERT_EQ (5, node->ledger.account_info (transaction, key1.pub).value ().block_count);
|
||||
ASSERT_EQ (5, node->store.confirmation_height.get (transaction, key1.pub).value ().height);
|
||||
ASSERT_EQ (receive4->hash (), node->store.confirmation_height.get (transaction, key1.pub).value ().frontier);
|
||||
}
|
||||
|
||||
TEST (ledger_confirm, send_receive_self)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::node_flags node_flags;
|
||||
nano::node_config node_config = system.default_config ();
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config, node_flags);
|
||||
nano::block_hash latest (node->latest (nano::dev::genesis_key.pub));
|
||||
|
||||
nano::block_builder builder;
|
||||
auto send1 = builder
|
||||
.send ()
|
||||
.previous (latest)
|
||||
.destination (nano::dev::genesis_key.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - 2)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (latest))
|
||||
.build ();
|
||||
auto receive1 = builder
|
||||
.receive ()
|
||||
.previous (send1->hash ())
|
||||
.source (send1->hash ())
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (send1->hash ()))
|
||||
.build ();
|
||||
auto send2 = builder
|
||||
.send ()
|
||||
.previous (receive1->hash ())
|
||||
.destination (nano::dev::genesis_key.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - 2)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (receive1->hash ()))
|
||||
.build ();
|
||||
auto send3 = builder
|
||||
.send ()
|
||||
.previous (send2->hash ())
|
||||
.destination (nano::dev::genesis_key.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - 3)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (send2->hash ()))
|
||||
.build ();
|
||||
auto receive2 = builder
|
||||
.receive ()
|
||||
.previous (send3->hash ())
|
||||
.source (send2->hash ())
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (send3->hash ()))
|
||||
.build ();
|
||||
auto receive3 = builder
|
||||
.receive ()
|
||||
.previous (receive2->hash ())
|
||||
.source (send3->hash ())
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (receive2->hash ()))
|
||||
.build ();
|
||||
|
||||
// Send to another account to prevent automatic receiving on the genesis account
|
||||
nano::keypair key1;
|
||||
auto send4 = builder
|
||||
.send ()
|
||||
.previous (receive3->hash ())
|
||||
.destination (key1.pub)
|
||||
.balance (node->online_reps.delta ())
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (receive3->hash ()))
|
||||
.build ();
|
||||
|
||||
auto transaction = node->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive1));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send3));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive2));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive3));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send4));
|
||||
|
||||
auto confirmed = node->ledger.confirm (transaction, receive3->hash ());
|
||||
ASSERT_EQ (6, confirmed.size ());
|
||||
ASSERT_EQ (6, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
|
||||
ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive3->hash ()));
|
||||
ASSERT_EQ (8, node->ledger.account_info (transaction, nano::dev::genesis_key.pub).value ().block_count);
|
||||
ASSERT_EQ (7, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height);
|
||||
ASSERT_EQ (receive3->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier);
|
||||
ASSERT_EQ (7, node->ledger.cemented_count ());
|
||||
}
|
||||
|
||||
TEST (ledger_confirm, all_block_types)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::node_flags node_flags;
|
||||
nano::node_config node_config = system.default_config ();
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node = system.add_node (node_config, node_flags);
|
||||
nano::block_hash latest (node->latest (nano::dev::genesis_key.pub));
|
||||
nano::keypair key1;
|
||||
nano::keypair key2;
|
||||
auto & store = node->store;
|
||||
nano::block_builder builder;
|
||||
auto send = builder
|
||||
.send ()
|
||||
.previous (latest)
|
||||
.destination (key1.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (latest))
|
||||
.build ();
|
||||
auto send1 = builder
|
||||
.send ()
|
||||
.previous (send->hash ())
|
||||
.destination (key2.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (send->hash ()))
|
||||
.build ();
|
||||
|
||||
auto open = builder
|
||||
.open ()
|
||||
.source (send->hash ())
|
||||
.representative (nano::dev::genesis_key.pub)
|
||||
.account (key1.pub)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (key1.pub))
|
||||
.build ();
|
||||
auto state_open = builder
|
||||
.state ()
|
||||
.account (key2.pub)
|
||||
.previous (0)
|
||||
.representative (0)
|
||||
.balance (nano::Gxrb_ratio)
|
||||
.link (send1->hash ())
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*system.work.generate (key2.pub))
|
||||
.build ();
|
||||
|
||||
auto send2 = builder
|
||||
.send ()
|
||||
.previous (open->hash ())
|
||||
.destination (key2.pub)
|
||||
.balance (0)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (open->hash ()))
|
||||
.build ();
|
||||
auto state_receive = builder
|
||||
.state ()
|
||||
.account (key2.pub)
|
||||
.previous (state_open->hash ())
|
||||
.representative (0)
|
||||
.balance (nano::Gxrb_ratio * 2)
|
||||
.link (send2->hash ())
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*system.work.generate (state_open->hash ()))
|
||||
.build ();
|
||||
|
||||
auto state_send = builder
|
||||
.state ()
|
||||
.account (key2.pub)
|
||||
.previous (state_receive->hash ())
|
||||
.representative (0)
|
||||
.balance (nano::Gxrb_ratio)
|
||||
.link (key1.pub)
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*system.work.generate (state_receive->hash ()))
|
||||
.build ();
|
||||
auto receive = builder
|
||||
.receive ()
|
||||
.previous (send2->hash ())
|
||||
.source (state_send->hash ())
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (send2->hash ()))
|
||||
.build ();
|
||||
|
||||
auto change = builder
|
||||
.change ()
|
||||
.previous (receive->hash ())
|
||||
.representative (key2.pub)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (receive->hash ()))
|
||||
.build ();
|
||||
|
||||
auto state_change = builder
|
||||
.state ()
|
||||
.account (key2.pub)
|
||||
.previous (state_send->hash ())
|
||||
.representative (nano::dev::genesis_key.pub)
|
||||
.balance (nano::Gxrb_ratio)
|
||||
.link (0)
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*system.work.generate (state_send->hash ()))
|
||||
.build ();
|
||||
|
||||
auto epoch = builder
|
||||
.state ()
|
||||
.account (key2.pub)
|
||||
.previous (state_change->hash ())
|
||||
.representative (nano::dev::genesis_key.pub)
|
||||
.balance (nano::Gxrb_ratio)
|
||||
.link (node->ledger.epoch_link (nano::epoch::epoch_1))
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (state_change->hash ()))
|
||||
.build ();
|
||||
|
||||
auto epoch1 = builder
|
||||
.state ()
|
||||
.account (key1.pub)
|
||||
.previous (change->hash ())
|
||||
.representative (key2.pub)
|
||||
.balance (nano::Gxrb_ratio)
|
||||
.link (node->ledger.epoch_link (nano::epoch::epoch_1))
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (change->hash ()))
|
||||
.build ();
|
||||
auto state_send1 = builder
|
||||
.state ()
|
||||
.account (key1.pub)
|
||||
.previous (epoch1->hash ())
|
||||
.representative (0)
|
||||
.balance (nano::Gxrb_ratio - 1)
|
||||
.link (key2.pub)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (epoch1->hash ()))
|
||||
.build ();
|
||||
auto state_receive2 = builder
|
||||
.state ()
|
||||
.account (key2.pub)
|
||||
.previous (epoch->hash ())
|
||||
.representative (0)
|
||||
.balance (nano::Gxrb_ratio + 1)
|
||||
.link (state_send1->hash ())
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*system.work.generate (epoch->hash ()))
|
||||
.build ();
|
||||
|
||||
auto state_send2 = builder
|
||||
.state ()
|
||||
.account (key2.pub)
|
||||
.previous (state_receive2->hash ())
|
||||
.representative (0)
|
||||
.balance (nano::Gxrb_ratio)
|
||||
.link (key1.pub)
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*system.work.generate (state_receive2->hash ()))
|
||||
.build ();
|
||||
auto state_send3 = builder
|
||||
.state ()
|
||||
.account (key2.pub)
|
||||
.previous (state_send2->hash ())
|
||||
.representative (0)
|
||||
.balance (nano::Gxrb_ratio - 1)
|
||||
.link (key1.pub)
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*system.work.generate (state_send2->hash ()))
|
||||
.build ();
|
||||
|
||||
auto state_send4 = builder
|
||||
.state ()
|
||||
.account (key1.pub)
|
||||
.previous (state_send1->hash ())
|
||||
.representative (0)
|
||||
.balance (nano::Gxrb_ratio - 2)
|
||||
.link (nano::dev::genesis_key.pub)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (state_send1->hash ()))
|
||||
.build ();
|
||||
auto state_receive3 = builder
|
||||
.state ()
|
||||
.account (nano::dev::genesis_key.pub)
|
||||
.previous (send1->hash ())
|
||||
.representative (nano::dev::genesis_key.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2 + 1)
|
||||
.link (state_send4->hash ())
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (send1->hash ()))
|
||||
.build ();
|
||||
|
||||
auto transaction (store.tx_begin_write ());
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_open));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_receive));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, change));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_change));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, epoch));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, epoch1));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send1));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_receive2));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send2));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send3));
|
||||
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send4));
|
||||
ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_receive3));
|
||||
|
||||
auto confirmed = node->ledger.confirm (transaction, state_send2->hash ());
|
||||
ASSERT_EQ (15, confirmed.size ());
|
||||
ASSERT_EQ (15, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (16, node->ledger.cemented_count ());
|
||||
|
||||
ASSERT_TRUE (node->ledger.block_confirmed (transaction, state_send2->hash ()));
|
||||
nano::confirmation_height_info confirmation_height_info;
|
||||
ASSERT_LE (4, node->ledger.account_info (transaction, nano::dev::genesis_key.pub).value ().block_count);
|
||||
ASSERT_EQ (3, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height);
|
||||
ASSERT_EQ (send1->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier);
|
||||
|
||||
ASSERT_LE (7, node->ledger.account_info (transaction, key1.pub).value ().block_count);
|
||||
ASSERT_EQ (6, node->store.confirmation_height.get (transaction, key1.pub).value ().height);
|
||||
ASSERT_EQ (state_send1->hash (), node->store.confirmation_height.get (transaction, key1.pub).value ().frontier);
|
||||
|
||||
ASSERT_EQ (8, node->ledger.account_info (transaction, key2.pub).value ().block_count);
|
||||
ASSERT_EQ (7, node->store.confirmation_height.get (transaction, key2.pub).value ().height);
|
||||
ASSERT_EQ (state_send2->hash (), node->store.confirmation_height.get (transaction, key2.pub).value ().frontier);
|
||||
}
|
||||
|
||||
// This test ensures a block that's cemented cannot be rolled back by the node
|
||||
// A block is inserted and confirmed then later a different block is force inserted with a rollback attempt
|
||||
TEST (ledger_confirm, conflict_rollback_cemented)
|
||||
{
|
||||
nano::state_block_builder builder;
|
||||
auto const genesis_hash = nano::dev::genesis->hash ();
|
||||
|
||||
nano::test::system system;
|
||||
nano::node_flags node_flags;
|
||||
auto node1 = system.add_node (node_flags);
|
||||
|
||||
nano::keypair key1;
|
||||
// create one side of a forked transaction on node1
|
||||
auto fork1a = builder.make_block ()
|
||||
.previous (genesis_hash)
|
||||
.account (nano::dev::genesis_key.pub)
|
||||
.representative (nano::dev::genesis_key.pub)
|
||||
.link (key1.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - 100)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (genesis_hash))
|
||||
.build ();
|
||||
{
|
||||
auto transaction = node1->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::block_status::progress, node1->ledger.process (transaction, fork1a));
|
||||
node1->ledger.confirm (transaction, fork1a->hash ());
|
||||
}
|
||||
ASSERT_TRUE (nano::test::confirmed (*node1, { fork1a }));
|
||||
|
||||
// create the other side of the fork on node2
|
||||
nano::keypair key2;
|
||||
auto fork1b = builder.make_block ()
|
||||
.previous (genesis_hash)
|
||||
.account (nano::dev::genesis_key.pub)
|
||||
.representative (nano::dev::genesis_key.pub)
|
||||
.link (key2.pub) // Different destination same 'previous'
|
||||
.balance (nano::dev::constants.genesis_amount - 100)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (genesis_hash))
|
||||
.build ();
|
||||
|
||||
node1->block_processor.force (fork1b);
|
||||
// node2 already has send2 forced confirmed whilst node1 should have confirmed send1 and therefore we have a cemented fork on node2
|
||||
// and node2 should print an error message on the log that it cannot rollback send2 because it is already cemented
|
||||
[[maybe_unused]] size_t count = 0;
|
||||
ASSERT_TIMELY_EQ (5s, 1, (count = node1->stats.count (nano::stat::type::ledger, nano::stat::detail::rollback_failed)));
|
||||
ASSERT_TRUE (nano::test::confirmed (*node1, { fork1a->hash () })); // fork1a should still remain after the rollback failed event
|
||||
}
|
||||
|
||||
TEST (ledger_confirm, observers)
|
||||
{
|
||||
auto amount (std::numeric_limits<nano::uint128_t>::max ());
|
||||
nano::test::system system;
|
||||
nano::node_flags node_flags;
|
||||
auto node1 = system.add_node (node_flags);
|
||||
nano::keypair key1;
|
||||
nano::block_hash latest1 (node1->latest (nano::dev::genesis_key.pub));
|
||||
nano::block_builder builder;
|
||||
auto send1 = builder
|
||||
.send ()
|
||||
.previous (latest1)
|
||||
.destination (key1.pub)
|
||||
.balance (amount - node1->config.receive_minimum.number ())
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (latest1))
|
||||
.build ();
|
||||
|
||||
auto transaction = node1->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::block_status::progress, node1->ledger.process (transaction, send1));
|
||||
node1->ledger.confirm (transaction, send1->hash ());
|
||||
ASSERT_TRUE (node1->ledger.block_confirmed (transaction, send1->hash ()));
|
||||
ASSERT_EQ (1, node1->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in));
|
||||
ASSERT_EQ (2, node1->ledger.cemented_count ());
|
||||
}
|
||||
|
||||
TEST (ledger_confirm, election_winner_details_clearing_node_process_confirmed)
|
||||
{
|
||||
// Make sure election_winner_details is also cleared if the block never enters the confirmation height processor from node::process_confirmed
|
||||
nano::test::system system (1);
|
||||
auto node = system.nodes.front ();
|
||||
|
||||
nano::block_builder builder;
|
||||
auto send = builder
|
||||
.send ()
|
||||
.previous (nano::dev::genesis->hash ())
|
||||
.destination (nano::dev::genesis_key.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (nano::dev::genesis->hash ()))
|
||||
.build ();
|
||||
// Add to election_winner_details. Use an unrealistic iteration so that it should fall into the else case and do a cleanup
|
||||
node->active.add_election_winner_details (send->hash (), nullptr);
|
||||
nano::election_status election;
|
||||
election.winner = send;
|
||||
node->process_confirmed (election, 1000000);
|
||||
ASSERT_EQ (0, node->active.election_winner_details_size ());
|
||||
}
|
||||
|
||||
TEST (ledger_confirm, pruned_source)
|
||||
{
|
||||
nano::logger logger;
|
||||
auto path (nano::unique_path ());
|
||||
auto store = nano::make_store (logger, path, nano::dev::constants);
|
||||
ASSERT_TRUE (!store->init_error ());
|
||||
nano::stats stats;
|
||||
nano::ledger ledger (*store, stats, nano::dev::constants);
|
||||
ledger.pruning = true;
|
||||
nano::write_database_queue write_database_queue (false);
|
||||
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
|
||||
nano::keypair key1, key2;
|
||||
nano::block_builder builder;
|
||||
auto send1 = builder
|
||||
.state ()
|
||||
.account (nano::dev::genesis_key.pub)
|
||||
.previous (nano::dev::genesis->hash ())
|
||||
.representative (nano::dev::genesis_key.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - 100)
|
||||
.link (key1.pub)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*pool.generate (nano::dev::genesis->hash ()))
|
||||
.build ();
|
||||
auto open1 = builder
|
||||
.state ()
|
||||
.account (key1.pub)
|
||||
.previous (0)
|
||||
.representative (key1.pub)
|
||||
.balance (100)
|
||||
.link (send1->hash ())
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*pool.generate (key1.pub))
|
||||
.build ();
|
||||
auto send2 = builder
|
||||
.state ()
|
||||
.account (key1.pub)
|
||||
.previous (open1->hash ())
|
||||
.representative (key1.pub)
|
||||
.balance (50)
|
||||
.link (key2.pub)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*pool.generate (open1->hash ()))
|
||||
.build ();
|
||||
auto send3 = builder
|
||||
.state ()
|
||||
.account (key1.pub)
|
||||
.previous (send2->hash ())
|
||||
.representative (key1.pub)
|
||||
.balance (25)
|
||||
.link (key2.pub)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*pool.generate (send2->hash ()))
|
||||
.build ();
|
||||
auto open2 = builder
|
||||
.state ()
|
||||
.account (key2.pub)
|
||||
.previous (0)
|
||||
.representative (key1.pub)
|
||||
.balance (50)
|
||||
.link (send2->hash ())
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*pool.generate (key2.pub))
|
||||
.build ();
|
||||
auto transaction (store->tx_begin_write ());
|
||||
store->initialize (transaction, ledger.cache, nano::dev::constants);
|
||||
ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1));
|
||||
ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open1));
|
||||
ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2));
|
||||
ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send3));
|
||||
ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open2));
|
||||
ledger.confirm (transaction, send2->hash ());
|
||||
ASSERT_EQ (2, ledger.pruning_action (transaction, send2->hash (), 2));
|
||||
ASSERT_FALSE (ledger.block_exists (transaction, send2->hash ()));
|
||||
ASSERT_FALSE (ledger.block_confirmed (transaction, open2->hash ()));
|
||||
auto confirmed = ledger.confirm (transaction, open2->hash ());
|
||||
ASSERT_TRUE (ledger.block_confirmed (transaction, open2->hash ()));
|
||||
}
|
||||
|
||||
// Test that if a block is marked to be confirmed that doesn't exist in the ledger the program aborts
|
||||
TEST (ledger_confirmDeathTest, rollback_added_block)
|
||||
{
|
||||
// For ASSERT_DEATH_IF_SUPPORTED
|
||||
testing::FLAGS_gtest_death_test_style = "threadsafe";
|
||||
|
||||
// valgrind can be noisy with death tests
|
||||
if (!nano::running_within_valgrind ())
|
||||
{
|
||||
nano::logger logger;
|
||||
auto path (nano::unique_path ());
|
||||
auto store = nano::make_store (logger, path, nano::dev::constants);
|
||||
ASSERT_TRUE (!store->init_error ());
|
||||
nano::stats stats;
|
||||
nano::ledger ledger (*store, stats, nano::dev::constants);
|
||||
nano::write_database_queue write_database_queue (false);
|
||||
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
|
||||
nano::keypair key1;
|
||||
nano::block_builder builder;
|
||||
auto send = builder
|
||||
.send ()
|
||||
.previous (nano::dev::genesis->hash ())
|
||||
.destination (key1.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*pool.generate (nano::dev::genesis->hash ()))
|
||||
.build ();
|
||||
auto transaction (store->tx_begin_write ());
|
||||
store->initialize (transaction, ledger.cache, ledger.constants);
|
||||
ASSERT_DEATH_IF_SUPPORTED (ledger.confirm (transaction, send->hash ()), "");
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
#include <nano/node/scheduler/priority.hpp>
|
||||
#include <nano/node/transport/inproc.hpp>
|
||||
#include <nano/node/transport/socket.hpp>
|
||||
#include <nano/node/transport/tcp_listener.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/test_common/network.hpp>
|
||||
#include <nano/test_common/system.hpp>
|
||||
|
|
@ -21,14 +22,14 @@ using namespace std::chrono_literals;
|
|||
TEST (network, tcp_connection)
|
||||
{
|
||||
nano::test::system system;
|
||||
boost::asio::ip::tcp::acceptor acceptor (system.io_ctx);
|
||||
boost::asio::ip::tcp::acceptor acceptor (*system.io_ctx);
|
||||
auto port = system.get_available_port ();
|
||||
boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v4::any (), port);
|
||||
acceptor.open (endpoint.protocol ());
|
||||
acceptor.set_option (boost::asio::ip::tcp::acceptor::reuse_address (true));
|
||||
acceptor.bind (endpoint);
|
||||
acceptor.listen ();
|
||||
boost::asio::ip::tcp::socket incoming (system.io_ctx);
|
||||
boost::asio::ip::tcp::socket incoming (*system.io_ctx);
|
||||
std::atomic<bool> done1 (false);
|
||||
std::string message1;
|
||||
acceptor.async_accept (incoming, [&done1, &message1] (boost::system::error_code const & ec_a) {
|
||||
|
|
@ -39,7 +40,7 @@ TEST (network, tcp_connection)
|
|||
}
|
||||
done1 = true;
|
||||
});
|
||||
boost::asio::ip::tcp::socket connector (system.io_ctx);
|
||||
boost::asio::ip::tcp::socket connector (*system.io_ctx);
|
||||
std::atomic<bool> done2 (false);
|
||||
std::string message2;
|
||||
connector.async_connect (boost::asio::ip::tcp::endpoint (boost::asio::ip::address_v4::loopback (), acceptor.local_endpoint ().port ()),
|
||||
|
|
@ -104,7 +105,6 @@ TEST (network, send_node_id_handshake_tcp)
|
|||
auto list2 (node1->network.list (1));
|
||||
ASSERT_EQ (nano::transport::transport_type::tcp, list2[0]->get_type ());
|
||||
ASSERT_EQ (node0->get_node_id (), list2[0]->get_node_id ());
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
TEST (network, last_contacted)
|
||||
|
|
@ -530,46 +530,10 @@ TEST (network, ipv6_from_ipv4)
|
|||
ASSERT_TRUE (endpoint2.address ().is_v6 ());
|
||||
}
|
||||
|
||||
TEST (network, ipv6_bind_send_ipv4)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::endpoint endpoint1 (boost::asio::ip::address_v6::any (), 0);
|
||||
nano::endpoint endpoint2 (boost::asio::ip::address_v4::any (), 0);
|
||||
std::array<uint8_t, 16> bytes1{};
|
||||
std::atomic<bool> finish1{ false };
|
||||
nano::endpoint endpoint3;
|
||||
boost::asio::ip::udp::socket socket1 (system.io_ctx, endpoint1);
|
||||
socket1.async_receive_from (boost::asio::buffer (bytes1.data (), bytes1.size ()), endpoint3, [&finish1] (boost::system::error_code const & error, size_t size_a) {
|
||||
ASSERT_FALSE (error);
|
||||
ASSERT_EQ (16, size_a);
|
||||
finish1 = true;
|
||||
});
|
||||
boost::asio::ip::udp::socket socket2 (system.io_ctx, endpoint2);
|
||||
nano::endpoint endpoint5 (boost::asio::ip::address_v4::loopback (), socket1.local_endpoint ().port ());
|
||||
nano::endpoint endpoint6 (boost::asio::ip::address_v6::v4_mapped (boost::asio::ip::address_v4::loopback ()), socket2.local_endpoint ().port ());
|
||||
socket2.async_send_to (boost::asio::buffer (std::array<uint8_t, 16>{}, 16), endpoint5, [] (boost::system::error_code const & error, size_t size_a) {
|
||||
ASSERT_FALSE (error);
|
||||
ASSERT_EQ (16, size_a);
|
||||
});
|
||||
auto iterations (0);
|
||||
ASSERT_TIMELY (5s, finish1);
|
||||
ASSERT_EQ (endpoint6, endpoint3);
|
||||
std::array<uint8_t, 16> bytes2;
|
||||
nano::endpoint endpoint4;
|
||||
socket2.async_receive_from (boost::asio::buffer (bytes2.data (), bytes2.size ()), endpoint4, [] (boost::system::error_code const & error, size_t size_a) {
|
||||
ASSERT_FALSE (!error);
|
||||
ASSERT_EQ (16, size_a);
|
||||
});
|
||||
socket1.async_send_to (boost::asio::buffer (std::array<uint8_t, 16>{}, 16), endpoint6, [] (boost::system::error_code const & error, size_t size_a) {
|
||||
ASSERT_FALSE (error);
|
||||
ASSERT_EQ (16, size_a);
|
||||
});
|
||||
}
|
||||
|
||||
TEST (network, endpoint_bad_fd)
|
||||
{
|
||||
nano::test::system system (1);
|
||||
system.nodes[0]->stop ();
|
||||
system.stop_node (*system.nodes[0]);
|
||||
auto endpoint (system.nodes[0]->network.endpoint ());
|
||||
ASSERT_TRUE (endpoint.address ().is_loopback ());
|
||||
// The endpoint is invalidated asynchronously
|
||||
|
|
@ -698,10 +662,7 @@ TEST (tcp_listener, DISABLED_tcp_listener_timeout_empty)
|
|||
system.deadline_set (std::chrono::seconds (6));
|
||||
while (!disconnected)
|
||||
{
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (node0->tcp_listener->mutex);
|
||||
disconnected = node0->tcp_listener->connections.empty ();
|
||||
}
|
||||
disconnected = node0->tcp_listener->connection_count () == 0;
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
}
|
||||
|
|
@ -722,19 +683,13 @@ TEST (tcp_listener, tcp_listener_timeout_node_id_handshake)
|
|||
ASSERT_FALSE (ec);
|
||||
});
|
||||
});
|
||||
ASSERT_TIMELY (5s, node0->stats.count (nano::stat::type::message, nano::stat::detail::node_id_handshake) != 0);
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (node0->tcp_listener->mutex);
|
||||
ASSERT_EQ (node0->tcp_listener->connections.size (), 1);
|
||||
}
|
||||
ASSERT_TIMELY (5s, node0->stats.count (nano::stat::type::tcp_server, nano::stat::detail::node_id_handshake) != 0);
|
||||
ASSERT_EQ (node0->tcp_listener->connection_count (), 1);
|
||||
bool disconnected (false);
|
||||
system.deadline_set (std::chrono::seconds (20));
|
||||
while (!disconnected)
|
||||
{
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (node0->tcp_listener->mutex);
|
||||
disconnected = node0->tcp_listener->connections.empty ();
|
||||
}
|
||||
disconnected = node0->tcp_listener->connection_count () == 0;
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
}
|
||||
|
|
@ -759,7 +714,7 @@ TEST (network, peer_max_tcp_attempts)
|
|||
node->network.merge_peer (node2->network.endpoint ());
|
||||
}
|
||||
ASSERT_EQ (0, node->network.size ());
|
||||
ASSERT_TRUE (node->network.tcp_channels.reachout (nano::endpoint (node->network.endpoint ().address (), system.get_available_port ())));
|
||||
ASSERT_FALSE (node->network.tcp_channels.track_reachout (nano::endpoint (node->network.endpoint ().address (), system.get_available_port ())));
|
||||
ASSERT_EQ (1, node->stats.count (nano::stat::type::tcp, nano::stat::detail::tcp_max_per_ip, nano::stat::dir::out));
|
||||
}
|
||||
#endif
|
||||
|
|
@ -779,11 +734,11 @@ namespace transport
|
|||
{
|
||||
auto address (boost::asio::ip::address_v6::v4_mapped (boost::asio::ip::address_v4 (0x7f000001 + i))); // 127.0.0.1 hex
|
||||
nano::endpoint endpoint (address, system.get_available_port ());
|
||||
ASSERT_FALSE (node->network.tcp_channels.reachout (endpoint));
|
||||
ASSERT_TRUE (node->network.tcp_channels.track_reachout (endpoint));
|
||||
}
|
||||
ASSERT_EQ (0, node->network.size ());
|
||||
ASSERT_EQ (0, node->stats.count (nano::stat::type::tcp, nano::stat::detail::tcp_max_per_subnetwork, nano::stat::dir::out));
|
||||
ASSERT_TRUE (node->network.tcp_channels.reachout (nano::endpoint (boost::asio::ip::make_address_v6 ("::ffff:127.0.0.1"), system.get_available_port ())));
|
||||
ASSERT_FALSE (node->network.tcp_channels.track_reachout (nano::endpoint (boost::asio::ip::make_address_v6 ("::ffff:127.0.0.1"), system.get_available_port ())));
|
||||
ASSERT_EQ (1, node->stats.count (nano::stat::type::tcp, nano::stat::detail::tcp_max_per_subnetwork, nano::stat::dir::out));
|
||||
}
|
||||
}
|
||||
|
|
@ -973,7 +928,7 @@ TEST (network, tcp_no_connect_excluded_peers)
|
|||
ASSERT_EQ (nullptr, node0->network.find_node_id (node1->get_node_id ()));
|
||||
|
||||
// Should not actively reachout to excluded peers
|
||||
ASSERT_TRUE (node0->network.reachout (node1->network.endpoint (), true));
|
||||
ASSERT_FALSE (node0->network.track_reachout (node1->network.endpoint ()));
|
||||
|
||||
// Erasing from excluded peers should allow a connection
|
||||
node0->network.excluded_peers.remove (endpoint1_tcp);
|
||||
|
|
@ -1079,7 +1034,7 @@ TEST (network, cleanup_purge)
|
|||
ASSERT_EQ (1, node1.network.size ());
|
||||
|
||||
node1.network.cleanup (std::chrono::steady_clock::now ());
|
||||
ASSERT_EQ (0, node1.network.size ());
|
||||
ASSERT_TIMELY_EQ (5s, 0, node1.network.size ());
|
||||
}
|
||||
|
||||
TEST (network, loopback_channel)
|
||||
|
|
@ -1095,8 +1050,6 @@ TEST (network, loopback_channel)
|
|||
ASSERT_EQ (channel1.get_node_id (), node1.node_id.pub);
|
||||
ASSERT_EQ (channel1.get_node_id_optional ().value_or (0), node1.node_id.pub);
|
||||
nano::transport::inproc::channel channel2 (node2, node2);
|
||||
ASSERT_EQ (channel1, channel1);
|
||||
ASSERT_FALSE (channel1 == channel2);
|
||||
++node1.network.port;
|
||||
ASSERT_NE (channel1.get_endpoint (), node1.network.endpoint ());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/lib/config.hpp>
|
||||
#include <nano/lib/logging.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/confirming_set.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/local_vote_history.hpp>
|
||||
#include <nano/node/make_store.hpp>
|
||||
#include <nano/node/scheduler/component.hpp>
|
||||
#include <nano/node/scheduler/manual.hpp>
|
||||
#include <nano/node/scheduler/priority.hpp>
|
||||
#include <nano/node/transport/fake.hpp>
|
||||
#include <nano/node/transport/inproc.hpp>
|
||||
#include <nano/node/transport/tcp_listener.hpp>
|
||||
#include <nano/node/vote_generator.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/test_common/network.hpp>
|
||||
#include <nano/test_common/system.hpp>
|
||||
|
|
@ -35,8 +40,6 @@ TEST (node, stop)
|
|||
{
|
||||
nano::test::system system (1);
|
||||
ASSERT_NE (system.nodes[0]->wallets.items.end (), system.nodes[0]->wallets.items.begin ());
|
||||
system.nodes[0]->stop ();
|
||||
system.io_ctx.run ();
|
||||
ASSERT_TRUE (true);
|
||||
}
|
||||
|
||||
|
|
@ -68,13 +71,14 @@ TEST (node, work_generate)
|
|||
TEST (node, block_store_path_failure)
|
||||
{
|
||||
nano::test::system system;
|
||||
auto service (std::make_shared<boost::asio::io_context> ());
|
||||
auto io_ctx = std::make_shared<boost::asio::io_context> ();
|
||||
auto path (nano::unique_path ());
|
||||
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
|
||||
auto node (std::make_shared<nano::node> (*service, system.get_available_port (), path, pool));
|
||||
auto node (std::make_shared<nano::node> (io_ctx, system.get_available_port (), path, pool));
|
||||
system.register_node (node);
|
||||
ASSERT_TRUE (node->wallets.items.empty ());
|
||||
node->stop ();
|
||||
}
|
||||
|
||||
#if defined(__clang__) && defined(__linux__) && CI
|
||||
// Disable test due to instability with clang and actions
|
||||
TEST (node_DeathTest, DISABLED_readonly_block_store_not_exist)
|
||||
|
|
@ -97,16 +101,13 @@ TEST (node_DeathTest, readonly_block_store_not_exist)
|
|||
TEST (node, password_fanout)
|
||||
{
|
||||
nano::test::system system;
|
||||
boost::asio::io_context io_ctx;
|
||||
auto path (nano::unique_path ());
|
||||
nano::node_config config;
|
||||
config.peering_port = system.get_available_port ();
|
||||
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
|
||||
config.password_fanout = 10;
|
||||
nano::node node (io_ctx, path, config, pool);
|
||||
auto & node = *system.add_node (config);
|
||||
auto wallet (node.wallets.create (100));
|
||||
ASSERT_EQ (10, wallet->store.password.values.size ());
|
||||
node.stop ();
|
||||
}
|
||||
|
||||
TEST (node, balance)
|
||||
|
|
@ -276,11 +277,9 @@ TEST (node, auto_bootstrap)
|
|||
ASSERT_TIMELY (10s, !node1->bootstrap_initiator.in_progress ());
|
||||
ASSERT_TRUE (node1->ledger.block_or_pruned_exists (send1->hash ()));
|
||||
// Wait block receive
|
||||
ASSERT_TIMELY_EQ (5s, node1->ledger.cache.block_count, 3);
|
||||
ASSERT_TIMELY_EQ (5s, node1->ledger.block_count (), 3);
|
||||
// Confirmation for all blocks
|
||||
ASSERT_TIMELY_EQ (5s, node1->ledger.cache.cemented_count, 3);
|
||||
|
||||
node1->stop ();
|
||||
ASSERT_TIMELY_EQ (5s, node1->ledger.cemented_count (), 3);
|
||||
}
|
||||
|
||||
TEST (node, auto_bootstrap_reverse)
|
||||
|
|
@ -324,8 +323,6 @@ TEST (node, auto_bootstrap_age)
|
|||
ASSERT_TIMELY (10s, node0->stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate_legacy_age, nano::stat::dir::out) >= 3);
|
||||
// More attempts with frontiers age
|
||||
ASSERT_GE (node0->stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate_legacy_age, nano::stat::dir::out), node0->stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate, nano::stat::dir::out));
|
||||
|
||||
node1->stop ();
|
||||
}
|
||||
|
||||
TEST (node, merge_peers)
|
||||
|
|
@ -430,7 +427,7 @@ TEST (node, search_receivable_pruned)
|
|||
// Confirmation
|
||||
ASSERT_TIMELY (10s, node1->active.empty () && node2->active.empty ());
|
||||
ASSERT_TIMELY (5s, node1->ledger.block_confirmed (node1->store.tx_begin_read (), send2->hash ()));
|
||||
ASSERT_TIMELY_EQ (5s, node2->ledger.cache.cemented_count, 3);
|
||||
ASSERT_TIMELY_EQ (5s, node2->ledger.cemented_count (), 3);
|
||||
system.wallet (0)->store.erase (node1->wallets.tx_begin_write (), nano::dev::genesis_key.pub);
|
||||
|
||||
// Pruning
|
||||
|
|
@ -438,7 +435,7 @@ TEST (node, search_receivable_pruned)
|
|||
auto transaction (node2->store.tx_begin_write ());
|
||||
ASSERT_EQ (1, node2->ledger.pruning_action (transaction, send1->hash (), 1));
|
||||
}
|
||||
ASSERT_EQ (1, node2->ledger.cache.pruned_count);
|
||||
ASSERT_EQ (1, node2->ledger.pruned_count ());
|
||||
ASSERT_TRUE (node2->ledger.block_or_pruned_exists (send1->hash ())); // true for pruned
|
||||
|
||||
// Receive pruned block
|
||||
|
|
@ -1281,7 +1278,7 @@ TEST (node, DISABLED_broadcast_elected)
|
|||
auto election (node->active.election (block->qualified_root ()));
|
||||
ASSERT_NE (nullptr, election);
|
||||
election->force_confirm ();
|
||||
ASSERT_TIMELY_EQ (5s, 4, node->ledger.cache.cemented_count)
|
||||
ASSERT_TIMELY_EQ (5s, 4, node->ledger.cemented_count ())
|
||||
}
|
||||
|
||||
system.wallet (0)->insert_adhoc (rep_big.prv);
|
||||
|
|
@ -1596,7 +1593,7 @@ TEST (node, unconfirmed_send)
|
|||
ASSERT_TIMELY (5s, node1.block_confirmed (send2->hash ()));
|
||||
ASSERT_TIMELY (5s, node2.block_confirmed (send3->hash ()));
|
||||
ASSERT_TIMELY (5s, node1.block_confirmed (send3->hash ()));
|
||||
ASSERT_TIMELY_EQ (5s, node2.ledger.cache.cemented_count, 7);
|
||||
ASSERT_TIMELY_EQ (5s, node2.ledger.cemented_count (), 7);
|
||||
ASSERT_TIMELY_EQ (5s, node1.balance (nano::dev::genesis_key.pub), nano::dev::constants.genesis_amount);
|
||||
}
|
||||
|
||||
|
|
@ -1918,7 +1915,7 @@ TEST (node, local_votes_cache)
|
|||
std::shared_ptr<nano::election> election;
|
||||
ASSERT_TIMELY (5s, election = node.active.election (send2->qualified_root ()));
|
||||
election->force_confirm ();
|
||||
ASSERT_TIMELY_EQ (3s, node.ledger.cache.cemented_count, 3);
|
||||
ASSERT_TIMELY_EQ (3s, node.ledger.cemented_count (), 3);
|
||||
system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv);
|
||||
nano::confirm_req message1{ nano::dev::network_params.network, send1->hash (), send1->root () };
|
||||
nano::confirm_req message2{ nano::dev::network_params.network, send2->hash (), send2->root () };
|
||||
|
|
@ -1980,7 +1977,7 @@ TEST (node, DISABLED_local_votes_cache_batch)
|
|||
.work (*node.work_generate_blocking (nano::dev::genesis->hash ()))
|
||||
.build ();
|
||||
ASSERT_EQ (nano::block_status::progress, node.ledger.process (node.store.tx_begin_write (), send1));
|
||||
node.confirmation_height_processor.add (send1);
|
||||
node.confirming_set.add (send1->hash ());
|
||||
ASSERT_TIMELY (5s, node.ledger.block_confirmed (node.store.tx_begin_read (), send1->hash ()));
|
||||
auto send2 = nano::state_block_builder ()
|
||||
.account (nano::dev::genesis_key.pub)
|
||||
|
|
@ -2839,7 +2836,7 @@ TEST (node, peers)
|
|||
ASSERT_TRUE (store.peer.exists (store.tx_begin_read (), endpoint_key));
|
||||
|
||||
// Stop the peer node and check that it is removed from the store
|
||||
node1->stop ();
|
||||
system.stop_node (*node1);
|
||||
|
||||
// TODO: In `tcp_channels::store_all` we skip store operation when there are no peers present,
|
||||
// so the best we can do here is check if network is empty
|
||||
|
|
@ -2869,7 +2866,7 @@ TEST (node, peer_cache_restart)
|
|||
auto list (node2->network.list (2));
|
||||
ASSERT_EQ (node1->network.endpoint (), list[0]->get_endpoint ());
|
||||
ASSERT_EQ (1, node2->network.size ());
|
||||
node2->stop ();
|
||||
system.stop_node (*node2);
|
||||
}
|
||||
// Restart node
|
||||
{
|
||||
|
|
@ -2892,7 +2889,7 @@ TEST (node, peer_cache_restart)
|
|||
auto list (node3->network.list (2));
|
||||
ASSERT_EQ (node1->network.endpoint (), list[0]->get_endpoint ());
|
||||
ASSERT_EQ (1, node3->network.size ());
|
||||
node3->stop ();
|
||||
system.stop_node (*node3);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2907,7 +2904,7 @@ TEST (node, dont_write_lock_node)
|
|||
nano::logger logger;
|
||||
auto store = nano::make_store (logger, path, nano::dev::constants, false, true);
|
||||
{
|
||||
nano::ledger_cache ledger_cache;
|
||||
nano::ledger_cache ledger_cache{ store->rep_weight };
|
||||
auto transaction (store->tx_begin_write ());
|
||||
store->initialize (transaction, ledger_cache, nano::dev::constants);
|
||||
}
|
||||
|
|
@ -3085,7 +3082,7 @@ TEST (node, rollback_vote_self)
|
|||
// Process and mark the first 2 blocks as confirmed to allow voting
|
||||
ASSERT_TRUE (nano::test::process (node, { send1, open }));
|
||||
ASSERT_TRUE (nano::test::start_elections (system, node, { send1, open }, true));
|
||||
ASSERT_TIMELY_EQ (5s, node.ledger.cache.cemented_count, 3);
|
||||
ASSERT_TIMELY_EQ (5s, node.ledger.cemented_count (), 3);
|
||||
|
||||
// wait until the rep weights have caught up with the weight transfer
|
||||
ASSERT_TIMELY_EQ (5s, nano::dev::constants.genesis_amount / 2, node.weight (key.pub));
|
||||
|
|
@ -3367,7 +3364,7 @@ TEST (node, dependency_graph)
|
|||
{ key3_receive->hash (), { key3_open->hash (), key1_send2->hash () } },
|
||||
{ key3_epoch->hash (), { key3_receive->hash () } },
|
||||
};
|
||||
ASSERT_EQ (node.ledger.cache.block_count - 2, dependency_graph.size ());
|
||||
ASSERT_EQ (node.ledger.block_count () - 2, dependency_graph.size ());
|
||||
|
||||
// Start an election for the first block of the dependency graph, and ensure all blocks are eventually confirmed
|
||||
system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv);
|
||||
|
|
@ -3393,9 +3390,9 @@ TEST (node, dependency_graph)
|
|||
});
|
||||
|
||||
EXPECT_FALSE (error);
|
||||
return error || node.ledger.cache.cemented_count == node.ledger.cache.block_count;
|
||||
return error || node.ledger.cemented_count () == node.ledger.block_count ();
|
||||
}));
|
||||
ASSERT_EQ (node.ledger.cache.cemented_count, node.ledger.cache.block_count);
|
||||
ASSERT_EQ (node.ledger.cemented_count (), node.ledger.block_count ());
|
||||
ASSERT_TIMELY (5s, node.active.empty ());
|
||||
}
|
||||
|
||||
|
|
@ -3562,8 +3559,8 @@ TEST (node, dependency_graph_frontier)
|
|||
ASSERT_TIMELY (10s, node2.active.active (gen_send1->qualified_root ()));
|
||||
node1.start_election (gen_send1);
|
||||
|
||||
ASSERT_TIMELY_EQ (15s, node1.ledger.cache.cemented_count, node1.ledger.cache.block_count);
|
||||
ASSERT_TIMELY_EQ (15s, node2.ledger.cache.cemented_count, node2.ledger.cache.block_count);
|
||||
ASSERT_TIMELY_EQ (15s, node1.ledger.cemented_count (), node1.ledger.block_count ());
|
||||
ASSERT_TIMELY_EQ (15s, node2.ledger.cemented_count (), node2.ledger.block_count ());
|
||||
}
|
||||
|
||||
namespace nano
|
||||
|
|
@ -3737,11 +3734,11 @@ TEST (node, pruning_automatic)
|
|||
ASSERT_TIMELY (5s, node1.block_confirmed (send2->hash ()));
|
||||
|
||||
// Check pruning result
|
||||
ASSERT_EQ (3, node1.ledger.cache.block_count);
|
||||
ASSERT_TIMELY_EQ (5s, node1.ledger.cache.pruned_count, 1);
|
||||
ASSERT_EQ (3, node1.ledger.block_count ());
|
||||
ASSERT_TIMELY_EQ (5s, node1.ledger.pruned_count (), 1);
|
||||
ASSERT_TIMELY_EQ (5s, node1.store.pruned.count (node1.store.tx_begin_read ()), 1);
|
||||
ASSERT_EQ (1, node1.ledger.cache.pruned_count);
|
||||
ASSERT_EQ (3, node1.ledger.cache.block_count);
|
||||
ASSERT_EQ (1, node1.ledger.pruned_count ());
|
||||
ASSERT_EQ (3, node1.ledger.block_count ());
|
||||
|
||||
ASSERT_TRUE (nano::test::block_or_pruned_all_exists (node1, { nano::dev::genesis, send1, send2 }));
|
||||
}
|
||||
|
|
@ -3788,19 +3785,19 @@ TEST (node, pruning_age)
|
|||
ASSERT_TIMELY (5s, node1.block_confirmed (send2->hash ()));
|
||||
|
||||
// Three blocks in total, nothing pruned yet
|
||||
ASSERT_EQ (0, node1.ledger.cache.pruned_count);
|
||||
ASSERT_EQ (3, node1.ledger.cache.block_count);
|
||||
ASSERT_EQ (0, node1.ledger.pruned_count ());
|
||||
ASSERT_EQ (3, node1.ledger.block_count ());
|
||||
|
||||
// Pruning with default age 1 day
|
||||
node1.ledger_pruning (1, true);
|
||||
ASSERT_EQ (0, node1.ledger.cache.pruned_count);
|
||||
ASSERT_EQ (3, node1.ledger.cache.block_count);
|
||||
ASSERT_EQ (0, node1.ledger.pruned_count ());
|
||||
ASSERT_EQ (3, node1.ledger.block_count ());
|
||||
|
||||
// Pruning with max age 0
|
||||
node1.config.max_pruning_age = std::chrono::seconds{ 0 };
|
||||
node1.ledger_pruning (1, true);
|
||||
ASSERT_EQ (1, node1.ledger.cache.pruned_count);
|
||||
ASSERT_EQ (3, node1.ledger.cache.block_count);
|
||||
ASSERT_EQ (1, node1.ledger.pruned_count ());
|
||||
ASSERT_EQ (3, node1.ledger.block_count ());
|
||||
|
||||
ASSERT_TRUE (nano::test::block_or_pruned_all_exists (node1, { nano::dev::genesis, send1, send2 }));
|
||||
}
|
||||
|
|
@ -3849,19 +3846,19 @@ TEST (node, pruning_depth)
|
|||
ASSERT_TIMELY (5s, node1.block_confirmed (send2->hash ()));
|
||||
|
||||
// Three blocks in total, nothing pruned yet
|
||||
ASSERT_EQ (0, node1.ledger.cache.pruned_count);
|
||||
ASSERT_EQ (3, node1.ledger.cache.block_count);
|
||||
ASSERT_EQ (0, node1.ledger.pruned_count ());
|
||||
ASSERT_EQ (3, node1.ledger.block_count ());
|
||||
|
||||
// Pruning with default depth (unlimited)
|
||||
node1.ledger_pruning (1, true);
|
||||
ASSERT_EQ (0, node1.ledger.cache.pruned_count);
|
||||
ASSERT_EQ (3, node1.ledger.cache.block_count);
|
||||
ASSERT_EQ (0, node1.ledger.pruned_count ());
|
||||
ASSERT_EQ (3, node1.ledger.block_count ());
|
||||
|
||||
// Pruning with max depth 1
|
||||
node1.config.max_pruning_depth = 1;
|
||||
node1.ledger_pruning (1, true);
|
||||
ASSERT_EQ (1, node1.ledger.cache.pruned_count);
|
||||
ASSERT_EQ (3, node1.ledger.cache.block_count);
|
||||
ASSERT_EQ (1, node1.ledger.pruned_count ());
|
||||
ASSERT_EQ (3, node1.ledger.block_count ());
|
||||
|
||||
ASSERT_TRUE (nano::test::block_or_pruned_all_exists (node1, { nano::dev::genesis, send1, send2 }));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -556,4 +556,36 @@ TEST (object_stream, to_json)
|
|||
)");
|
||||
|
||||
ASSERT_EQ (str, expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST (object_stream, print_range)
|
||||
{
|
||||
std::deque<streamable_object> objects;
|
||||
objects.push_back ({ 1 });
|
||||
objects.push_back ({ 2 });
|
||||
objects.push_back ({ 3 });
|
||||
|
||||
std::stringstream ss1, ss2;
|
||||
ss1 << nano::streamed_range (objects);
|
||||
ss2 << fmt::format ("{}", nano::streamed_range (objects));
|
||||
|
||||
auto expected = trim (R"(
|
||||
[
|
||||
{
|
||||
uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
block_hash: "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
{
|
||||
uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||
block_hash: "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
{
|
||||
uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000003",
|
||||
block_hash: "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
]
|
||||
)");
|
||||
|
||||
ASSERT_EQ (ss1.str (), expected);
|
||||
ASSERT_EQ (ss2.str (), expected);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/test_common/chains.hpp>
|
||||
#include <nano/test_common/system.hpp>
|
||||
|
|
|
|||
|
|
@ -217,18 +217,19 @@ TEST (peer_container, reachout)
|
|||
auto outer_node1 = nano::test::add_outer_node (system);
|
||||
ASSERT_NE (nullptr, nano::test::establish_tcp (system, node1, outer_node1->network.endpoint ()));
|
||||
// Make sure having been contacted by them already indicates we shouldn't reach out
|
||||
ASSERT_TRUE (node1.network.reachout (outer_node1->network.endpoint ()));
|
||||
ASSERT_FALSE (node1.network.track_reachout (outer_node1->network.endpoint ()));
|
||||
auto outer_node2 = nano::test::add_outer_node (system);
|
||||
ASSERT_FALSE (node1.network.reachout (outer_node2->network.endpoint ()));
|
||||
ASSERT_TRUE (node1.network.track_reachout (outer_node2->network.endpoint ()));
|
||||
ASSERT_NE (nullptr, nano::test::establish_tcp (system, node1, outer_node2->network.endpoint ()));
|
||||
// Reaching out to them once should signal we shouldn't reach out again.
|
||||
ASSERT_TRUE (node1.network.reachout (outer_node2->network.endpoint ()));
|
||||
ASSERT_FALSE (node1.network.track_reachout (outer_node2->network.endpoint ()));
|
||||
// Make sure we don't purge new items
|
||||
node1.network.cleanup (std::chrono::steady_clock::now () - std::chrono::seconds (10));
|
||||
ASSERT_TRUE (node1.network.reachout (outer_node2->network.endpoint ()));
|
||||
ASSERT_FALSE (node1.network.track_reachout (outer_node2->network.endpoint ()));
|
||||
// Make sure we purge old items
|
||||
node1.network.cleanup (std::chrono::steady_clock::now () + std::chrono::seconds (10));
|
||||
ASSERT_FALSE (node1.network.reachout (outer_node2->network.endpoint ()));
|
||||
ASSERT_TIMELY (5s, node1.network.empty ());
|
||||
ASSERT_TRUE (node1.network.track_reachout (outer_node2->network.endpoint ()));
|
||||
}
|
||||
|
||||
// This test is similar to network.filter_invalid_version_using with the difference that
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/lib/config.hpp>
|
||||
#include <nano/lib/logging.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/repcrawler.hpp>
|
||||
#include <nano/node/transport/fake.hpp>
|
||||
#include <nano/node/transport/inproc.hpp>
|
||||
|
|
@ -106,7 +107,7 @@ TEST (rep_crawler, rep_weight)
|
|||
ASSERT_EQ (1, reps.size ());
|
||||
ASSERT_EQ (node.balance (nano::dev::genesis_key.pub), node.ledger.weight (reps[0].account));
|
||||
ASSERT_EQ (nano::dev::genesis_key.pub, reps[0].account);
|
||||
ASSERT_EQ (*channel1, *reps[0].channel);
|
||||
ASSERT_EQ (channel1, reps[0].channel);
|
||||
ASSERT_TRUE (node.rep_crawler.is_pr (channel1));
|
||||
ASSERT_FALSE (node.rep_crawler.is_pr (channel2));
|
||||
ASSERT_TRUE (node.rep_crawler.is_pr (channel3));
|
||||
|
|
@ -189,7 +190,7 @@ TEST (rep_crawler, rep_remove)
|
|||
ASSERT_EQ (1, reps.size ());
|
||||
ASSERT_EQ (searching_node.minimum_principal_weight () * 2, searching_node.ledger.weight (reps[0].account));
|
||||
ASSERT_EQ (keys_rep1.pub, reps[0].account);
|
||||
ASSERT_EQ (*channel_rep1, *reps[0].channel);
|
||||
ASSERT_EQ (channel_rep1, reps[0].channel);
|
||||
|
||||
// When rep1 disconnects then rep1 should not be found anymore
|
||||
channel_rep1->close ();
|
||||
|
|
@ -219,7 +220,7 @@ TEST (rep_crawler, rep_remove)
|
|||
ASSERT_TIMELY_EQ (10s, searching_node.rep_crawler.representative_count (), 2);
|
||||
|
||||
// When Rep2 is stopped, it should not be found as principal representative anymore
|
||||
node_rep2->stop ();
|
||||
system.stop_node (*node_rep2);
|
||||
ASSERT_TIMELY_EQ (10s, searching_node.rep_crawler.representative_count (), 1);
|
||||
|
||||
// Now only genesisRep should be found:
|
||||
|
|
@ -238,7 +239,7 @@ TEST (rep_crawler, rep_connection_close)
|
|||
// Add working representative (node 2)
|
||||
system.wallet (1)->insert_adhoc (nano::dev::genesis_key.prv);
|
||||
ASSERT_TIMELY_EQ (10s, node1.rep_crawler.representative_count (), 1);
|
||||
node2.stop ();
|
||||
system.stop_node (node2);
|
||||
// Remove representative with closed channel
|
||||
ASSERT_TIMELY_EQ (10s, node1.rep_crawler.representative_count (), 0);
|
||||
}
|
||||
|
|
@ -251,7 +252,7 @@ TEST (rep_crawler, recently_confirmed)
|
|||
{
|
||||
nano::test::system system (1);
|
||||
auto & node1 (*system.nodes[0]);
|
||||
ASSERT_EQ (1, node1.ledger.cache.block_count);
|
||||
ASSERT_EQ (1, node1.ledger.block_count ());
|
||||
auto const block = nano::dev::genesis;
|
||||
node1.active.recently_confirmed.put (block->qualified_root (), block->hash ());
|
||||
auto & node2 (*system.add_node ());
|
||||
|
|
|
|||
75
nano/core_test/rep_weight_store.cpp
Normal file
75
nano/core_test/rep_weight_store.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#include <nano/lib/numbers.hpp>
|
||||
#include <nano/store/component.hpp>
|
||||
#include <nano/store/rep_weight.hpp>
|
||||
#include <nano/test_common/make_store.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
|
||||
TEST (rep_weight_store, empty)
|
||||
{
|
||||
auto store = nano::test::make_store ();
|
||||
ASSERT_TRUE (!store->init_error ());
|
||||
auto txn{ store->tx_begin_read () };
|
||||
ASSERT_EQ (0, store->rep_weight.count (txn));
|
||||
}
|
||||
|
||||
TEST (rep_weight_store, add_item)
|
||||
{
|
||||
auto store = nano::test::make_store ();
|
||||
ASSERT_TRUE (!store->init_error ());
|
||||
auto txn{ store->tx_begin_write () };
|
||||
|
||||
nano::account representative{ 123 };
|
||||
nano::uint128_t weight{ 456 };
|
||||
store->rep_weight.put (txn, representative, weight);
|
||||
|
||||
ASSERT_EQ (1, store->rep_weight.count (txn));
|
||||
ASSERT_EQ (weight, store->rep_weight.get (txn, representative));
|
||||
}
|
||||
|
||||
TEST (rep_weight_store, del)
|
||||
{
|
||||
auto store = nano::test::make_store ();
|
||||
ASSERT_TRUE (!store->init_error ());
|
||||
auto txn{ store->tx_begin_write () };
|
||||
|
||||
store->rep_weight.put (txn, 1, 100);
|
||||
store->rep_weight.put (txn, 2, 200);
|
||||
store->rep_weight.put (txn, 3, 300);
|
||||
|
||||
store->rep_weight.del (txn, 2);
|
||||
|
||||
ASSERT_EQ (2, store->rep_weight.count (txn));
|
||||
ASSERT_EQ (0, store->rep_weight.get (txn, 200));
|
||||
}
|
||||
|
||||
TEST (rep_weight_store, for_each_par)
|
||||
{
|
||||
auto store = nano::test::make_store ();
|
||||
ASSERT_TRUE (!store->init_error ());
|
||||
{
|
||||
auto txn{ store->tx_begin_write () };
|
||||
for (auto i = 0; i < 50; ++i)
|
||||
{
|
||||
store->rep_weight.put (txn, i, 100);
|
||||
}
|
||||
}
|
||||
|
||||
std::atomic_size_t rep_total{ 0 };
|
||||
std::atomic_size_t weight_total{ 0 };
|
||||
|
||||
store->rep_weight.for_each_par (
|
||||
[&rep_total, &weight_total] (auto const &, auto i, auto n) {
|
||||
for (; i != n; ++i)
|
||||
{
|
||||
rep_total.fetch_add (static_cast<std::size_t> (i->first.number ()));
|
||||
weight_total.fetch_add (static_cast<std::size_t> (i->second.number ()));
|
||||
}
|
||||
});
|
||||
|
||||
ASSERT_EQ (1225, rep_total.load ());
|
||||
ASSERT_EQ (50 * 100, weight_total.load ());
|
||||
}
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/confirming_set.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/local_vote_history.hpp>
|
||||
#include <nano/node/request_aggregator.hpp>
|
||||
#include <nano/node/transport/inproc.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
|
|
@ -76,7 +80,7 @@ TEST (request_aggregator, one_update)
|
|||
.work (*node.work_generate_blocking (nano::dev::genesis->hash ()))
|
||||
.build ();
|
||||
ASSERT_EQ (nano::block_status::progress, node.ledger.process (node.store.tx_begin_write (), send1));
|
||||
node.confirmation_height_processor.add (send1);
|
||||
node.confirming_set.add (send1->hash ());
|
||||
ASSERT_TIMELY (5s, node.ledger.block_confirmed (node.store.tx_begin_read (), send1->hash ()));
|
||||
auto send2 = nano::state_block_builder ()
|
||||
.account (nano::dev::genesis_key.pub)
|
||||
|
|
@ -142,7 +146,7 @@ TEST (request_aggregator, two)
|
|||
.work (*node.work_generate_blocking (nano::dev::genesis->hash ()))
|
||||
.build ();
|
||||
ASSERT_EQ (nano::block_status::progress, node.ledger.process (node.store.tx_begin_write (), send1));
|
||||
node.confirmation_height_processor.add (send1);
|
||||
node.confirming_set.add (send1->hash ());
|
||||
ASSERT_TIMELY (5s, node.ledger.block_confirmed (node.store.tx_begin_read (), send1->hash ()));
|
||||
auto send2 = builder.make_block ()
|
||||
.account (nano::dev::genesis_key.pub)
|
||||
|
|
@ -289,7 +293,7 @@ TEST (request_aggregator, split)
|
|||
std::shared_ptr<nano::election> election;
|
||||
ASSERT_TIMELY (5s, election = node.active.election (blocks.back ()->qualified_root ()));
|
||||
election->force_confirm ();
|
||||
ASSERT_TIMELY_EQ (5s, max_vbh + 2, node.ledger.cache.cemented_count);
|
||||
ASSERT_TIMELY_EQ (5s, max_vbh + 2, node.ledger.cemented_count ());
|
||||
ASSERT_EQ (max_vbh + 1, request.size ());
|
||||
auto client = std::make_shared<nano::transport::socket> (node);
|
||||
std::shared_ptr<nano::transport::channel> dummy_channel = std::make_shared<nano::transport::channel_tcp> (node, client);
|
||||
|
|
@ -447,8 +451,6 @@ TEST (request_aggregator, cannot_vote)
|
|||
nano::node_flags flags;
|
||||
flags.disable_request_loop = true;
|
||||
auto & node (*system.add_node (flags));
|
||||
// This prevents activation of blocks which are cemented
|
||||
node.confirmation_height_processor.cemented_observers.clear ();
|
||||
nano::state_block_builder builder;
|
||||
auto send1 = builder.make_block ()
|
||||
.account (nano::dev::genesis_key.pub)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include <nano/boost/asio/ip/network_v6.hpp>
|
||||
#include <nano/lib/thread_runner.hpp>
|
||||
#include <nano/node/transport/socket.hpp>
|
||||
#include <nano/node/transport/tcp_listener.hpp>
|
||||
#include <nano/test_common/system.hpp>
|
||||
#include <nano/test_common/testutil.hpp>
|
||||
|
||||
|
|
@ -29,8 +30,12 @@ TEST (socket, max_connections)
|
|||
|
||||
// start a server socket that allows max 2 live connections
|
||||
auto listener = std::make_shared<nano::transport::tcp_listener> (server_port, *node, 2);
|
||||
listener->start ([&server_sockets] (std::shared_ptr<nano::transport::socket> const & new_connection, boost::system::error_code const & ec_a) {
|
||||
server_sockets.push_back (new_connection);
|
||||
nano::test::stop_guard stop_guard{ *listener };
|
||||
listener->start ([&server_sockets] (std::shared_ptr<nano::transport::socket> const & new_connection, boost::system::error_code const & ec) {
|
||||
if (!ec)
|
||||
{
|
||||
server_sockets.push_back (new_connection);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
|
|
@ -101,8 +106,6 @@ TEST (socket, max_connections)
|
|||
ASSERT_TIMELY_EQ (5s, get_tcp_accept_successes (), 5);
|
||||
ASSERT_TIMELY_EQ (5s, connection_attempts, 8); // connections initiated by the client
|
||||
ASSERT_TIMELY_EQ (5s, server_sockets.size (), 5); // connections accepted by the server
|
||||
|
||||
node->stop ();
|
||||
}
|
||||
|
||||
TEST (socket, max_connections_per_ip)
|
||||
|
|
@ -123,8 +126,12 @@ TEST (socket, max_connections_per_ip)
|
|||
std::vector<std::shared_ptr<nano::transport::socket>> server_sockets;
|
||||
|
||||
auto listener = std::make_shared<nano::transport::tcp_listener> (server_port, *node, max_global_connections);
|
||||
listener->start ([&server_sockets] (std::shared_ptr<nano::transport::socket> const & new_connection, boost::system::error_code const & ec_a) {
|
||||
server_sockets.push_back (new_connection);
|
||||
nano::test::stop_guard stop_guard{ *listener };
|
||||
listener->start ([&server_sockets] (std::shared_ptr<nano::transport::socket> const & new_connection, boost::system::error_code const & ec) {
|
||||
if (!ec)
|
||||
{
|
||||
server_sockets.push_back (new_connection);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
|
|
@ -159,8 +166,6 @@ TEST (socket, max_connections_per_ip)
|
|||
ASSERT_TIMELY_EQ (5s, get_tcp_accept_successes (), max_ip_connections);
|
||||
ASSERT_TIMELY_EQ (5s, get_tcp_max_per_ip (), 1);
|
||||
ASSERT_TIMELY_EQ (5s, connection_attempts, max_ip_connections + 1);
|
||||
|
||||
node->stop ();
|
||||
}
|
||||
|
||||
TEST (socket, limited_subnet_address)
|
||||
|
|
@ -243,8 +248,12 @@ TEST (socket, max_connections_per_subnetwork)
|
|||
std::vector<std::shared_ptr<nano::transport::socket>> server_sockets;
|
||||
|
||||
auto listener = std::make_shared<nano::transport::tcp_listener> (server_port, *node, max_global_connections);
|
||||
listener->start ([&server_sockets] (std::shared_ptr<nano::transport::socket> const & new_connection, boost::system::error_code const & ec_a) {
|
||||
server_sockets.push_back (new_connection);
|
||||
nano::test::stop_guard stop_guard{ *listener };
|
||||
listener->start ([&server_sockets] (std::shared_ptr<nano::transport::socket> const & new_connection, boost::system::error_code const & ec) {
|
||||
if (!ec)
|
||||
{
|
||||
server_sockets.push_back (new_connection);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
|
|
@ -279,8 +288,6 @@ TEST (socket, max_connections_per_subnetwork)
|
|||
ASSERT_TIMELY_EQ (5s, get_tcp_accept_successes (), max_subnetwork_connections);
|
||||
ASSERT_TIMELY_EQ (5s, get_tcp_max_per_subnetwork (), 1);
|
||||
ASSERT_TIMELY_EQ (5s, connection_attempts, max_subnetwork_connections + 1);
|
||||
|
||||
node->stop ();
|
||||
}
|
||||
|
||||
TEST (socket, disabled_max_peers_per_ip)
|
||||
|
|
@ -303,8 +310,12 @@ TEST (socket, disabled_max_peers_per_ip)
|
|||
std::vector<std::shared_ptr<nano::transport::socket>> server_sockets;
|
||||
|
||||
auto server_socket = std::make_shared<nano::transport::tcp_listener> (server_port, *node, max_global_connections);
|
||||
server_socket->start ([&server_sockets] (std::shared_ptr<nano::transport::socket> const & new_connection, boost::system::error_code const & ec_a) {
|
||||
server_sockets.push_back (new_connection);
|
||||
nano::test::stop_guard stop_guard{ *server_socket };
|
||||
server_socket->start ([&server_sockets] (std::shared_ptr<nano::transport::socket> const & new_connection, boost::system::error_code const & ec) {
|
||||
if (!ec)
|
||||
{
|
||||
server_sockets.push_back (new_connection);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
|
|
@ -339,8 +350,6 @@ TEST (socket, disabled_max_peers_per_ip)
|
|||
ASSERT_TIMELY_EQ (5s, get_tcp_accept_successes (), max_ip_connections + 1);
|
||||
ASSERT_TIMELY_EQ (5s, get_tcp_max_per_ip (), 0);
|
||||
ASSERT_TIMELY_EQ (5s, connection_attempts, max_ip_connections + 1);
|
||||
|
||||
node->stop ();
|
||||
}
|
||||
|
||||
TEST (socket, disconnection_of_silent_connections)
|
||||
|
|
@ -363,8 +372,12 @@ TEST (socket, disconnection_of_silent_connections)
|
|||
|
||||
// start a server listening socket
|
||||
auto listener = std::make_shared<nano::transport::tcp_listener> (server_port, *node, 1);
|
||||
listener->start ([&server_data_socket] (std::shared_ptr<nano::transport::socket> const & new_connection, boost::system::error_code const & ec_a) {
|
||||
server_data_socket = new_connection;
|
||||
nano::test::stop_guard stop_guard{ *listener };
|
||||
listener->start ([&server_data_socket] (std::shared_ptr<nano::transport::socket> const & new_connection, boost::system::error_code const & ec) {
|
||||
if (!ec)
|
||||
{
|
||||
server_data_socket = new_connection;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
|
|
@ -393,8 +406,6 @@ TEST (socket, disconnection_of_silent_connections)
|
|||
ASSERT_EQ (0, get_tcp_io_timeout_drops ());
|
||||
// Asserts the silent checker worked.
|
||||
ASSERT_EQ (1, get_tcp_silent_connection_drops ());
|
||||
|
||||
node->stop ();
|
||||
}
|
||||
|
||||
TEST (socket, drop_policy)
|
||||
|
|
@ -406,7 +417,7 @@ TEST (socket, drop_policy)
|
|||
nano::inactive_node inactivenode (nano::unique_path (), node_flags);
|
||||
auto node = inactivenode.node;
|
||||
|
||||
nano::thread_runner runner (node->io_ctx, 1);
|
||||
nano::thread_runner runner (node->io_ctx_shared, 1);
|
||||
|
||||
std::vector<std::shared_ptr<nano::transport::socket>> connections;
|
||||
|
||||
|
|
@ -414,13 +425,17 @@ TEST (socket, drop_policy)
|
|||
auto server_port (system.get_available_port ());
|
||||
|
||||
auto listener = std::make_shared<nano::transport::tcp_listener> (server_port, *node, 1);
|
||||
listener->start ([&connections] (std::shared_ptr<nano::transport::socket> const & new_connection, boost::system::error_code const & ec_a) {
|
||||
connections.push_back (new_connection);
|
||||
nano::test::stop_guard stop_guard{ *listener };
|
||||
listener->start ([&connections] (std::shared_ptr<nano::transport::socket> const & new_connection, boost::system::error_code const & ec) {
|
||||
if (!ec)
|
||||
{
|
||||
connections.push_back (new_connection);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
auto client = std::make_shared<nano::transport::socket> (*node);
|
||||
nano::transport::channel_tcp channel{ *node, client };
|
||||
auto channel = std::make_shared<nano::transport::channel_tcp> (*node, client);
|
||||
nano::test::counted_completion write_completion (static_cast<unsigned> (total_message_count));
|
||||
|
||||
client->async_connect (boost::asio::ip::tcp::endpoint (boost::asio::ip::address_v6::loopback (), listener->endpoint ().port ()),
|
||||
|
|
@ -428,7 +443,7 @@ TEST (socket, drop_policy)
|
|||
for (int i = 0; i < total_message_count; i++)
|
||||
{
|
||||
std::vector<uint8_t> buff (1);
|
||||
channel.send_buffer (
|
||||
channel->send_buffer (
|
||||
nano::shared_const_buffer (std::move (buff)), [&write_completion, client] (boost::system::error_code const & ec, size_t size_a) mutable {
|
||||
client.reset ();
|
||||
write_completion.increment ();
|
||||
|
|
@ -469,7 +484,7 @@ TEST (socket, concurrent_writes)
|
|||
|
||||
// This gives more realistic execution than using system#poll, allowing writes to
|
||||
// queue up and drain concurrently.
|
||||
nano::thread_runner runner (node->io_ctx, 1);
|
||||
nano::thread_runner runner (node->io_ctx_shared, 1);
|
||||
|
||||
constexpr size_t max_connections = 4;
|
||||
constexpr size_t client_count = max_connections;
|
||||
|
|
@ -502,6 +517,7 @@ TEST (socket, concurrent_writes)
|
|||
std::vector<std::shared_ptr<nano::transport::socket>> connections;
|
||||
|
||||
auto listener = std::make_shared<nano::transport::tcp_listener> (server_port, *node, max_connections);
|
||||
nano::test::stop_guard stop_guard{ *listener };
|
||||
listener->start ([&connections, &reader] (std::shared_ptr<nano::transport::socket> const & new_connection, boost::system::error_code const & ec_a) {
|
||||
if (ec_a)
|
||||
{
|
||||
|
|
@ -622,13 +638,13 @@ TEST (socket_timeout, read)
|
|||
|
||||
// create a server socket
|
||||
boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v6::loopback (), system.get_available_port ());
|
||||
boost::asio::ip::tcp::acceptor acceptor (system.io_ctx);
|
||||
boost::asio::ip::tcp::acceptor acceptor (*system.io_ctx);
|
||||
acceptor.open (endpoint.protocol ());
|
||||
acceptor.bind (endpoint);
|
||||
acceptor.listen (boost::asio::socket_base::max_listen_connections);
|
||||
|
||||
// asynchronously accept an incoming connection and create a newsock and do not send any data
|
||||
boost::asio::ip::tcp::socket newsock (system.io_ctx);
|
||||
boost::asio::ip::tcp::socket newsock (*system.io_ctx);
|
||||
acceptor.async_accept (newsock, [] (boost::system::error_code const & ec_a) {
|
||||
EXPECT_FALSE (ec_a);
|
||||
});
|
||||
|
|
@ -668,13 +684,13 @@ TEST (socket_timeout, write)
|
|||
|
||||
// create a server socket
|
||||
boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v6::loopback (), system.get_available_port ());
|
||||
boost::asio::ip::tcp::acceptor acceptor (system.io_ctx);
|
||||
boost::asio::ip::tcp::acceptor acceptor (*system.io_ctx);
|
||||
acceptor.open (endpoint.protocol ());
|
||||
acceptor.bind (endpoint);
|
||||
acceptor.listen (boost::asio::socket_base::max_listen_connections);
|
||||
|
||||
// asynchronously accept an incoming connection and create a newsock and do not receive any data
|
||||
boost::asio::ip::tcp::socket newsock (system.io_ctx);
|
||||
boost::asio::ip::tcp::socket newsock (*system.io_ctx);
|
||||
acceptor.async_accept (newsock, [] (boost::system::error_code const & ec_a) {
|
||||
EXPECT_FALSE (ec_a);
|
||||
});
|
||||
|
|
@ -719,13 +735,13 @@ TEST (socket_timeout, read_overlapped)
|
|||
|
||||
// create a server socket
|
||||
boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v6::loopback (), system.get_available_port ());
|
||||
boost::asio::ip::tcp::acceptor acceptor (system.io_ctx);
|
||||
boost::asio::ip::tcp::acceptor acceptor (*system.io_ctx);
|
||||
acceptor.open (endpoint.protocol ());
|
||||
acceptor.bind (endpoint);
|
||||
acceptor.listen (boost::asio::socket_base::max_listen_connections);
|
||||
|
||||
// asynchronously accept an incoming connection and send one byte only
|
||||
boost::asio::ip::tcp::socket newsock (system.io_ctx);
|
||||
boost::asio::ip::tcp::socket newsock (*system.io_ctx);
|
||||
acceptor.async_accept (newsock, [&newsock] (boost::system::error_code const & ec_a) {
|
||||
EXPECT_FALSE (ec_a);
|
||||
|
||||
|
|
@ -777,13 +793,13 @@ TEST (socket_timeout, write_overlapped)
|
|||
|
||||
// create a server socket
|
||||
boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v6::loopback (), system.get_available_port ());
|
||||
boost::asio::ip::tcp::acceptor acceptor (system.io_ctx);
|
||||
boost::asio::ip::tcp::acceptor acceptor (*system.io_ctx);
|
||||
acceptor.open (endpoint.protocol ());
|
||||
acceptor.bind (endpoint);
|
||||
acceptor.listen (boost::asio::socket_base::max_listen_connections);
|
||||
|
||||
// asynchronously accept an incoming connection and read 2 bytes only
|
||||
boost::asio::ip::tcp::socket newsock (system.io_ctx);
|
||||
boost::asio::ip::tcp::socket newsock (*system.io_ctx);
|
||||
auto buffer = std::make_shared<std::vector<uint8_t>> (1);
|
||||
acceptor.async_accept (newsock, [&newsock, &buffer] (boost::system::error_code const & ec_a) {
|
||||
EXPECT_FALSE (ec_a);
|
||||
|
|
|
|||
|
|
@ -334,16 +334,14 @@ TEST (telemetry, disconnected)
|
|||
nano::node_flags node_flags;
|
||||
auto node_client = system.add_node (node_flags);
|
||||
auto node_server = system.add_node (node_flags);
|
||||
|
||||
nano::test::wait_peer_connections (system);
|
||||
|
||||
auto channel = node_client->network.find_node_id (node_server->get_node_id ());
|
||||
ASSERT_NE (nullptr, channel);
|
||||
|
||||
// Ensure telemetry is available before disconnecting
|
||||
ASSERT_TIMELY (5s, node_client->telemetry.get_telemetry (channel->get_endpoint ()));
|
||||
|
||||
node_server->stop ();
|
||||
system.stop_node (*node_server);
|
||||
ASSERT_TRUE (channel);
|
||||
|
||||
// Ensure telemetry from disconnected peer is removed
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ TEST (toml, daemon_config_deserialize_defaults)
|
|||
ASSERT_EQ (conf.node.bootstrap_serving_threads, defaults.node.bootstrap_serving_threads);
|
||||
ASSERT_EQ (conf.node.bootstrap_frontier_request_count, defaults.node.bootstrap_frontier_request_count);
|
||||
ASSERT_EQ (conf.node.bootstrap_fraction_numerator, defaults.node.bootstrap_fraction_numerator);
|
||||
ASSERT_EQ (conf.node.conf_height_processor_batch_min_time, defaults.node.conf_height_processor_batch_min_time);
|
||||
ASSERT_EQ (conf.node.confirming_set_batch_time, defaults.node.confirming_set_batch_time);
|
||||
ASSERT_EQ (conf.node.confirmation_history_size, defaults.node.confirmation_history_size);
|
||||
ASSERT_EQ (conf.node.enable_voting, defaults.node.enable_voting);
|
||||
ASSERT_EQ (conf.node.external_address, defaults.node.external_address);
|
||||
|
|
@ -176,6 +176,7 @@ TEST (toml, daemon_config_deserialize_defaults)
|
|||
ASSERT_EQ (conf.node.background_threads, defaults.node.background_threads);
|
||||
ASSERT_EQ (conf.node.secondary_work_peers, defaults.node.secondary_work_peers);
|
||||
ASSERT_EQ (conf.node.online_weight_minimum, defaults.node.online_weight_minimum);
|
||||
ASSERT_EQ (conf.node.representative_vote_weight_minimum, defaults.node.representative_vote_weight_minimum);
|
||||
ASSERT_EQ (conf.node.rep_crawler_weight_minimum, defaults.node.rep_crawler_weight_minimum);
|
||||
ASSERT_EQ (conf.node.password_fanout, defaults.node.password_fanout);
|
||||
ASSERT_EQ (conf.node.peering_port, defaults.node.peering_port);
|
||||
|
|
@ -194,6 +195,7 @@ TEST (toml, daemon_config_deserialize_defaults)
|
|||
ASSERT_EQ (conf.node.work_peers, defaults.node.work_peers);
|
||||
ASSERT_EQ (conf.node.work_threads, defaults.node.work_threads);
|
||||
ASSERT_EQ (conf.node.max_queued_requests, defaults.node.max_queued_requests);
|
||||
ASSERT_EQ (conf.node.request_aggregator_threads, defaults.node.request_aggregator_threads);
|
||||
ASSERT_EQ (conf.node.max_unchecked_blocks, defaults.node.max_unchecked_blocks);
|
||||
ASSERT_EQ (conf.node.backlog_scan_batch_size, defaults.node.backlog_scan_batch_size);
|
||||
ASSERT_EQ (conf.node.backlog_scan_frequency, defaults.node.backlog_scan_frequency);
|
||||
|
|
@ -245,6 +247,7 @@ TEST (toml, daemon_config_deserialize_defaults)
|
|||
ASSERT_EQ (conf.node.optimistic_scheduler.gap_threshold, defaults.node.optimistic_scheduler.gap_threshold);
|
||||
ASSERT_EQ (conf.node.optimistic_scheduler.max_size, defaults.node.optimistic_scheduler.max_size);
|
||||
|
||||
ASSERT_EQ (conf.node.hinted_scheduler.enabled, defaults.node.hinted_scheduler.enabled);
|
||||
ASSERT_EQ (conf.node.hinted_scheduler.hinting_threshold_percent, defaults.node.hinted_scheduler.hinting_threshold_percent);
|
||||
ASSERT_EQ (conf.node.hinted_scheduler.check_interval.count (), defaults.node.hinted_scheduler.check_interval.count ());
|
||||
ASSERT_EQ (conf.node.hinted_scheduler.block_cooldown.count (), defaults.node.hinted_scheduler.block_cooldown.count ());
|
||||
|
|
@ -400,7 +403,7 @@ TEST (toml, daemon_config_deserialize_no_defaults)
|
|||
bootstrap_serving_threads = 999
|
||||
bootstrap_frontier_request_count = 9999
|
||||
bootstrap_fraction_numerator = 999
|
||||
conf_height_processor_batch_min_time = 999
|
||||
confirming_set_batch_time = 999
|
||||
confirmation_history_size = 999
|
||||
enable_voting = false
|
||||
external_address = "0:0:0:0:0:ffff:7f01:101"
|
||||
|
|
@ -410,6 +413,7 @@ TEST (toml, daemon_config_deserialize_no_defaults)
|
|||
network_threads = 999
|
||||
background_threads = 999
|
||||
online_weight_minimum = "999"
|
||||
representative_vote_weight_minimum = "999"
|
||||
rep_crawler_weight_minimum = "999"
|
||||
password_fanout = 999
|
||||
peering_port = 999
|
||||
|
|
@ -429,6 +433,7 @@ TEST (toml, daemon_config_deserialize_no_defaults)
|
|||
work_threads = 999
|
||||
max_work_generate_multiplier = 1.0
|
||||
max_queued_requests = 999
|
||||
request_aggregator_threads = 999
|
||||
max_unchecked_blocks = 999
|
||||
frontiers_confirmation = "always"
|
||||
backlog_scan_batch_size = 999
|
||||
|
|
@ -528,6 +533,7 @@ TEST (toml, daemon_config_deserialize_no_defaults)
|
|||
max_size = 999
|
||||
|
||||
[node.hinted_scheduler]
|
||||
enabled = false
|
||||
hinting_threshold = 99
|
||||
check_interval = 999
|
||||
block_cooldown = 999
|
||||
|
|
@ -594,7 +600,7 @@ TEST (toml, daemon_config_deserialize_no_defaults)
|
|||
ASSERT_NE (conf.node.bootstrap_serving_threads, defaults.node.bootstrap_serving_threads);
|
||||
ASSERT_NE (conf.node.bootstrap_frontier_request_count, defaults.node.bootstrap_frontier_request_count);
|
||||
ASSERT_NE (conf.node.bootstrap_fraction_numerator, defaults.node.bootstrap_fraction_numerator);
|
||||
ASSERT_NE (conf.node.conf_height_processor_batch_min_time, defaults.node.conf_height_processor_batch_min_time);
|
||||
ASSERT_NE (conf.node.confirming_set_batch_time, defaults.node.confirming_set_batch_time);
|
||||
ASSERT_NE (conf.node.confirmation_history_size, defaults.node.confirmation_history_size);
|
||||
ASSERT_NE (conf.node.enable_voting, defaults.node.enable_voting);
|
||||
ASSERT_NE (conf.node.external_address, defaults.node.external_address);
|
||||
|
|
@ -609,6 +615,7 @@ TEST (toml, daemon_config_deserialize_no_defaults)
|
|||
ASSERT_NE (conf.node.max_pruning_age, defaults.node.max_pruning_age);
|
||||
ASSERT_NE (conf.node.max_pruning_depth, defaults.node.max_pruning_depth);
|
||||
ASSERT_NE (conf.node.online_weight_minimum, defaults.node.online_weight_minimum);
|
||||
ASSERT_NE (conf.node.representative_vote_weight_minimum, defaults.node.representative_vote_weight_minimum);
|
||||
ASSERT_NE (conf.node.rep_crawler_weight_minimum, defaults.node.rep_crawler_weight_minimum);
|
||||
ASSERT_NE (conf.node.password_fanout, defaults.node.password_fanout);
|
||||
ASSERT_NE (conf.node.peering_port, defaults.node.peering_port);
|
||||
|
|
@ -627,6 +634,7 @@ TEST (toml, daemon_config_deserialize_no_defaults)
|
|||
ASSERT_NE (conf.node.work_peers, defaults.node.work_peers);
|
||||
ASSERT_NE (conf.node.work_threads, defaults.node.work_threads);
|
||||
ASSERT_NE (conf.node.max_queued_requests, defaults.node.max_queued_requests);
|
||||
ASSERT_NE (conf.node.request_aggregator_threads, defaults.node.request_aggregator_threads);
|
||||
ASSERT_NE (conf.node.backlog_scan_batch_size, defaults.node.backlog_scan_batch_size);
|
||||
ASSERT_NE (conf.node.backlog_scan_frequency, defaults.node.backlog_scan_frequency);
|
||||
|
||||
|
|
@ -678,6 +686,7 @@ TEST (toml, daemon_config_deserialize_no_defaults)
|
|||
ASSERT_NE (conf.node.optimistic_scheduler.gap_threshold, defaults.node.optimistic_scheduler.gap_threshold);
|
||||
ASSERT_NE (conf.node.optimistic_scheduler.max_size, defaults.node.optimistic_scheduler.max_size);
|
||||
|
||||
ASSERT_NE (conf.node.hinted_scheduler.enabled, defaults.node.hinted_scheduler.enabled);
|
||||
ASSERT_NE (conf.node.hinted_scheduler.hinting_threshold_percent, defaults.node.hinted_scheduler.hinting_threshold_percent);
|
||||
ASSERT_NE (conf.node.hinted_scheduler.check_interval.count (), defaults.node.hinted_scheduler.check_interval.count ());
|
||||
ASSERT_NE (conf.node.hinted_scheduler.block_cooldown.count (), defaults.node.hinted_scheduler.block_cooldown.count ());
|
||||
|
|
@ -1028,4 +1037,4 @@ TEST (toml, log_config_no_required)
|
|||
confg.deserialize_toml (toml);
|
||||
|
||||
ASSERT_FALSE (toml.get_error ()) << toml.get_error ().get_message ();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <nano/lib/thread_pool.hpp>
|
||||
#include <nano/lib/timer.hpp>
|
||||
#include <nano/lib/utility.hpp>
|
||||
#include <nano/secure/pending_info.hpp>
|
||||
#include <nano/secure/utility.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
|
@ -346,3 +347,16 @@ TEST (relaxed_atomic_integral, many_threads)
|
|||
// Check values
|
||||
ASSERT_EQ (0, atomic);
|
||||
}
|
||||
|
||||
TEST (pending_key, sorting)
|
||||
{
|
||||
nano::pending_key one{ 1, 2 };
|
||||
nano::pending_key two{ 1, 3 };
|
||||
nano::pending_key three{ 2, 1 };
|
||||
ASSERT_LT (one, two);
|
||||
ASSERT_LT (one, three);
|
||||
ASSERT_LT (two, three);
|
||||
nano::pending_key one_same{ 1, 2 };
|
||||
ASSERT_EQ (std::hash<nano::pending_key>{}(one), std::hash<nano::pending_key>{}(one_same));
|
||||
ASSERT_NE (std::hash<nano::pending_key>{}(one), std::hash<nano::pending_key>{}(two));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ TEST (vote_cache, construction)
|
|||
ASSERT_EQ (0, vote_cache.size ());
|
||||
ASSERT_TRUE (vote_cache.empty ());
|
||||
auto hash1 = nano::test::random_hash ();
|
||||
ASSERT_FALSE (vote_cache.find (hash1));
|
||||
ASSERT_TRUE (vote_cache.find (hash1).empty ());
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -57,16 +57,12 @@ TEST (vote_cache, insert_one_hash)
|
|||
auto rep1 = create_rep (7);
|
||||
auto hash1 = nano::test::random_hash ();
|
||||
auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024);
|
||||
vote_cache.vote (vote1->hashes.front (), vote1);
|
||||
vote_cache.insert (vote1);
|
||||
ASSERT_EQ (1, vote_cache.size ());
|
||||
|
||||
auto peek1 = vote_cache.find (hash1);
|
||||
ASSERT_TRUE (peek1);
|
||||
ASSERT_EQ (peek1->hash (), hash1);
|
||||
ASSERT_EQ (peek1->voters ().size (), 1);
|
||||
ASSERT_EQ (peek1->voters ().front ().representative, rep1.pub); // account
|
||||
ASSERT_EQ (peek1->voters ().front ().timestamp, 1024 * 1024); // timestamp
|
||||
ASSERT_EQ (peek1->tally (), 7);
|
||||
ASSERT_EQ (peek1.size (), 1);
|
||||
ASSERT_EQ (peek1.front (), vote1);
|
||||
|
||||
auto tops = vote_cache.top (0);
|
||||
ASSERT_EQ (tops.size (), 1);
|
||||
|
|
@ -92,17 +88,17 @@ TEST (vote_cache, insert_one_hash_many_votes)
|
|||
auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024);
|
||||
auto vote2 = nano::test::make_vote (rep2, { hash1 }, 2 * 1024 * 1024);
|
||||
auto vote3 = nano::test::make_vote (rep3, { hash1 }, 3 * 1024 * 1024);
|
||||
vote_cache.vote (vote1->hashes.front (), vote1);
|
||||
vote_cache.vote (vote2->hashes.front (), vote2);
|
||||
vote_cache.vote (vote3->hashes.front (), vote3);
|
||||
vote_cache.insert (vote1);
|
||||
vote_cache.insert (vote2);
|
||||
vote_cache.insert (vote3);
|
||||
|
||||
// We have 3 votes but for a single hash, so just one entry in vote cache
|
||||
ASSERT_EQ (1, vote_cache.size ());
|
||||
auto peek1 = vote_cache.find (hash1);
|
||||
ASSERT_TRUE (peek1);
|
||||
ASSERT_EQ (peek1->voters ().size (), 3);
|
||||
// Tally must be the sum of rep weights
|
||||
ASSERT_EQ (peek1->tally (), 7 + 9 + 11);
|
||||
ASSERT_EQ (peek1.size (), 3);
|
||||
// Verify each vote is present
|
||||
ASSERT_TRUE (std::find (peek1.begin (), peek1.end (), vote1) != peek1.end ());
|
||||
ASSERT_TRUE (std::find (peek1.begin (), peek1.end (), vote2) != peek1.end ());
|
||||
ASSERT_TRUE (std::find (peek1.begin (), peek1.end (), vote3) != peek1.end ());
|
||||
|
||||
auto tops = vote_cache.top (0);
|
||||
ASSERT_EQ (tops.size (), 1);
|
||||
|
|
@ -136,14 +132,14 @@ TEST (vote_cache, insert_many_hashes_many_votes)
|
|||
auto vote3 = nano::test::make_vote (rep3, { hash3 }, 1024 * 1024);
|
||||
auto vote4 = nano::test::make_vote (rep4, { hash1 }, 1024 * 1024);
|
||||
// Insert first 3 votes in cache
|
||||
vote_cache.vote (vote1->hashes.front (), vote1);
|
||||
vote_cache.vote (vote2->hashes.front (), vote2);
|
||||
vote_cache.vote (vote3->hashes.front (), vote3);
|
||||
vote_cache.insert (vote1);
|
||||
vote_cache.insert (vote2);
|
||||
vote_cache.insert (vote3);
|
||||
// Ensure all of those are properly inserted
|
||||
ASSERT_EQ (3, vote_cache.size ());
|
||||
ASSERT_TRUE (vote_cache.find (hash1));
|
||||
ASSERT_TRUE (vote_cache.find (hash2));
|
||||
ASSERT_TRUE (vote_cache.find (hash3));
|
||||
ASSERT_EQ (1, vote_cache.find (hash1).size ());
|
||||
ASSERT_EQ (1, vote_cache.find (hash2).size ());
|
||||
ASSERT_EQ (1, vote_cache.find (hash3).size ());
|
||||
|
||||
// Ensure that first entry in queue is the one for hash3 (rep3 has the highest weight of the first 3 reps)
|
||||
auto tops1 = vote_cache.top (0);
|
||||
|
|
@ -151,14 +147,12 @@ TEST (vote_cache, insert_many_hashes_many_votes)
|
|||
ASSERT_EQ (tops1[0].hash, hash3);
|
||||
ASSERT_EQ (tops1[0].tally, 11);
|
||||
|
||||
auto peek1 = vote_cache.find (tops1[0].hash);
|
||||
ASSERT_TRUE (peek1);
|
||||
ASSERT_EQ (peek1->voters ().size (), 1);
|
||||
ASSERT_EQ (peek1->tally (), 11);
|
||||
ASSERT_EQ (peek1->hash (), hash3);
|
||||
auto peek1 = vote_cache.find (hash3);
|
||||
ASSERT_EQ (peek1.size (), 1);
|
||||
ASSERT_EQ (peek1.front (), vote3);
|
||||
|
||||
// Now add a vote from rep4 with the highest voting weight
|
||||
vote_cache.vote (vote4->hashes.front (), vote4);
|
||||
vote_cache.insert (vote4);
|
||||
|
||||
// Ensure that the first entry in queue is now the one for hash1 (rep1 + rep4 tally weight)
|
||||
auto tops2 = vote_cache.top (0);
|
||||
|
|
@ -166,31 +160,26 @@ TEST (vote_cache, insert_many_hashes_many_votes)
|
|||
ASSERT_EQ (tops2[0].hash, hash1);
|
||||
ASSERT_EQ (tops2[0].tally, 7 + 13);
|
||||
|
||||
auto pop1 = vote_cache.find (tops2[0].hash);
|
||||
ASSERT_TRUE (pop1);
|
||||
ASSERT_EQ ((*pop1).voters ().size (), 2);
|
||||
ASSERT_EQ ((*pop1).tally (), 7 + 13);
|
||||
ASSERT_EQ ((*pop1).hash (), hash1);
|
||||
auto pop1 = vote_cache.find (hash1);
|
||||
ASSERT_EQ (pop1.size (), 2);
|
||||
ASSERT_TRUE (std::find (pop1.begin (), pop1.end (), vote1) != pop1.end ());
|
||||
ASSERT_TRUE (std::find (pop1.begin (), pop1.end (), vote4) != pop1.end ());
|
||||
|
||||
// The next entry in queue should be hash3 (rep3 tally weight)
|
||||
ASSERT_EQ (tops2[1].hash, hash3);
|
||||
ASSERT_EQ (tops2[1].tally, 11);
|
||||
|
||||
auto pop2 = vote_cache.find (tops2[1].hash);
|
||||
ASSERT_EQ ((*pop2).voters ().size (), 1);
|
||||
ASSERT_EQ ((*pop2).tally (), 11);
|
||||
ASSERT_EQ ((*pop2).hash (), hash3);
|
||||
ASSERT_TRUE (vote_cache.find (hash3));
|
||||
auto pop2 = vote_cache.find (hash3);
|
||||
ASSERT_EQ (pop2.size (), 1);
|
||||
ASSERT_EQ (pop2.front (), vote3);
|
||||
|
||||
// And last one should be hash2 with rep2 tally weight
|
||||
ASSERT_EQ (tops2[2].hash, hash2);
|
||||
ASSERT_EQ (tops2[2].tally, 9);
|
||||
|
||||
auto pop3 = vote_cache.find (tops2[2].hash);
|
||||
ASSERT_EQ ((*pop3).voters ().size (), 1);
|
||||
ASSERT_EQ ((*pop3).tally (), 9);
|
||||
ASSERT_EQ ((*pop3).hash (), hash2);
|
||||
ASSERT_TRUE (vote_cache.find (hash2));
|
||||
auto pop3 = vote_cache.find (hash2);
|
||||
ASSERT_EQ (pop3.size (), 1);
|
||||
ASSERT_EQ (pop3.front (), vote2);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -206,8 +195,8 @@ TEST (vote_cache, insert_duplicate)
|
|||
auto rep1 = create_rep (9);
|
||||
auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024);
|
||||
auto vote2 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024);
|
||||
vote_cache.vote (vote1->hashes.front (), vote1);
|
||||
vote_cache.vote (vote2->hashes.front (), vote2);
|
||||
vote_cache.insert (vote1);
|
||||
vote_cache.insert (vote2);
|
||||
ASSERT_EQ (1, vote_cache.size ());
|
||||
}
|
||||
|
||||
|
|
@ -223,18 +212,15 @@ TEST (vote_cache, insert_newer)
|
|||
auto hash1 = nano::test::random_hash ();
|
||||
auto rep1 = create_rep (9);
|
||||
auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024);
|
||||
vote_cache.vote (vote1->hashes.front (), vote1);
|
||||
vote_cache.insert (vote1);
|
||||
auto peek1 = vote_cache.find (hash1);
|
||||
ASSERT_TRUE (peek1);
|
||||
ASSERT_EQ (peek1.size (), 1);
|
||||
ASSERT_EQ (peek1.front (), vote1);
|
||||
auto vote2 = nano::test::make_final_vote (rep1, { hash1 });
|
||||
vote_cache.vote (vote2->hashes.front (), vote2);
|
||||
vote_cache.insert (vote2);
|
||||
auto peek2 = vote_cache.find (hash1);
|
||||
ASSERT_TRUE (peek2);
|
||||
ASSERT_EQ (1, vote_cache.size ());
|
||||
ASSERT_EQ (1, peek2->voters ().size ());
|
||||
// Second entry should have timestamp greater than the first one
|
||||
ASSERT_GT (peek2->voters ().front ().timestamp, peek1->voters ().front ().timestamp);
|
||||
ASSERT_EQ (peek2->voters ().front ().timestamp, std::numeric_limits<uint64_t>::max ()); // final timestamp
|
||||
ASSERT_EQ (peek2.size (), 1);
|
||||
ASSERT_EQ (peek2.front (), vote2); // vote2 should replace vote1 as it has a higher timestamp
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -249,16 +235,15 @@ TEST (vote_cache, insert_older)
|
|||
auto hash1 = nano::test::random_hash ();
|
||||
auto rep1 = create_rep (9);
|
||||
auto vote1 = nano::test::make_vote (rep1, { hash1 }, 2 * 1024 * 1024);
|
||||
vote_cache.vote (vote1->hashes.front (), vote1);
|
||||
vote_cache.insert (vote1);
|
||||
auto peek1 = vote_cache.find (hash1);
|
||||
ASSERT_TRUE (peek1);
|
||||
ASSERT_EQ (peek1.size (), 1);
|
||||
ASSERT_EQ (peek1.front (), vote1);
|
||||
auto vote2 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024);
|
||||
vote_cache.vote (vote2->hashes.front (), vote2);
|
||||
vote_cache.insert (vote2);
|
||||
auto peek2 = vote_cache.find (hash1);
|
||||
ASSERT_TRUE (peek2);
|
||||
ASSERT_EQ (1, vote_cache.size ());
|
||||
ASSERT_EQ (1, peek2->voters ().size ());
|
||||
ASSERT_EQ (peek2->voters ().front ().timestamp, peek1->voters ().front ().timestamp); // timestamp2 == timestamp1
|
||||
ASSERT_EQ (peek2.size (), 1);
|
||||
ASSERT_EQ (peek2.front (), vote1); // vote1 should still be in cache as it has a higher timestamp
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -280,24 +265,24 @@ TEST (vote_cache, erase)
|
|||
auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024);
|
||||
auto vote2 = nano::test::make_vote (rep2, { hash2 }, 1024 * 1024);
|
||||
auto vote3 = nano::test::make_vote (rep3, { hash3 }, 1024 * 1024);
|
||||
vote_cache.vote (vote1->hashes.front (), vote1);
|
||||
vote_cache.vote (vote2->hashes.front (), vote2);
|
||||
vote_cache.vote (vote3->hashes.front (), vote3);
|
||||
vote_cache.insert (vote1);
|
||||
vote_cache.insert (vote2);
|
||||
vote_cache.insert (vote3);
|
||||
ASSERT_EQ (3, vote_cache.size ());
|
||||
ASSERT_FALSE (vote_cache.empty ());
|
||||
ASSERT_TRUE (vote_cache.find (hash1));
|
||||
ASSERT_TRUE (vote_cache.find (hash2));
|
||||
ASSERT_TRUE (vote_cache.find (hash3));
|
||||
ASSERT_FALSE (vote_cache.find (hash1).empty ());
|
||||
ASSERT_FALSE (vote_cache.find (hash2).empty ());
|
||||
ASSERT_FALSE (vote_cache.find (hash3).empty ());
|
||||
vote_cache.erase (hash2);
|
||||
ASSERT_EQ (2, vote_cache.size ());
|
||||
ASSERT_TRUE (vote_cache.find (hash1));
|
||||
ASSERT_FALSE (vote_cache.find (hash2));
|
||||
ASSERT_TRUE (vote_cache.find (hash3));
|
||||
ASSERT_FALSE (vote_cache.find (hash1).empty ());
|
||||
ASSERT_TRUE (vote_cache.find (hash2).empty ());
|
||||
ASSERT_FALSE (vote_cache.find (hash3).empty ());
|
||||
vote_cache.erase (hash1);
|
||||
vote_cache.erase (hash3);
|
||||
ASSERT_FALSE (vote_cache.find (hash1));
|
||||
ASSERT_FALSE (vote_cache.find (hash2));
|
||||
ASSERT_FALSE (vote_cache.find (hash3));
|
||||
ASSERT_TRUE (vote_cache.find (hash1).empty ());
|
||||
ASSERT_TRUE (vote_cache.find (hash2).empty ());
|
||||
ASSERT_TRUE (vote_cache.find (hash3).empty ());
|
||||
ASSERT_TRUE (vote_cache.empty ());
|
||||
}
|
||||
|
||||
|
|
@ -319,7 +304,7 @@ TEST (vote_cache, overfill)
|
|||
auto rep1 = create_rep (count - n);
|
||||
auto hash1 = nano::test::random_hash ();
|
||||
auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024);
|
||||
vote_cache.vote (vote1->hashes.front (), vote1);
|
||||
vote_cache.insert (vote1);
|
||||
}
|
||||
ASSERT_LT (vote_cache.size (), count);
|
||||
// Check that oldest votes are dropped first
|
||||
|
|
@ -343,7 +328,7 @@ TEST (vote_cache, overfill_entry)
|
|||
{
|
||||
auto rep1 = create_rep (9);
|
||||
auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024);
|
||||
vote_cache.vote (vote1->hashes.front (), vote1);
|
||||
vote_cache.insert (vote1);
|
||||
}
|
||||
ASSERT_EQ (1, vote_cache.size ());
|
||||
}
|
||||
|
|
@ -359,9 +344,9 @@ TEST (vote_cache, age_cutoff)
|
|||
auto hash1 = nano::test::random_hash ();
|
||||
auto rep1 = create_rep (9);
|
||||
auto vote1 = nano::test::make_vote (rep1, { hash1 }, 3);
|
||||
vote_cache.vote (vote1->hashes.front (), vote1);
|
||||
vote_cache.insert (vote1);
|
||||
ASSERT_EQ (1, vote_cache.size ());
|
||||
ASSERT_TRUE (vote_cache.find (hash1));
|
||||
ASSERT_FALSE (vote_cache.find (hash1).empty ());
|
||||
|
||||
auto tops1 = vote_cache.top (0);
|
||||
ASSERT_EQ (tops1.size (), 1);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/transport/inproc.hpp>
|
||||
#include <nano/node/vote_processor.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
|
|
@ -13,8 +15,14 @@ using namespace std::chrono_literals;
|
|||
|
||||
TEST (vote_processor, codes)
|
||||
{
|
||||
nano::test::system system (1);
|
||||
auto & node (*system.nodes[0]);
|
||||
nano::test::system system;
|
||||
auto node_config = system.default_config ();
|
||||
// Disable all election schedulers
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
node_config.hinted_scheduler.enabled = false;
|
||||
node_config.optimistic_scheduler.enabled = false;
|
||||
auto & node = *system.add_node (node_config);
|
||||
|
||||
auto blocks = nano::test::setup_chain (system, node, 1, nano::dev::genesis_key, false);
|
||||
auto vote = nano::test::make_vote (nano::dev::genesis_key, { blocks[0] }, nano::vote::timestamp_min * 1, 0);
|
||||
auto vote_invalid = std::make_shared<nano::vote> (*vote);
|
||||
|
|
@ -46,7 +54,7 @@ TEST (vote_processor, codes)
|
|||
ASSERT_EQ (nano::vote_code::invalid, node.vote_processor.vote_blocking (vote_invalid, channel));
|
||||
|
||||
// Once the election is removed (confirmed / dropped) the vote is again indeterminate
|
||||
node.active.erase (*blocks[0]);
|
||||
ASSERT_TRUE (node.active.erase (blocks[0]->qualified_root ()));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.vote_processor.vote_blocking (vote, channel));
|
||||
}
|
||||
|
||||
|
|
@ -176,8 +184,10 @@ TEST (vote_processor, no_broadcast_local)
|
|||
nano::node_flags flags;
|
||||
flags.disable_request_loop = true;
|
||||
nano::node_config config1, config2;
|
||||
config1.representative_vote_weight_minimum = 0;
|
||||
config1.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto & node (*system.add_node (config1, flags));
|
||||
config2.representative_vote_weight_minimum = 0;
|
||||
config2.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
config2.peering_port = system.get_available_port ();
|
||||
system.add_node (config2, flags);
|
||||
|
|
@ -206,7 +216,7 @@ TEST (vote_processor, no_broadcast_local)
|
|||
ASSERT_FALSE (node.wallets.reps ().have_half_rep ()); // Genesis balance remaining after `send' is less than the half_rep threshold
|
||||
// Process a vote with a key that is in the local wallet.
|
||||
auto vote = std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::milliseconds_since_epoch (), nano::vote::duration_max, std::vector<nano::block_hash>{ send->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote));
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote).at (send->hash ()));
|
||||
// Make sure the vote was processed.
|
||||
auto election (node.active.election (send->qualified_root ()));
|
||||
ASSERT_NE (nullptr, election);
|
||||
|
|
@ -229,8 +239,10 @@ TEST (vote_processor, local_broadcast_without_a_representative)
|
|||
nano::node_flags flags;
|
||||
flags.disable_request_loop = true;
|
||||
nano::node_config config1, config2;
|
||||
config1.representative_vote_weight_minimum = 0;
|
||||
config1.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto & node (*system.add_node (config1, flags));
|
||||
config2.representative_vote_weight_minimum = 0;
|
||||
config2.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
config2.peering_port = system.get_available_port ();
|
||||
system.add_node (config2, flags);
|
||||
|
|
@ -254,7 +266,7 @@ TEST (vote_processor, local_broadcast_without_a_representative)
|
|||
node.start_election (send);
|
||||
// Process a vote without a representative
|
||||
auto vote = std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::milliseconds_since_epoch (), nano::vote::duration_max, std::vector<nano::block_hash>{ send->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote));
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote).at (send->hash ()));
|
||||
// Make sure the vote was processed.
|
||||
std::shared_ptr<nano::election> election;
|
||||
ASSERT_TIMELY (5s, election = node.active.election (send->qualified_root ()));
|
||||
|
|
@ -307,7 +319,7 @@ TEST (vote_processor, no_broadcast_local_with_a_principal_representative)
|
|||
ASSERT_TRUE (node.wallets.reps ().have_half_rep ()); // Genesis balance after `send' is over both half_rep and PR threshold.
|
||||
// Process a vote with a key that is in the local wallet.
|
||||
auto vote = std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::milliseconds_since_epoch (), nano::vote::duration_max, std::vector<nano::block_hash>{ send->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote));
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote).at (send->hash ()));
|
||||
// Make sure the vote was processed.
|
||||
auto election (node.active.election (send->qualified_root ()));
|
||||
ASSERT_NE (nullptr, election);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/node/common.hpp>
|
||||
#include <nano/node/voting.hpp>
|
||||
#include <nano/node/local_vote_history.hpp>
|
||||
#include <nano/node/vote_generator.hpp>
|
||||
#include <nano/node/vote_spacing.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/test_common/system.hpp>
|
||||
#include <nano/test_common/testutil.hpp>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include <nano/crypto_lib/random_pool.hpp>
|
||||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/lib/thread_runner.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/store/lmdb/wallet_value.hpp>
|
||||
#include <nano/test_common/system.hpp>
|
||||
|
|
@ -1184,7 +1186,7 @@ TEST (wallet, search_receivable)
|
|||
wallet.insert_adhoc (nano::dev::genesis_key.prv);
|
||||
|
||||
// Pending search should create the receive block
|
||||
ASSERT_EQ (2, node.ledger.cache.block_count);
|
||||
ASSERT_EQ (2, node.ledger.block_count ());
|
||||
ASSERT_FALSE (wallet.search_receivable (wallet.wallets.tx_begin_read ()));
|
||||
ASSERT_TIMELY_EQ (3s, node.balance (nano::dev::genesis_key.pub), nano::dev::constants.genesis_amount);
|
||||
auto receive_hash = node.ledger.latest (node.store.tx_begin_read (), nano::dev::genesis_key.pub);
|
||||
|
|
@ -1218,12 +1220,12 @@ TEST (wallet, receive_pruned)
|
|||
auto send2 = wallet1.send_action (nano::dev::genesis_key.pub, key.pub, 1, 1);
|
||||
|
||||
// Pruning
|
||||
ASSERT_TIMELY_EQ (5s, node2.ledger.cache.cemented_count, 3);
|
||||
ASSERT_TIMELY_EQ (5s, node2.ledger.cemented_count (), 3);
|
||||
{
|
||||
auto transaction = node2.store.tx_begin_write ();
|
||||
ASSERT_EQ (1, node2.ledger.pruning_action (transaction, send1->hash (), 2));
|
||||
}
|
||||
ASSERT_EQ (1, node2.ledger.cache.pruned_count);
|
||||
ASSERT_EQ (1, node2.ledger.pruned_count ());
|
||||
ASSERT_TRUE (node2.ledger.block_or_pruned_exists (send1->hash ()));
|
||||
ASSERT_FALSE (node2.ledger.block_exists (node2.store.tx_begin_read (), send1->hash ()));
|
||||
|
||||
|
|
@ -1232,5 +1234,5 @@ TEST (wallet, receive_pruned)
|
|||
auto open1 = wallet2.receive_action (send1->hash (), key.pub, amount, send1->destination (), 1);
|
||||
ASSERT_NE (nullptr, open1);
|
||||
ASSERT_EQ (amount, node2.ledger.balance (node2.store.tx_begin_read (), open1->hash ()));
|
||||
ASSERT_TIMELY_EQ (5s, node2.ledger.cache.cemented_count, 4);
|
||||
ASSERT_TIMELY_EQ (5s, node2.ledger.cemented_count (), 4);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/store/versioning.hpp>
|
||||
#include <nano/test_common/system.hpp>
|
||||
|
|
@ -241,7 +243,7 @@ TEST (wallets, search_receivable)
|
|||
wallet->insert_adhoc (nano::dev::genesis_key.prv);
|
||||
|
||||
// Pending search should create the receive block
|
||||
ASSERT_EQ (2, node.ledger.cache.block_count);
|
||||
ASSERT_EQ (2, node.ledger.block_count ());
|
||||
if (search_all)
|
||||
{
|
||||
node.wallets.search_receivable_all ();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include <nano/core_test/fakes/websocket_client.hpp>
|
||||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/transport/fake.hpp>
|
||||
#include <nano/node/websocket.hpp>
|
||||
#include <nano/test_common/network.hpp>
|
||||
|
|
|
|||
|
|
@ -72,8 +72,6 @@ add_library(
|
|||
rate_limiting.hpp
|
||||
rate_limiting.cpp
|
||||
relaxed_atomic.hpp
|
||||
rep_weights.hpp
|
||||
rep_weights.cpp
|
||||
rocksdbconfig.hpp
|
||||
rocksdbconfig.cpp
|
||||
rpc_handler_interface.hpp
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ nano::block_hash nano::block::generate_hash () const
|
|||
blake2b_state hash_l;
|
||||
auto status (blake2b_init (&hash_l, sizeof (result.bytes)));
|
||||
debug_assert (status == 0);
|
||||
hash (hash_l);
|
||||
generate_hash (hash_l);
|
||||
status = blake2b_final (&hash_l, result.bytes.data (), sizeof (result.bytes));
|
||||
debug_assert (status == 0);
|
||||
return result;
|
||||
|
|
@ -162,6 +162,18 @@ bool nano::block::is_change () const noexcept
|
|||
}
|
||||
}
|
||||
|
||||
bool nano::block::is_epoch () const noexcept
|
||||
{
|
||||
release_assert (has_sideband ());
|
||||
switch (type ())
|
||||
{
|
||||
case nano::block_type::state:
|
||||
return sideband ().details.is_epoch;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
nano::block_hash const & nano::block::hash () const
|
||||
{
|
||||
if (!cached_hash.is_zero ())
|
||||
|
|
@ -347,7 +359,7 @@ void nano::send_block::visit (nano::mutable_block_visitor & visitor_a)
|
|||
visitor_a.send_block (*this);
|
||||
}
|
||||
|
||||
void nano::send_block::hash (blake2b_state & hash_a) const
|
||||
void nano::send_block::generate_hash (blake2b_state & hash_a) const
|
||||
{
|
||||
hashables.hash (hash_a);
|
||||
}
|
||||
|
|
@ -742,7 +754,7 @@ nano::open_block::open_block (bool & error_a, boost::property_tree::ptree const
|
|||
}
|
||||
}
|
||||
|
||||
void nano::open_block::hash (blake2b_state & hash_a) const
|
||||
void nano::open_block::generate_hash (blake2b_state & hash_a) const
|
||||
{
|
||||
hashables.hash (hash_a);
|
||||
}
|
||||
|
|
@ -1013,7 +1025,7 @@ nano::change_block::change_block (bool & error_a, boost::property_tree::ptree co
|
|||
}
|
||||
}
|
||||
|
||||
void nano::change_block::hash (blake2b_state & hash_a) const
|
||||
void nano::change_block::generate_hash (blake2b_state & hash_a) const
|
||||
{
|
||||
hashables.hash (hash_a);
|
||||
}
|
||||
|
|
@ -1310,7 +1322,7 @@ nano::state_block::state_block (bool & error_a, boost::property_tree::ptree cons
|
|||
}
|
||||
}
|
||||
|
||||
void nano::state_block::hash (blake2b_state & hash_a) const
|
||||
void nano::state_block::generate_hash (blake2b_state & hash_a) const
|
||||
{
|
||||
nano::uint256_union preamble (static_cast<uint64_t> (nano::block_type::state));
|
||||
blake2b_update (&hash_a, preamble.bytes.data (), preamble.bytes.size ());
|
||||
|
|
@ -1776,7 +1788,7 @@ nano::receive_block::receive_block (bool & error_a, boost::property_tree::ptree
|
|||
}
|
||||
}
|
||||
|
||||
void nano::receive_block::hash (blake2b_state & hash_a) const
|
||||
void nano::receive_block::generate_hash (blake2b_state & hash_a) const
|
||||
{
|
||||
hashables.hash (hash_a);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ public:
|
|||
void sideband_set (nano::block_sideband const &);
|
||||
bool has_sideband () const;
|
||||
std::string to_json () const;
|
||||
virtual void hash (blake2b_state &) const = 0;
|
||||
virtual uint64_t block_work () const = 0;
|
||||
virtual void block_work_set (uint64_t) = 0;
|
||||
// Previous block or account number for open blocks
|
||||
|
|
@ -55,6 +54,7 @@ public:
|
|||
bool is_send () const noexcept;
|
||||
bool is_receive () const noexcept;
|
||||
bool is_change () const noexcept;
|
||||
bool is_epoch () const noexcept;
|
||||
|
||||
public: // Direct access to the block fields or nullopt if the block type does not have the specified field
|
||||
// Returns account field or account from sideband
|
||||
|
|
@ -83,6 +83,7 @@ public: // Direct access to the block fields or nullopt if the block type does n
|
|||
virtual std::optional<nano::block_hash> source_field () const;
|
||||
|
||||
protected:
|
||||
virtual void generate_hash (blake2b_state &) const = 0;
|
||||
mutable nano::block_hash cached_hash{ 0 };
|
||||
/**
|
||||
* Contextual details about a block, some fields may or may not be set depending on block type.
|
||||
|
|
@ -120,8 +121,6 @@ public:
|
|||
send_block (bool &, nano::stream &);
|
||||
send_block (bool &, boost::property_tree::ptree const &);
|
||||
virtual ~send_block () = default;
|
||||
using nano::block::hash;
|
||||
void hash (blake2b_state &) const override;
|
||||
uint64_t block_work () const override;
|
||||
void block_work_set (uint64_t) override;
|
||||
nano::root const & root () const override;
|
||||
|
|
@ -150,6 +149,9 @@ public: // Send block fields
|
|||
|
||||
public: // Logging
|
||||
void operator() (nano::object_stream &) const override;
|
||||
|
||||
protected:
|
||||
void generate_hash (blake2b_state &) const override;
|
||||
};
|
||||
|
||||
class receive_hashables
|
||||
|
|
@ -173,8 +175,6 @@ public:
|
|||
receive_block (bool &, nano::stream &);
|
||||
receive_block (bool &, boost::property_tree::ptree const &);
|
||||
virtual ~receive_block () = default;
|
||||
using nano::block::hash;
|
||||
void hash (blake2b_state &) const override;
|
||||
uint64_t block_work () const override;
|
||||
void block_work_set (uint64_t) override;
|
||||
nano::root const & root () const override;
|
||||
|
|
@ -202,6 +202,9 @@ public: // Receive block fields
|
|||
|
||||
public: // Logging
|
||||
void operator() (nano::object_stream &) const override;
|
||||
|
||||
protected:
|
||||
void generate_hash (blake2b_state &) const override;
|
||||
};
|
||||
|
||||
class open_hashables
|
||||
|
|
@ -227,8 +230,6 @@ public:
|
|||
open_block (bool &, nano::stream &);
|
||||
open_block (bool &, boost::property_tree::ptree const &);
|
||||
virtual ~open_block () = default;
|
||||
using nano::block::hash;
|
||||
void hash (blake2b_state &) const override;
|
||||
uint64_t block_work () const override;
|
||||
void block_work_set (uint64_t) override;
|
||||
nano::root const & root () const override;
|
||||
|
|
@ -258,6 +259,9 @@ public: // Open block fields
|
|||
|
||||
public: // Logging
|
||||
void operator() (nano::object_stream &) const override;
|
||||
|
||||
protected:
|
||||
void generate_hash (blake2b_state &) const override;
|
||||
};
|
||||
|
||||
class change_hashables
|
||||
|
|
@ -281,8 +285,6 @@ public:
|
|||
change_block (bool &, nano::stream &);
|
||||
change_block (bool &, boost::property_tree::ptree const &);
|
||||
virtual ~change_block () = default;
|
||||
using nano::block::hash;
|
||||
void hash (blake2b_state &) const override;
|
||||
uint64_t block_work () const override;
|
||||
void block_work_set (uint64_t) override;
|
||||
nano::root const & root () const override;
|
||||
|
|
@ -310,6 +312,9 @@ public: // Change block fields
|
|||
|
||||
public: // Logging
|
||||
void operator() (nano::object_stream &) const override;
|
||||
|
||||
protected:
|
||||
void generate_hash (blake2b_state &) const override;
|
||||
};
|
||||
|
||||
class state_hashables
|
||||
|
|
@ -346,8 +351,6 @@ public:
|
|||
state_block (bool &, nano::stream &);
|
||||
state_block (bool &, boost::property_tree::ptree const &);
|
||||
virtual ~state_block () = default;
|
||||
using nano::block::hash;
|
||||
void hash (blake2b_state &) const override;
|
||||
uint64_t block_work () const override;
|
||||
void block_work_set (uint64_t) override;
|
||||
nano::root const & root () const override;
|
||||
|
|
@ -378,6 +381,9 @@ public: // State block fields
|
|||
|
||||
public: // Logging
|
||||
void operator() (nano::object_stream &) const override;
|
||||
|
||||
protected:
|
||||
void generate_hash (blake2b_state &) const override;
|
||||
};
|
||||
|
||||
class block_visitor
|
||||
|
|
|
|||
|
|
@ -257,8 +257,6 @@ char const * nano::mutex_identifier (mutexes mutex)
|
|||
return "block_uniquer";
|
||||
case mutexes::blockstore_cache:
|
||||
return "blockstore_cache";
|
||||
case mutexes::confirmation_height_processor:
|
||||
return "confirmation_height_processor";
|
||||
case mutexes::election_winner_details:
|
||||
return "election_winner_details";
|
||||
case mutexes::gap_cache:
|
||||
|
|
@ -286,4 +284,4 @@ char const * nano::mutex_identifier (mutexes mutex)
|
|||
}
|
||||
|
||||
throw std::runtime_error ("Invalid mutexes enum specified");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ enum class mutexes
|
|||
block_processor,
|
||||
block_uniquer,
|
||||
blockstore_cache,
|
||||
confirmation_height_processor,
|
||||
election_winner_details,
|
||||
gap_cache,
|
||||
network_filter,
|
||||
|
|
|
|||
|
|
@ -32,12 +32,58 @@ using logger_id = std::pair<nano::log::type, nano::log::detail>;
|
|||
|
||||
std::string to_string (logger_id);
|
||||
logger_id parse_logger_id (std::string const &);
|
||||
}
|
||||
|
||||
// Time helpers
|
||||
namespace nano::log
|
||||
{
|
||||
template <class Clock>
|
||||
auto microseconds (std::chrono::time_point<Clock> time)
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::microseconds> (time.time_since_epoch ()).count ();
|
||||
}
|
||||
|
||||
template <class Duration>
|
||||
auto microseconds (Duration duration)
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::microseconds> (duration).count ();
|
||||
}
|
||||
|
||||
template <class Clock>
|
||||
auto milliseconds (std::chrono::time_point<Clock> time)
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds> (time.time_since_epoch ()).count ();
|
||||
}
|
||||
|
||||
template <class Duration>
|
||||
auto milliseconds (Duration duration)
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds> (duration).count ();
|
||||
}
|
||||
|
||||
template <class Clock>
|
||||
auto seconds (std::chrono::time_point<Clock> time)
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::seconds> (time.time_since_epoch ()).count ();
|
||||
}
|
||||
|
||||
template <class Duration>
|
||||
auto seconds (Duration duration)
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::seconds> (duration).count ();
|
||||
}
|
||||
|
||||
template <class Clock>
|
||||
auto milliseconds_delta (std::chrono::time_point<Clock> time, std::chrono::time_point<Clock> now = Clock::now ())
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds> (now - time).count ();
|
||||
}
|
||||
|
||||
template <class Clock>
|
||||
auto seconds_delta (std::chrono::time_point<Clock> time, std::chrono::time_point<Clock> now = Clock::now ())
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::seconds> (now - time).count ();
|
||||
}
|
||||
}
|
||||
|
||||
namespace nano
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ enum class type
|
|||
tcp,
|
||||
tcp_server,
|
||||
tcp_listener,
|
||||
tcp_channels,
|
||||
prunning,
|
||||
conf_processor_bounded,
|
||||
conf_processor_unbounded,
|
||||
|
|
@ -72,6 +73,8 @@ enum class type
|
|||
election_scheduler,
|
||||
vote_generator,
|
||||
rep_tiers,
|
||||
syn_cookies,
|
||||
thread_runner,
|
||||
|
||||
// bootstrap
|
||||
bulk_pull_client,
|
||||
|
|
|
|||
|
|
@ -389,4 +389,12 @@ struct hash<std::reference_wrapper<::nano::block_hash const>>
|
|||
return hash (hash_a);
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct hash<::nano::root>
|
||||
{
|
||||
size_t operator() (::nano::root const & value_a) const
|
||||
{
|
||||
return std::hash<::nano::root> () (value_a);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@ public:
|
|||
|
||||
array_stream (array_stream const &) = delete; // Disallow copying
|
||||
|
||||
private:
|
||||
public:
|
||||
template <class Value>
|
||||
void write_single (Value const & value)
|
||||
{
|
||||
|
|
@ -290,7 +290,6 @@ private:
|
|||
ctx.end_array_element ();
|
||||
}
|
||||
|
||||
public:
|
||||
// Handle `.write (container)`
|
||||
template <class Container>
|
||||
void write (Container const & container)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/object_stream.hpp>
|
||||
#include <nano/lib/utility.hpp>
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
|
@ -9,42 +10,64 @@
|
|||
|
||||
namespace nano
|
||||
{
|
||||
template <class Streamable>
|
||||
template <class Streamable, class Writer>
|
||||
struct object_stream_formatter
|
||||
{
|
||||
nano::object_stream_config const & config;
|
||||
Streamable const & value;
|
||||
Writer writer;
|
||||
|
||||
explicit object_stream_formatter (Streamable const & value, nano::object_stream_config const & config) :
|
||||
explicit object_stream_formatter (Streamable const & value, Writer writer, nano::object_stream_config const & config) :
|
||||
config{ config },
|
||||
value{ value }
|
||||
value{ value },
|
||||
writer{ writer }
|
||||
{
|
||||
}
|
||||
|
||||
friend std::ostream & operator<< (std::ostream & os, object_stream_formatter<Streamable> const & self)
|
||||
friend std::ostream & operator<< (std::ostream & os, object_stream_formatter<Streamable, Writer> const & self)
|
||||
{
|
||||
nano::root_object_stream obs{ os, self.config };
|
||||
obs.write (self.value);
|
||||
self.writer (self.value, obs);
|
||||
return os;
|
||||
}
|
||||
|
||||
// Needed for fmt formatting, uses the ostream operator under the hood
|
||||
friend auto format_as (object_stream_formatter<Streamable> const & val)
|
||||
friend auto format_as (object_stream_formatter<Streamable, Writer> const & self)
|
||||
{
|
||||
return fmt::streamed (val);
|
||||
return fmt::streamed (self);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Streamable>
|
||||
auto streamed (Streamable const & value)
|
||||
enum class streamed_format
|
||||
{
|
||||
return object_stream_formatter{ value, nano::object_stream_config::default_config () };
|
||||
basic,
|
||||
json
|
||||
};
|
||||
|
||||
inline nano::object_stream_config const & to_object_stream_config (streamed_format format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case streamed_format::basic:
|
||||
return nano::object_stream_config::default_config ();
|
||||
case streamed_format::json:
|
||||
return nano::object_stream_config::json_config ();
|
||||
default:
|
||||
debug_assert (false);
|
||||
return nano::object_stream_config::default_config ();
|
||||
}
|
||||
}
|
||||
|
||||
template <class Streamable>
|
||||
auto streamed_as_json (Streamable const & value)
|
||||
auto streamed (Streamable const & value, streamed_format format = streamed_format::basic)
|
||||
{
|
||||
return object_stream_formatter{ value, nano::object_stream_config::json_config () };
|
||||
return object_stream_formatter{ value, [] (auto const & value, nano::root_object_stream & obs) { obs.write (value); }, to_object_stream_config (format) };
|
||||
}
|
||||
|
||||
template <class StreamableRange>
|
||||
auto streamed_range (StreamableRange const & value, streamed_format format = streamed_format::basic)
|
||||
{
|
||||
return object_stream_formatter{ value, [] (auto const & value, nano::root_object_stream & obs) { obs.write_range (value); }, to_object_stream_config (format) };
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -109,7 +132,7 @@ template <nano::object_or_array_streamable Value>
|
|||
std::string to_json (Value const & value)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << nano::streamed_as_json (value);
|
||||
ss << nano::streamed (value, nano::streamed_format::json);
|
||||
return ss.str ();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ public:
|
|||
}
|
||||
|
||||
public: // Container info
|
||||
std::unique_ptr<container_info_component> collect_container_info (std::string const & name)
|
||||
std::unique_ptr<container_info_component> collect_container_info (std::string const & name) const
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ mutex };
|
||||
|
||||
|
|
|
|||
31
nano/lib/random.hpp
Normal file
31
nano/lib/random.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
/**
|
||||
* Not safe for any crypto related code, use for non-crypto PRNG only.
|
||||
*/
|
||||
class random_generator final
|
||||
{
|
||||
public:
|
||||
/// Generate a random number in the range [min, max)
|
||||
auto random (auto min, auto max)
|
||||
{
|
||||
release_assert (min < max);
|
||||
std::uniform_int_distribution<decltype (min)> dist (min, max - 1);
|
||||
return dist (rng);
|
||||
}
|
||||
|
||||
/// Generate a random number in the range [0, max)
|
||||
auto random (auto max)
|
||||
{
|
||||
return random (decltype (max){ 0 }, max);
|
||||
}
|
||||
|
||||
private:
|
||||
std::random_device device;
|
||||
std::default_random_engine rng{ device () };
|
||||
};
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
#include <nano/lib/rep_weights.hpp>
|
||||
#include <nano/store/component.hpp>
|
||||
|
||||
void nano::rep_weights::representation_add (nano::account const & source_rep_a, nano::uint128_t const & amount_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (mutex);
|
||||
auto source_previous (get (source_rep_a));
|
||||
put (source_rep_a, source_previous + amount_a);
|
||||
}
|
||||
|
||||
void nano::rep_weights::representation_add_dual (nano::account const & source_rep_1, nano::uint128_t const & amount_1, nano::account const & source_rep_2, nano::uint128_t const & amount_2)
|
||||
{
|
||||
if (source_rep_1 != source_rep_2)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (mutex);
|
||||
auto source_previous_1 (get (source_rep_1));
|
||||
put (source_rep_1, source_previous_1 + amount_1);
|
||||
auto source_previous_2 (get (source_rep_2));
|
||||
put (source_rep_2, source_previous_2 + amount_2);
|
||||
}
|
||||
else
|
||||
{
|
||||
representation_add (source_rep_1, amount_1 + amount_2);
|
||||
}
|
||||
}
|
||||
|
||||
void nano::rep_weights::representation_put (nano::account const & account_a, nano::uint128_union const & representation_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (mutex);
|
||||
put (account_a, representation_a);
|
||||
}
|
||||
|
||||
nano::uint128_t nano::rep_weights::representation_get (nano::account const & account_a) const
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lk (mutex);
|
||||
return get (account_a);
|
||||
}
|
||||
|
||||
/** Makes a copy */
|
||||
std::unordered_map<nano::account, nano::uint128_t> nano::rep_weights::get_rep_amounts () const
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (mutex);
|
||||
return rep_amounts;
|
||||
}
|
||||
|
||||
void nano::rep_weights::copy_from (nano::rep_weights & other_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard_this (mutex);
|
||||
nano::lock_guard<nano::mutex> guard_other (other_a.mutex);
|
||||
for (auto const & entry : other_a.rep_amounts)
|
||||
{
|
||||
auto prev_amount (get (entry.first));
|
||||
put (entry.first, prev_amount + entry.second);
|
||||
}
|
||||
}
|
||||
|
||||
void nano::rep_weights::put (nano::account const & account_a, nano::uint128_union const & representation_a)
|
||||
{
|
||||
auto it = rep_amounts.find (account_a);
|
||||
auto amount = representation_a.number ();
|
||||
if (it != rep_amounts.end ())
|
||||
{
|
||||
it->second = amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
rep_amounts.emplace (account_a, amount);
|
||||
}
|
||||
}
|
||||
|
||||
nano::uint128_t nano::rep_weights::get (nano::account const & account_a) const
|
||||
{
|
||||
auto it = rep_amounts.find (account_a);
|
||||
if (it != rep_amounts.end ())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nano::uint128_t{ 0 };
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<nano::container_info_component> nano::collect_container_info (nano::rep_weights const & rep_weights, std::string const & name)
|
||||
{
|
||||
size_t rep_amounts_count;
|
||||
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (rep_weights.mutex);
|
||||
rep_amounts_count = rep_weights.rep_amounts.size ();
|
||||
}
|
||||
auto sizeof_element = sizeof (decltype (rep_weights.rep_amounts)::value_type);
|
||||
auto composite = std::make_unique<nano::container_info_composite> (name);
|
||||
composite->add_component (std::make_unique<nano::container_info_leaf> (container_info{ "rep_amounts", rep_amounts_count, sizeof_element }));
|
||||
return composite;
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/numbers.hpp>
|
||||
#include <nano/lib/utility.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
namespace store
|
||||
{
|
||||
class component;
|
||||
}
|
||||
|
||||
class rep_weights
|
||||
{
|
||||
public:
|
||||
void representation_add (nano::account const & source_rep_a, nano::uint128_t const & amount_a);
|
||||
void representation_add_dual (nano::account const & source_rep_1, nano::uint128_t const & amount_1, nano::account const & source_rep_2, nano::uint128_t const & amount_2);
|
||||
nano::uint128_t representation_get (nano::account const & account_a) const;
|
||||
void representation_put (nano::account const & account_a, nano::uint128_union const & representation_a);
|
||||
std::unordered_map<nano::account, nano::uint128_t> get_rep_amounts () const;
|
||||
void copy_from (rep_weights & other_a);
|
||||
|
||||
private:
|
||||
mutable nano::mutex mutex;
|
||||
std::unordered_map<nano::account, nano::uint128_t> rep_amounts;
|
||||
void put (nano::account const & account_a, nano::uint128_union const & representation_a);
|
||||
nano::uint128_t get (nano::account const & account_a) const;
|
||||
|
||||
friend std::unique_ptr<container_info_component> collect_container_info (rep_weights const &, std::string const &);
|
||||
};
|
||||
|
||||
std::unique_ptr<container_info_component> collect_container_info (rep_weights const &, std::string const &);
|
||||
}
|
||||
|
|
@ -17,12 +17,14 @@ enum class type : uint8_t
|
|||
ledger,
|
||||
rollback,
|
||||
bootstrap,
|
||||
network,
|
||||
tcp_server,
|
||||
vote,
|
||||
election,
|
||||
http_callback,
|
||||
ipc,
|
||||
tcp,
|
||||
tcp_channels,
|
||||
channel,
|
||||
socket,
|
||||
confirmation_height,
|
||||
|
|
@ -53,6 +55,7 @@ enum class type : uint8_t
|
|||
rep_crawler,
|
||||
local_block_broadcaster,
|
||||
rep_tiers,
|
||||
syn_cookies,
|
||||
|
||||
bootstrap_ascending,
|
||||
bootstrap_ascending_accounts,
|
||||
|
|
@ -68,6 +71,7 @@ enum class detail : uint8_t
|
|||
// common
|
||||
ok,
|
||||
loop,
|
||||
loop_cleanup,
|
||||
total,
|
||||
process,
|
||||
processed,
|
||||
|
|
@ -81,6 +85,7 @@ enum class detail : uint8_t
|
|||
none,
|
||||
success,
|
||||
unknown,
|
||||
cache,
|
||||
queue_overflow,
|
||||
|
||||
// processing queue
|
||||
|
|
@ -165,12 +170,15 @@ enum class detail : uint8_t
|
|||
frontier_confirmation_failed,
|
||||
error_socket_close,
|
||||
|
||||
// vote specific
|
||||
vote_valid,
|
||||
vote_replay,
|
||||
vote_indeterminate,
|
||||
vote_invalid,
|
||||
// vote result
|
||||
vote,
|
||||
valid,
|
||||
replay,
|
||||
indeterminate,
|
||||
|
||||
// vote processor
|
||||
vote_overflow,
|
||||
vote_ignored,
|
||||
|
||||
// election specific
|
||||
vote_new,
|
||||
|
|
@ -214,6 +222,11 @@ enum class detail : uint8_t
|
|||
message_size_too_big,
|
||||
outdated_version,
|
||||
|
||||
// network
|
||||
loop_keepalive,
|
||||
loop_reachout,
|
||||
merge_peer,
|
||||
|
||||
// tcp
|
||||
tcp_accept_success,
|
||||
tcp_accept_failure,
|
||||
|
|
@ -228,6 +241,15 @@ enum class detail : uint8_t
|
|||
tcp_read_error,
|
||||
tcp_write_error,
|
||||
|
||||
// tcp_server
|
||||
handshake,
|
||||
handshake_abort,
|
||||
handshake_error,
|
||||
handshake_network_error,
|
||||
handshake_initiate,
|
||||
handshake_response,
|
||||
handshake_response_invalid,
|
||||
|
||||
// ipc
|
||||
invocations,
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
#include <nano/lib/thread_roles.hpp>
|
||||
#include <nano/lib/utility.hpp>
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
std::string_view nano::thread_role::to_string (nano::thread_role::name name)
|
||||
{
|
||||
return magic_enum::enum_name (name);
|
||||
}
|
||||
|
||||
std::string nano::thread_role::get_string (nano::thread_role::name role)
|
||||
{
|
||||
std::string thread_role_name_string;
|
||||
|
|
@ -109,6 +116,15 @@ std::string nano::thread_role::get_string (nano::thread_role::name role)
|
|||
case nano::thread_role::name::rep_tiers:
|
||||
thread_role_name_string = "Rep tiers";
|
||||
break;
|
||||
case nano::thread_role::name::network_cleanup:
|
||||
thread_role_name_string = "Net cleanup";
|
||||
break;
|
||||
case nano::thread_role::name::network_keepalive:
|
||||
thread_role_name_string = "Net keepalive";
|
||||
break;
|
||||
case nano::thread_role::name::network_reachout:
|
||||
thread_role_name_string = "Net reachout";
|
||||
break;
|
||||
default:
|
||||
debug_assert (false && "nano::thread_role::get_string unhandled thread role");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,8 +45,13 @@ enum class name
|
|||
rep_crawler,
|
||||
local_block_broadcasting,
|
||||
rep_tiers,
|
||||
network_cleanup,
|
||||
network_keepalive,
|
||||
network_reachout,
|
||||
};
|
||||
|
||||
std::string_view to_string (name);
|
||||
|
||||
/*
|
||||
* Get/Set the identifier for the current thread
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -10,17 +10,22 @@
|
|||
* thread_runner
|
||||
*/
|
||||
|
||||
nano::thread_runner::thread_runner (boost::asio::io_context & io_ctx_a, unsigned num_threads, const nano::thread_role::name thread_role_a) :
|
||||
io_guard{ boost::asio::make_work_guard (io_ctx_a) },
|
||||
nano::thread_runner::thread_runner (std::shared_ptr<boost::asio::io_context> io_ctx_a, unsigned num_threads, const nano::thread_role::name thread_role_a) :
|
||||
io_ctx{ io_ctx_a },
|
||||
io_guard{ boost::asio::make_work_guard (*io_ctx_a) },
|
||||
role{ thread_role_a }
|
||||
{
|
||||
debug_assert (io_ctx != nullptr);
|
||||
|
||||
for (auto i (0u); i < num_threads; ++i)
|
||||
{
|
||||
threads.emplace_back (nano::thread_attributes::get_default (), [this, &io_ctx_a] () {
|
||||
threads.emplace_back (nano::thread_attributes::get_default (), [this, i] () {
|
||||
nano::thread_role::set (role);
|
||||
try
|
||||
{
|
||||
run (io_ctx_a);
|
||||
logger.debug (nano::log::type::thread_runner, "Thread #{} ({}) started", i, to_string (role));
|
||||
run (*io_ctx);
|
||||
logger.debug (nano::log::type::thread_runner, "Thread #{} ({}) stopped", i, to_string (role));
|
||||
}
|
||||
catch (std::exception const & ex)
|
||||
{
|
||||
|
|
@ -78,6 +83,7 @@ void nano::thread_runner::join ()
|
|||
i.join ();
|
||||
}
|
||||
}
|
||||
io_ctx.reset ();
|
||||
}
|
||||
|
||||
void nano::thread_runner::stop_event_processing ()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include <nano/boost/asio/deadline_timer.hpp>
|
||||
#include <nano/boost/asio/executor_work_guard.hpp>
|
||||
#include <nano/boost/asio/io_context.hpp>
|
||||
#include <nano/lib/logging.hpp>
|
||||
#include <nano/lib/thread_roles.hpp>
|
||||
#include <nano/lib/threading.hpp>
|
||||
|
||||
|
|
@ -11,19 +12,23 @@ namespace nano
|
|||
{
|
||||
class thread_runner final
|
||||
{
|
||||
nano::logger logger;
|
||||
|
||||
public:
|
||||
thread_runner (boost::asio::io_context &, unsigned num_threads, nano::thread_role::name thread_role = nano::thread_role::name::io);
|
||||
thread_runner (std::shared_ptr<boost::asio::io_context>, unsigned num_threads = nano::hardware_concurrency (), nano::thread_role::name thread_role = nano::thread_role::name::io);
|
||||
~thread_runner ();
|
||||
|
||||
/** Tells the IO context to stop processing events.*/
|
||||
void stop_event_processing ();
|
||||
|
||||
/** Wait for IO threads to complete */
|
||||
void join ();
|
||||
|
||||
private:
|
||||
std::shared_ptr<boost::asio::io_context> io_ctx;
|
||||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> io_guard;
|
||||
nano::thread_role::name const role;
|
||||
std::vector<boost::thread> threads;
|
||||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> io_guard;
|
||||
|
||||
private:
|
||||
void run (boost::asio::io_context &);
|
||||
|
|
|
|||
|
|
@ -592,7 +592,8 @@ int main (int argc, char * const * argv)
|
|||
std::this_thread::sleep_for (std::chrono::seconds (7));
|
||||
std::cout << "Connecting nodes..." << std::endl;
|
||||
|
||||
boost::asio::io_context ioc;
|
||||
std::shared_ptr<boost::asio::io_context> ioc_shared = std::make_shared<boost::asio::io_context> ();
|
||||
boost::asio::io_context & ioc{ *ioc_shared };
|
||||
|
||||
debug_assert (!nano::signal_handler_impl);
|
||||
nano::signal_handler_impl = [&ioc] () {
|
||||
|
|
@ -715,7 +716,8 @@ int main (int argc, char * const * argv)
|
|||
// Stop main node
|
||||
stop_rpc (ioc, primary_node_results);
|
||||
});
|
||||
nano::thread_runner runner (ioc, simultaneous_process_calls);
|
||||
|
||||
nano::thread_runner runner (ioc_shared, simultaneous_process_calls);
|
||||
t.join ();
|
||||
runner.join ();
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,8 @@ void nano::daemon::run (std::filesystem::path const & data_path, nano::node_flag
|
|||
config.node.websocket_config.tls_config = tls_config;
|
||||
}
|
||||
|
||||
boost::asio::io_context io_ctx;
|
||||
std::shared_ptr<boost::asio::io_context> io_ctx = std::make_shared<boost::asio::io_context> ();
|
||||
|
||||
auto opencl = nano::opencl_work::create (config.opencl_enable, config.opencl, logger, config.node.network_params.work);
|
||||
nano::opencl_work_func_t opencl_work_func;
|
||||
if (opencl)
|
||||
|
|
@ -132,7 +133,7 @@ void nano::daemon::run (std::filesystem::path const & data_path, nano::node_flag
|
|||
config.node.peering_port = network_params.network.default_node_port;
|
||||
}
|
||||
|
||||
auto node (std::make_shared<nano::node> (io_ctx, data_path, config.node, opencl_work, flags));
|
||||
auto node = std::make_shared<nano::node> (io_ctx, data_path, config.node, opencl_work, flags);
|
||||
if (!node->init_error ())
|
||||
{
|
||||
auto network_label = node->network_params.network.get_current_network_as_string ();
|
||||
|
|
@ -149,7 +150,7 @@ void nano::daemon::run (std::filesystem::path const & data_path, nano::node_flag
|
|||
|
||||
nano::ipc::ipc_server ipc_server (*node, config.rpc);
|
||||
std::unique_ptr<boost::process::child> rpc_process;
|
||||
std::unique_ptr<nano::rpc> rpc;
|
||||
std::shared_ptr<nano::rpc> rpc;
|
||||
std::unique_ptr<nano::rpc_handler_interface> rpc_handler;
|
||||
if (config.rpc_enable)
|
||||
{
|
||||
|
|
@ -165,10 +166,14 @@ void nano::daemon::run (std::filesystem::path const & data_path, nano::node_flag
|
|||
}
|
||||
|
||||
rpc_config.tls_config = tls_config;
|
||||
rpc_handler = std::make_unique<nano::inprocess_rpc_handler> (*node, ipc_server, config.rpc, [&ipc_server, &workers = node->workers, &io_ctx] () {
|
||||
rpc_handler = std::make_unique<nano::inprocess_rpc_handler> (*node, ipc_server, config.rpc,
|
||||
[&ipc_server, &workers = node->workers, io_ctx_w = std::weak_ptr{ io_ctx }] () {
|
||||
ipc_server.stop ();
|
||||
workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (3), [&io_ctx] () {
|
||||
io_ctx.stop ();
|
||||
workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (3), [io_ctx_w] () {
|
||||
if (auto io_ctx_l = io_ctx_w.lock ())
|
||||
{
|
||||
io_ctx_l->stop ();
|
||||
}
|
||||
});
|
||||
});
|
||||
rpc = nano::get_rpc (io_ctx, rpc_config, *rpc_handler);
|
||||
|
|
@ -189,10 +194,13 @@ void nano::daemon::run (std::filesystem::path const & data_path, nano::node_flag
|
|||
}
|
||||
|
||||
debug_assert (!nano::signal_handler_impl);
|
||||
nano::signal_handler_impl = [this, &io_ctx] () {
|
||||
nano::signal_handler_impl = [this, io_ctx_w = std::weak_ptr{ io_ctx }] () {
|
||||
logger.warn (nano::log::type::daemon, "Interrupt signal received, stopping...");
|
||||
|
||||
io_ctx.stop ();
|
||||
if (auto io_ctx_l = io_ctx_w.lock ())
|
||||
{
|
||||
io_ctx_l->stop ();
|
||||
}
|
||||
sig_int_or_term = 1;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
#include <nano/lib/thread_runner.hpp>
|
||||
#include <nano/lib/utility.hpp>
|
||||
#include <nano/nano_node/daemon.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/cli.hpp>
|
||||
#include <nano/node/confirming_set.hpp>
|
||||
#include <nano/node/daemonconfig.hpp>
|
||||
#include <nano/node/ipc/ipc_server.hpp>
|
||||
#include <nano/node/json_handler.hpp>
|
||||
|
|
@ -167,7 +169,7 @@ int main (int argc, char * const * argv)
|
|||
auto const & hardcoded = bootstrap_weights.second;
|
||||
auto const hardcoded_height = bootstrap_weights.first;
|
||||
auto const ledger_unfiltered = node->ledger.cache.rep_weights.get_rep_amounts ();
|
||||
auto const ledger_height = node->ledger.cache.block_count.load ();
|
||||
auto const ledger_height = node->ledger.block_count ();
|
||||
|
||||
auto get_total = [] (decltype (bootstrap_weights.second) const & reps) -> nano::uint128_union {
|
||||
return std::accumulate (reps.begin (), reps.end (), nano::uint128_t{ 0 }, [] (auto sum, auto const & rep) { return sum + rep.second; });
|
||||
|
|
@ -325,7 +327,7 @@ int main (int argc, char * const * argv)
|
|||
node_flags.generate_cache.block_count = true;
|
||||
nano::inactive_node inactive_node (data_path, node_flags);
|
||||
auto node = inactive_node.node;
|
||||
std::cout << boost::str (boost::format ("Block count: %1%\n") % node->ledger.cache.block_count);
|
||||
std::cout << boost::str (boost::format ("Block count: %1%\n") % node->ledger.block_count ());
|
||||
}
|
||||
else if (vm.count ("debug_bootstrap_generate"))
|
||||
{
|
||||
|
|
@ -446,7 +448,7 @@ int main (int argc, char * const * argv)
|
|||
nano::update_flags (node_flags, vm);
|
||||
node_flags.generate_cache.account_count = true;
|
||||
nano::inactive_node inactive_node (data_path, node_flags);
|
||||
std::cout << boost::str (boost::format ("Frontier count: %1%\n") % inactive_node.node->ledger.cache.account_count);
|
||||
std::cout << boost::str (boost::format ("Frontier count: %1%\n") % inactive_node.node->ledger.account_count ());
|
||||
}
|
||||
else if (vm.count ("debug_profile_kdf"))
|
||||
{
|
||||
|
|
@ -985,14 +987,14 @@ int main (int argc, char * const * argv)
|
|||
blocks.pop_front ();
|
||||
}
|
||||
nano::timer<std::chrono::seconds> timer_l (nano::timer_state::started);
|
||||
while (node->ledger.cache.block_count != max_blocks + 1)
|
||||
while (node->ledger.block_count () != max_blocks + 1)
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::milliseconds (10));
|
||||
// Message each 15 seconds
|
||||
if (timer_l.after_deadline (std::chrono::seconds (15)))
|
||||
{
|
||||
timer_l.restart ();
|
||||
std::cout << boost::str (boost::format ("%1% (%2%) blocks processed (unchecked), %3% remaining") % node->ledger.cache.block_count % node->unchecked.count () % node->block_processor.size ()) << std::endl;
|
||||
std::cout << boost::str (boost::format ("%1% (%2%) blocks processed (unchecked), %3% remaining") % node->ledger.block_count () % node->unchecked.count () % node->block_processor.size ()) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1000,7 +1002,7 @@ int main (int argc, char * const * argv)
|
|||
auto time (std::chrono::duration_cast<std::chrono::microseconds> (end - begin).count ());
|
||||
node->stop ();
|
||||
std::cout << boost::str (boost::format ("%|1$ 12d| us \n%2% blocks per second\n") % time % (max_blocks * 1000000 / time));
|
||||
release_assert (node->ledger.cache.block_count == max_blocks + 1);
|
||||
release_assert (node->ledger.block_count () == max_blocks + 1);
|
||||
}
|
||||
else if (vm.count ("debug_profile_votes"))
|
||||
{
|
||||
|
|
@ -1129,8 +1131,8 @@ int main (int argc, char * const * argv)
|
|||
}
|
||||
}
|
||||
std::cout << boost::str (boost::format ("Starting generating %1% blocks...\n") % (count * 2));
|
||||
boost::asio::io_context io_ctx1;
|
||||
boost::asio::io_context io_ctx2;
|
||||
auto io_ctx1 = std::make_shared<boost::asio::io_context> ();
|
||||
auto io_ctx2 = std::make_shared<boost::asio::io_context> ();
|
||||
nano::work_pool work{ network_params.network, std::numeric_limits<unsigned>::max () };
|
||||
auto path1 (nano::unique_path ());
|
||||
auto path2 (nano::unique_path ());
|
||||
|
|
@ -1200,25 +1202,25 @@ int main (int argc, char * const * argv)
|
|||
node1->block_processor.add (block);
|
||||
}
|
||||
auto iteration (0);
|
||||
while (node1->ledger.cache.block_count != count * 2 + 1)
|
||||
while (node1->ledger.block_count () != count * 2 + 1)
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::milliseconds (500));
|
||||
if (++iteration % 60 == 0)
|
||||
{
|
||||
std::cout << boost::str (boost::format ("%1% blocks processed\n") % node1->ledger.cache.block_count);
|
||||
std::cout << boost::str (boost::format ("%1% blocks processed\n") % node1->ledger.block_count ());
|
||||
}
|
||||
}
|
||||
// Confirm blocks for node1
|
||||
for (auto & block : blocks)
|
||||
{
|
||||
node1->confirmation_height_processor.add (block);
|
||||
node1->confirming_set.add (block->hash ());
|
||||
}
|
||||
while (node1->ledger.cache.cemented_count != node1->ledger.cache.block_count)
|
||||
while (node1->ledger.cemented_count () != node1->ledger.block_count ())
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::milliseconds (500));
|
||||
if (++iteration % 60 == 0)
|
||||
{
|
||||
std::cout << boost::str (boost::format ("%1% blocks cemented\n") % node1->ledger.cache.cemented_count);
|
||||
std::cout << boost::str (boost::format ("%1% blocks cemented\n") % node1->ledger.cemented_count ());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1248,12 +1250,12 @@ int main (int argc, char * const * argv)
|
|||
node2->block_processor.add (block);
|
||||
blocks.pop_front ();
|
||||
}
|
||||
while (node2->ledger.cache.block_count != count * 2 + 1)
|
||||
while (node2->ledger.block_count () != count * 2 + 1)
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::milliseconds (500));
|
||||
if (++iteration % 60 == 0)
|
||||
{
|
||||
std::cout << boost::str (boost::format ("%1% blocks processed\n") % node2->ledger.cache.block_count);
|
||||
std::cout << boost::str (boost::format ("%1% blocks processed\n") % node2->ledger.block_count ());
|
||||
}
|
||||
}
|
||||
// Insert representative
|
||||
|
|
@ -1272,19 +1274,19 @@ int main (int argc, char * const * argv)
|
|||
auto begin (std::chrono::high_resolution_clock::now ());
|
||||
std::cout << boost::str (boost::format ("Starting confirming %1% frontiers (test node)\n") % (count + 1));
|
||||
// Wait for full frontiers confirmation
|
||||
while (node2->ledger.cache.cemented_count != node2->ledger.cache.block_count)
|
||||
while (node2->ledger.cemented_count () != node2->ledger.block_count ())
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::milliseconds (25));
|
||||
if (++iteration % 1200 == 0)
|
||||
{
|
||||
std::cout << boost::str (boost::format ("%1% blocks confirmed\n") % node2->ledger.cache.cemented_count);
|
||||
std::cout << boost::str (boost::format ("%1% blocks confirmed\n") % node2->ledger.cemented_count ());
|
||||
}
|
||||
}
|
||||
auto end (std::chrono::high_resolution_clock::now ());
|
||||
auto time (std::chrono::duration_cast<std::chrono::microseconds> (end - begin).count ());
|
||||
std::cout << boost::str (boost::format ("%|1$ 12d| us \n%2% frontiers per second\n") % time % ((count + 1) * 1000000 / time));
|
||||
io_ctx1.stop ();
|
||||
io_ctx2.stop ();
|
||||
io_ctx1->stop ();
|
||||
io_ctx2->stop ();
|
||||
runner1.join ();
|
||||
runner2.join ();
|
||||
node1->stop ();
|
||||
|
|
@ -1588,7 +1590,7 @@ int main (int argc, char * const * argv)
|
|||
calculated_representative = block->representative_field ().value ();
|
||||
}
|
||||
// Retrieving successor block hash
|
||||
hash = node->store.block.successor (transaction, hash);
|
||||
hash = node->ledger.successor (transaction, hash).value_or (0);
|
||||
// Retrieving block data
|
||||
if (!hash.is_zero ())
|
||||
{
|
||||
|
|
@ -1791,7 +1793,7 @@ int main (int argc, char * const * argv)
|
|||
nano::inactive_node inactive_node (data_path, node_flags);
|
||||
auto source_node = inactive_node.node;
|
||||
auto transaction (source_node->store.tx_begin_read ());
|
||||
block_count = source_node->ledger.cache.block_count;
|
||||
block_count = source_node->ledger.block_count ();
|
||||
std::cout << boost::str (boost::format ("Performing bootstrap emulation, %1% blocks in ledger...") % block_count) << std::endl;
|
||||
for (auto i (source_node->store.account.begin (transaction)), n (source_node->store.account.end ()); i != n; ++i)
|
||||
{
|
||||
|
|
@ -1822,7 +1824,7 @@ int main (int argc, char * const * argv)
|
|||
}
|
||||
}
|
||||
nano::timer<std::chrono::seconds> timer_l (nano::timer_state::started);
|
||||
while (node.node->ledger.cache.block_count != block_count)
|
||||
while (node.node->ledger.block_count () != block_count)
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::milliseconds (500));
|
||||
// Add epoch open blocks again if required
|
||||
|
|
@ -1837,7 +1839,7 @@ int main (int argc, char * const * argv)
|
|||
if (timer_l.after_deadline (std::chrono::seconds (60)))
|
||||
{
|
||||
timer_l.restart ();
|
||||
std::cout << boost::str (boost::format ("%1% (%2%) blocks processed (unchecked)") % node.node->ledger.cache.block_count % node.node->unchecked.count ()) << std::endl;
|
||||
std::cout << boost::str (boost::format ("%1% (%2%) blocks processed (unchecked)") % node.node->ledger.block_count () % node.node->unchecked.count ()) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1847,7 +1849,7 @@ int main (int argc, char * const * argv)
|
|||
auto seconds (time / us_in_second);
|
||||
nano::remove_temporary_directories ();
|
||||
std::cout << boost::str (boost::format ("%|1$ 12d| seconds \n%2% blocks per second") % seconds % (block_count * us_in_second / time)) << std::endl;
|
||||
release_assert (node.node->ledger.cache.block_count == block_count);
|
||||
release_assert (node.node->ledger.block_count () == block_count);
|
||||
}
|
||||
else if (vm.count ("debug_peers"))
|
||||
{
|
||||
|
|
@ -1866,7 +1868,7 @@ int main (int argc, char * const * argv)
|
|||
node_flags.generate_cache.cemented_count = true;
|
||||
nano::update_flags (node_flags, vm);
|
||||
nano::inactive_node node (data_path, node_flags);
|
||||
std::cout << "Total cemented block count: " << node.node->ledger.cache.cemented_count << std::endl;
|
||||
std::cout << "Total cemented block count: " << node.node->ledger.cemented_count () << std::endl;
|
||||
}
|
||||
else if (vm.count ("debug_prune"))
|
||||
{
|
||||
|
|
@ -1991,20 +1993,6 @@ int main (int argc, char * const * argv)
|
|||
output_account_version_number (i, unopened_account_version_totals[i]);
|
||||
}
|
||||
}
|
||||
else if (vm.count ("debug_unconfirmed_frontiers"))
|
||||
{
|
||||
auto inactive_node = nano::default_inactive_node (data_path, vm);
|
||||
auto node = inactive_node->node;
|
||||
|
||||
auto unconfirmed_frontiers = node->ledger.unconfirmed_frontiers ();
|
||||
std::cout << "Account: Height delta | Frontier | Confirmed frontier\n";
|
||||
for (auto const & [height_delta, unconfirmed_info] : unconfirmed_frontiers)
|
||||
{
|
||||
std::cout << (boost::format ("%1%: %2% %3% %4%\n") % unconfirmed_info.account.to_account () % height_delta % unconfirmed_info.frontier.to_string () % unconfirmed_info.cemented_frontier.to_string ()).str ();
|
||||
}
|
||||
|
||||
std::cout << "\nNumber of unconfirmed frontiers: " << unconfirmed_frontiers.size () << std::endl;
|
||||
}
|
||||
else if (vm.count ("version"))
|
||||
{
|
||||
std::cout << "Version " << NANO_VERSION_STRING << "\n"
|
||||
|
|
|
|||
|
|
@ -49,17 +49,23 @@ void run (std::filesystem::path const & data_path, std::vector<std::string> cons
|
|||
rpc_config.tls_config = tls_config;
|
||||
}
|
||||
|
||||
boost::asio::io_context io_ctx;
|
||||
std::shared_ptr<boost::asio::io_context> io_ctx = std::make_shared<boost::asio::io_context> ();
|
||||
|
||||
nano::signal_manager sigman;
|
||||
try
|
||||
{
|
||||
nano::ipc_rpc_processor ipc_rpc_processor (io_ctx, rpc_config);
|
||||
nano::ipc_rpc_processor ipc_rpc_processor (*io_ctx, rpc_config);
|
||||
auto rpc = nano::get_rpc (io_ctx, rpc_config, ipc_rpc_processor);
|
||||
rpc->start ();
|
||||
|
||||
debug_assert (!nano::signal_handler_impl);
|
||||
nano::signal_handler_impl = [&io_ctx] () {
|
||||
io_ctx.stop ();
|
||||
nano::signal_handler_impl = [io_ctx_w = std::weak_ptr{ io_ctx }] () {
|
||||
logger.warn (nano::log::type::daemon, "Interrupt signal received, stopping...");
|
||||
|
||||
if (auto io_ctx_l = io_ctx_w.lock ())
|
||||
{
|
||||
io_ctx_l->stop ();
|
||||
}
|
||||
sig_int_or_term = 1;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -122,7 +122,8 @@ int run_wallet (QApplication & application, int argc, char * const * argv, std::
|
|||
config.node.websocket_config.tls_config = tls_config;
|
||||
}
|
||||
|
||||
boost::asio::io_context io_ctx;
|
||||
std::shared_ptr<boost::asio::io_context> io_ctx = std::make_shared<boost::asio::io_context> ();
|
||||
|
||||
nano::thread_runner runner (io_ctx, config.node.io_threads);
|
||||
|
||||
std::shared_ptr<nano::node> node;
|
||||
|
|
@ -174,7 +175,7 @@ int run_wallet (QApplication & application, int argc, char * const * argv, std::
|
|||
nano::ipc::ipc_server ipc (*node, config.rpc);
|
||||
|
||||
std::unique_ptr<boost::process::child> rpc_process;
|
||||
std::unique_ptr<nano::rpc> rpc;
|
||||
std::shared_ptr<nano::rpc> rpc;
|
||||
std::unique_ptr<nano::rpc_handler_interface> rpc_handler;
|
||||
if (config.rpc_enable)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -59,12 +59,8 @@ add_library(
|
|||
cli.cpp
|
||||
common.hpp
|
||||
common.cpp
|
||||
confirmation_height_bounded.hpp
|
||||
confirmation_height_bounded.cpp
|
||||
confirmation_height_processor.hpp
|
||||
confirmation_height_processor.cpp
|
||||
confirmation_height_unbounded.hpp
|
||||
confirmation_height_unbounded.cpp
|
||||
confirming_set.hpp
|
||||
confirming_set.cpp
|
||||
confirmation_solicitor.hpp
|
||||
confirmation_solicitor.cpp
|
||||
daemonconfig.hpp
|
||||
|
|
@ -75,14 +71,12 @@ add_library(
|
|||
distributed_work_factory.cpp
|
||||
election.hpp
|
||||
election.cpp
|
||||
election_behavior.hpp
|
||||
election_insertion_result.hpp
|
||||
election_status.hpp
|
||||
epoch_upgrader.hpp
|
||||
epoch_upgrader.cpp
|
||||
fair_queue.hpp
|
||||
inactive_cache_information.hpp
|
||||
inactive_cache_information.cpp
|
||||
inactive_cache_status.hpp
|
||||
inactive_cache_status.cpp
|
||||
ipc/action_handler.hpp
|
||||
ipc/action_handler.cpp
|
||||
ipc/flatbuffers_handler.hpp
|
||||
|
|
@ -101,6 +95,8 @@ add_library(
|
|||
json_handler.cpp
|
||||
local_block_broadcaster.cpp
|
||||
local_block_broadcaster.hpp
|
||||
local_vote_history.cpp
|
||||
local_vote_history.hpp
|
||||
make_store.hpp
|
||||
make_store.cpp
|
||||
network.hpp
|
||||
|
|
@ -159,6 +155,8 @@ add_library(
|
|||
transport/socket.cpp
|
||||
transport/tcp.hpp
|
||||
transport/tcp.cpp
|
||||
transport/tcp_listener.hpp
|
||||
transport/tcp_listener.cpp
|
||||
transport/tcp_server.hpp
|
||||
transport/tcp_server.cpp
|
||||
transport/transport.hpp
|
||||
|
|
@ -167,10 +165,13 @@ add_library(
|
|||
unchecked_map.hpp
|
||||
vote_cache.hpp
|
||||
vote_cache.cpp
|
||||
vote_generator.hpp
|
||||
vote_generator.cpp
|
||||
vote_processor.hpp
|
||||
vote_processor.cpp
|
||||
voting.hpp
|
||||
voting.cpp
|
||||
vote_spacing.hpp
|
||||
vote_spacing.cpp
|
||||
vote_with_weight_info.hpp
|
||||
wallet.hpp
|
||||
wallet.cpp
|
||||
websocket.hpp
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/lib/threading.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/confirmation_height_processor.hpp>
|
||||
#include <nano/node/confirmation_solicitor.hpp>
|
||||
#include <nano/node/confirming_set.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/node.hpp>
|
||||
#include <nano/node/repcrawler.hpp>
|
||||
|
|
@ -15,9 +15,9 @@
|
|||
|
||||
using namespace std::chrono;
|
||||
|
||||
nano::active_transactions::active_transactions (nano::node & node_a, nano::confirmation_height_processor & confirmation_height_processor_a, nano::block_processor & block_processor_a) :
|
||||
nano::active_transactions::active_transactions (nano::node & node_a, nano::confirming_set & confirming_set, nano::block_processor & block_processor_a) :
|
||||
node{ node_a },
|
||||
confirmation_height_processor{ confirmation_height_processor_a },
|
||||
confirming_set{ confirming_set },
|
||||
block_processor{ block_processor_a },
|
||||
recently_confirmed{ 65536 },
|
||||
recently_cemented{ node.config.confirmation_history_size },
|
||||
|
|
@ -26,12 +26,12 @@ nano::active_transactions::active_transactions (nano::node & node_a, nano::confi
|
|||
count_by_behavior.fill (0); // Zero initialize array
|
||||
|
||||
// Register a callback which will get called after a block is cemented
|
||||
confirmation_height_processor.add_cemented_observer ([this] (std::shared_ptr<nano::block> const & callback_block_a) {
|
||||
confirming_set.cemented_observers.add ([this] (std::shared_ptr<nano::block> const & callback_block_a) {
|
||||
this->block_cemented_callback (callback_block_a);
|
||||
});
|
||||
|
||||
// Register a callback which will get called if a block is already cemented
|
||||
confirmation_height_processor.add_block_already_cemented_observer ([this] (nano::block_hash const & hash_a) {
|
||||
confirming_set.block_already_cemented_observers.add ([this] (nano::block_hash const & hash_a) {
|
||||
this->block_already_cemented_callback (hash_a);
|
||||
});
|
||||
|
||||
|
|
@ -80,145 +80,74 @@ void nano::active_transactions::stop ()
|
|||
clear ();
|
||||
}
|
||||
|
||||
void nano::active_transactions::block_cemented_callback (std::shared_ptr<nano::block> const & block_a)
|
||||
void nano::active_transactions::block_cemented_callback (std::shared_ptr<nano::block> const & block)
|
||||
{
|
||||
auto transaction = node.store.tx_begin_read ();
|
||||
auto status_type = election_status (transaction, block_a);
|
||||
|
||||
if (!status_type)
|
||||
return;
|
||||
|
||||
switch (*status_type)
|
||||
debug_assert (node.block_confirmed (block->hash ()));
|
||||
if (auto election_l = election (block->qualified_root ()))
|
||||
{
|
||||
case nano::election_status_type::inactive_confirmation_height:
|
||||
process_inactive_confirmation (transaction, block_a);
|
||||
break;
|
||||
|
||||
default:
|
||||
process_active_confirmation (transaction, block_a, *status_type);
|
||||
break;
|
||||
election_l->try_confirm (block->hash ());
|
||||
}
|
||||
|
||||
handle_final_votes_confirmation (block_a, transaction, *status_type);
|
||||
}
|
||||
|
||||
boost::optional<nano::election_status_type> nano::active_transactions::election_status (nano::store::read_transaction const & transaction, std::shared_ptr<nano::block> const & block)
|
||||
{
|
||||
boost::optional<nano::election_status_type> status_type;
|
||||
|
||||
if (!confirmation_height_processor.is_processing_added_block (block->hash ()))
|
||||
auto election = remove_election_winner_details (block->hash ());
|
||||
nano::election_status status;
|
||||
std::vector<nano::vote_with_weight_info> votes;
|
||||
status.winner = block;
|
||||
if (election)
|
||||
{
|
||||
status_type = confirm_block (transaction, block);
|
||||
status = election->get_status ();
|
||||
votes = election->votes_with_weight ();
|
||||
}
|
||||
if (confirming_set.exists (block->hash ()))
|
||||
{
|
||||
status.type = nano::election_status_type::active_confirmed_quorum;
|
||||
}
|
||||
else if (election)
|
||||
{
|
||||
status.type = nano::election_status_type::active_confirmation_height;
|
||||
}
|
||||
else
|
||||
{
|
||||
status_type = nano::election_status_type::active_confirmed_quorum;
|
||||
status.type = nano::election_status_type::inactive_confirmation_height;
|
||||
}
|
||||
recently_cemented.put (status);
|
||||
auto transaction = node.store.tx_begin_read ();
|
||||
notify_observers (transaction, status, votes);
|
||||
bool cemented_bootstrap_count_reached = node.ledger.cemented_count () >= node.ledger.bootstrap_weight_max_blocks;
|
||||
bool was_active = status.type == nano::election_status_type::active_confirmed_quorum || status.type == nano::election_status_type::active_confirmation_height;
|
||||
|
||||
return status_type;
|
||||
}
|
||||
|
||||
void nano::active_transactions::process_inactive_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr<nano::block> const & block)
|
||||
{
|
||||
nano::account account;
|
||||
nano::uint128_t amount{ 0 };
|
||||
bool is_state_send = false;
|
||||
bool is_state_epoch = false;
|
||||
nano::account pending_account{};
|
||||
node.process_confirmed_data (transaction, block, block->hash (), account, amount, is_state_send, is_state_epoch, pending_account);
|
||||
node.observers.blocks.notify (nano::election_status{ block, 0, 0, std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values<std::chrono::milliseconds>::zero (), 0, 1, 0, nano::election_status_type::inactive_confirmation_height }, {}, account, amount, is_state_send, is_state_epoch);
|
||||
}
|
||||
|
||||
void nano::active_transactions::process_active_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr<nano::block> const & block, nano::election_status_type status_type)
|
||||
{
|
||||
auto hash (block->hash ());
|
||||
nano::unique_lock<nano::mutex> election_winners_lk{ election_winner_details_mutex };
|
||||
auto existing = election_winner_details.find (hash);
|
||||
if (existing != election_winner_details.end ())
|
||||
// Next-block activations are only done for blocks with previously active elections
|
||||
if (cemented_bootstrap_count_reached && was_active)
|
||||
{
|
||||
auto election = existing->second;
|
||||
election_winner_details.erase (hash);
|
||||
election_winners_lk.unlock ();
|
||||
if (election->confirmed () && election->winner ()->hash () == hash)
|
||||
{
|
||||
handle_confirmation (transaction, block, election, status_type);
|
||||
}
|
||||
activate_successors (transaction, block);
|
||||
}
|
||||
}
|
||||
|
||||
void nano::active_transactions::handle_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr<nano::block> const & block, std::shared_ptr<nano::election> election, nano::election_status_type status_type)
|
||||
{
|
||||
nano::block_hash hash = block->hash ();
|
||||
recently_cemented.put (election->get_status ());
|
||||
|
||||
nano::account account;
|
||||
nano::uint128_t amount (0);
|
||||
bool is_state_send = false;
|
||||
bool is_state_epoch = false;
|
||||
nano::account pending_account;
|
||||
|
||||
handle_block_confirmation (transaction, block, hash, account, amount, is_state_send, is_state_epoch, pending_account);
|
||||
|
||||
auto status = election->set_status_type (status_type);
|
||||
auto votes = election->votes_with_weight ();
|
||||
notify_observers (status, votes, account, amount, is_state_send, is_state_epoch, pending_account);
|
||||
}
|
||||
|
||||
void nano::active_transactions::handle_block_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr<nano::block> const & block, nano::block_hash const & hash, nano::account & account, nano::uint128_t & amount, bool & is_state_send, bool & is_state_epoch, nano::account & pending_account)
|
||||
{
|
||||
if (block->is_send ())
|
||||
{
|
||||
node.receive_confirmed (transaction, hash, block->destination ());
|
||||
}
|
||||
node.process_confirmed_data (transaction, block, hash, account, amount, is_state_send, is_state_epoch, pending_account);
|
||||
}
|
||||
|
||||
void nano::active_transactions::notify_observers (nano::election_status const & status, std::vector<nano::vote_with_weight_info> const & votes, nano::account const & account, nano::uint128_t amount, bool is_state_send, bool is_state_epoch, nano::account const & pending_account)
|
||||
void nano::active_transactions::notify_observers (nano::store::read_transaction const & transaction, nano::election_status const & status, std::vector<nano::vote_with_weight_info> const & votes)
|
||||
{
|
||||
auto block = status.winner;
|
||||
auto account = block->account ();
|
||||
auto amount = node.ledger.amount (transaction, block->hash ()).value_or (0);
|
||||
auto is_state_send = block->type () == block_type::state && block->is_send ();
|
||||
auto is_state_epoch = block->type () == block_type::state && block->is_epoch ();
|
||||
node.observers.blocks.notify (status, votes, account, amount, is_state_send, is_state_epoch);
|
||||
|
||||
if (amount > 0)
|
||||
{
|
||||
node.observers.account_balance.notify (account, false);
|
||||
if (!pending_account.is_zero ())
|
||||
if (block->is_send ())
|
||||
{
|
||||
node.observers.account_balance.notify (pending_account, true);
|
||||
node.observers.account_balance.notify (block->destination (), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nano::active_transactions::handle_final_votes_confirmation (std::shared_ptr<nano::block> const & block, nano::store::read_transaction const & transaction, nano::election_status_type status)
|
||||
void nano::active_transactions::activate_successors (nano::store::read_transaction const & transaction, std::shared_ptr<nano::block> const & block)
|
||||
{
|
||||
auto account = block->account ();
|
||||
|
||||
bool is_canary_not_set = !node.ledger.cache.final_votes_confirmation_canary.load ();
|
||||
bool is_canary_account = account == node.network_params.ledger.final_votes_canary_account;
|
||||
bool is_height_above_threshold = block->sideband ().height >= node.network_params.ledger.final_votes_canary_height;
|
||||
|
||||
if (is_canary_not_set && is_canary_account && is_height_above_threshold)
|
||||
{
|
||||
node.ledger.cache.final_votes_confirmation_canary.store (true);
|
||||
}
|
||||
|
||||
bool cemented_bootstrap_count_reached = node.ledger.cache.cemented_count >= node.ledger.bootstrap_weight_max_blocks;
|
||||
bool was_active = status == nano::election_status_type::active_confirmed_quorum || status == nano::election_status_type::active_confirmation_height;
|
||||
|
||||
// Next-block activations are only done for blocks with previously active elections
|
||||
if (cemented_bootstrap_count_reached && was_active)
|
||||
{
|
||||
activate_successors (account, block, transaction);
|
||||
}
|
||||
}
|
||||
|
||||
void nano::active_transactions::activate_successors (const nano::account & account, std::shared_ptr<nano::block> const & block, nano::store::read_transaction const & transaction)
|
||||
{
|
||||
node.scheduler.priority.activate (account, transaction);
|
||||
auto const & destination = node.ledger.block_destination (transaction, *block);
|
||||
node.scheduler.priority.activate (block->account (), transaction);
|
||||
|
||||
// Start or vote for the next unconfirmed block in the destination account
|
||||
if (!destination.is_zero () && destination != account)
|
||||
if (block->is_send () && !block->destination ().is_zero () && block->destination () != block->account ())
|
||||
{
|
||||
node.scheduler.priority.activate (destination, transaction);
|
||||
node.scheduler.priority.activate (block->destination (), transaction);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -228,10 +157,17 @@ void nano::active_transactions::add_election_winner_details (nano::block_hash co
|
|||
election_winner_details.emplace (hash_a, election_a);
|
||||
}
|
||||
|
||||
void nano::active_transactions::remove_election_winner_details (nano::block_hash const & hash_a)
|
||||
std::shared_ptr<nano::election> nano::active_transactions::remove_election_winner_details (nano::block_hash const & hash_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ election_winner_details_mutex };
|
||||
election_winner_details.erase (hash_a);
|
||||
std::shared_ptr<nano::election> result;
|
||||
auto existing = election_winner_details.find (hash_a);
|
||||
if (existing != election_winner_details.end ())
|
||||
{
|
||||
result = existing->second;
|
||||
election_winner_details.erase (existing);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::active_transactions::block_already_cemented_callback (nano::block_hash const & hash_a)
|
||||
|
|
@ -433,9 +369,11 @@ void nano::active_transactions::trim ()
|
|||
|
||||
nano::election_insertion_result nano::active_transactions::insert (std::shared_ptr<nano::block> const & block_a, nano::election_behavior election_behavior_a)
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
debug_assert (block_a);
|
||||
debug_assert (block_a->has_sideband ());
|
||||
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
|
||||
nano::election_insertion_result result;
|
||||
|
||||
if (stopped)
|
||||
|
|
@ -478,16 +416,13 @@ nano::election_insertion_result nano::active_transactions::insert (std::shared_p
|
|||
result.election = existing->election;
|
||||
}
|
||||
|
||||
lock.unlock (); // end of critical section
|
||||
lock.unlock ();
|
||||
|
||||
if (result.inserted)
|
||||
{
|
||||
release_assert (result.election);
|
||||
debug_assert (result.election);
|
||||
|
||||
if (auto const cache = node.vote_cache.find (hash); cache)
|
||||
{
|
||||
cache->fill (result.election);
|
||||
}
|
||||
trigger_vote_cache (hash);
|
||||
|
||||
node.observers.active_started.notify (hash);
|
||||
vacancy_update ();
|
||||
|
|
@ -498,73 +433,68 @@ nano::election_insertion_result nano::active_transactions::insert (std::shared_p
|
|||
{
|
||||
result.election->broadcast_vote ();
|
||||
}
|
||||
|
||||
trim ();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Validate a vote and apply it to the current election if one exists
|
||||
nano::vote_code nano::active_transactions::vote (std::shared_ptr<nano::vote> const & vote_a)
|
||||
bool nano::active_transactions::trigger_vote_cache (nano::block_hash hash)
|
||||
{
|
||||
nano::vote_code result{ nano::vote_code::indeterminate };
|
||||
// If all hashes were recently confirmed then it is a replay
|
||||
unsigned recently_confirmed_counter (0);
|
||||
auto cached = node.vote_cache.find (hash);
|
||||
for (auto const & cached_vote : cached)
|
||||
{
|
||||
vote (cached_vote, nano::vote_source::cache);
|
||||
}
|
||||
return !cached.empty ();
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::shared_ptr<nano::election>, nano::block_hash>> process;
|
||||
// Validate a vote and apply it to the current election if one exists
|
||||
std::unordered_map<nano::block_hash, nano::vote_code> nano::active_transactions::vote (std::shared_ptr<nano::vote> const & vote, nano::vote_source source)
|
||||
{
|
||||
std::unordered_map<nano::block_hash, nano::vote_code> results;
|
||||
std::unordered_map<nano::block_hash, std::shared_ptr<nano::election>> process;
|
||||
std::vector<nano::block_hash> inactive; // Hashes that should be added to inactive vote cache
|
||||
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
for (auto const & hash : vote_a->hashes)
|
||||
for (auto const & hash : vote->hashes)
|
||||
{
|
||||
auto existing (blocks.find (hash));
|
||||
if (existing != blocks.end ())
|
||||
// Ignore duplicate hashes (should not happen with a well-behaved voting node)
|
||||
if (results.find (hash) != results.end ())
|
||||
{
|
||||
process.emplace_back (existing->second, hash);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto existing = blocks.find (hash); existing != blocks.end ())
|
||||
{
|
||||
process[hash] = existing->second;
|
||||
}
|
||||
else if (!recently_confirmed.exists (hash))
|
||||
{
|
||||
inactive.emplace_back (hash);
|
||||
results[hash] = nano::vote_code::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
++recently_confirmed_counter;
|
||||
results[hash] = nano::vote_code::replay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process inactive votes outside of the critical section
|
||||
for (auto & hash : inactive)
|
||||
for (auto const & [block_hash, election] : process)
|
||||
{
|
||||
add_vote_cache (hash, vote_a);
|
||||
auto const vote_result = election->vote (vote->account, vote->timestamp (), block_hash, source);
|
||||
results[block_hash] = vote_result;
|
||||
}
|
||||
|
||||
if (!process.empty ())
|
||||
{
|
||||
bool replay (false);
|
||||
bool processed (false);
|
||||
for (auto const & [election, block_hash] : process)
|
||||
{
|
||||
auto const result_l = election->vote (vote_a->account, vote_a->timestamp (), block_hash);
|
||||
processed = processed || result_l.processed;
|
||||
replay = replay || result_l.replay;
|
||||
}
|
||||
// All hashes should have their result set
|
||||
debug_assert (std::all_of (vote->hashes.begin (), vote->hashes.end (), [&results] (auto const & hash) {
|
||||
return results.find (hash) != results.end ();
|
||||
}));
|
||||
|
||||
// Republish vote if it is new and the node does not host a principal representative (or close to)
|
||||
if (processed)
|
||||
{
|
||||
auto const reps (node.wallets.reps ());
|
||||
if (!reps.have_half_rep () && !reps.exists (vote_a->account))
|
||||
{
|
||||
node.network.flood_vote (vote_a, 0.5f);
|
||||
}
|
||||
}
|
||||
result = replay ? nano::vote_code::replay : nano::vote_code::vote;
|
||||
}
|
||||
else if (recently_confirmed_counter == vote_a->hashes.size ())
|
||||
{
|
||||
result = nano::vote_code::replay;
|
||||
}
|
||||
return result;
|
||||
vote_processed.notify (vote, source, results);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
bool nano::active_transactions::active (nano::qualified_root const & root_a) const
|
||||
|
|
@ -611,26 +541,29 @@ std::shared_ptr<nano::block> nano::active_transactions::winner (nano::block_hash
|
|||
return result;
|
||||
}
|
||||
|
||||
void nano::active_transactions::erase (nano::block const & block_a)
|
||||
bool nano::active_transactions::erase (nano::block const & block_a)
|
||||
{
|
||||
erase (block_a.qualified_root ());
|
||||
return erase (block_a.qualified_root ());
|
||||
}
|
||||
|
||||
void nano::active_transactions::erase (nano::qualified_root const & root_a)
|
||||
bool nano::active_transactions::erase (nano::qualified_root const & root_a)
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
auto root_it (roots.get<tag_root> ().find (root_a));
|
||||
if (root_it != roots.get<tag_root> ().end ())
|
||||
{
|
||||
cleanup_election (lock, root_it->election);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void nano::active_transactions::erase_hash (nano::block_hash const & hash_a)
|
||||
bool nano::active_transactions::erase_hash (nano::block_hash const & hash_a)
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
[[maybe_unused]] auto erased (blocks.erase (hash_a));
|
||||
debug_assert (erased == 1);
|
||||
return erased == 1;
|
||||
}
|
||||
|
||||
void nano::active_transactions::erase_oldest ()
|
||||
|
|
@ -670,51 +603,15 @@ bool nano::active_transactions::publish (std::shared_ptr<nano::block> const & bl
|
|||
lock.lock ();
|
||||
blocks.emplace (block_a->hash (), election);
|
||||
lock.unlock ();
|
||||
if (auto const cache = node.vote_cache.find (block_a->hash ()); cache)
|
||||
{
|
||||
cache->fill (election);
|
||||
}
|
||||
|
||||
trigger_vote_cache (block_a->hash ());
|
||||
|
||||
node.stats.inc (nano::stat::type::active, nano::stat::detail::election_block_conflict);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns the type of election status requiring callbacks calling later
|
||||
boost::optional<nano::election_status_type> nano::active_transactions::confirm_block (store::transaction const & transaction_a, std::shared_ptr<nano::block> const & block_a)
|
||||
{
|
||||
auto const hash = block_a->hash ();
|
||||
std::shared_ptr<nano::election> election = nullptr;
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ mutex };
|
||||
auto existing = blocks.find (hash);
|
||||
if (existing != blocks.end ())
|
||||
{
|
||||
election = existing->second;
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<nano::election_status_type> status_type;
|
||||
if (election)
|
||||
{
|
||||
status_type = election->try_confirm (hash);
|
||||
}
|
||||
else
|
||||
{
|
||||
status_type = nano::election_status_type::inactive_confirmation_height;
|
||||
}
|
||||
|
||||
return status_type;
|
||||
}
|
||||
|
||||
void nano::active_transactions::add_vote_cache (nano::block_hash const & hash, std::shared_ptr<nano::vote> const vote)
|
||||
{
|
||||
if (node.ledger.weight (vote->account) > node.minimum_principal_weight ())
|
||||
{
|
||||
node.vote_cache.vote (hash, vote);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t nano::active_transactions::election_winner_details_size ()
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ election_winner_details_mutex };
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/numbers.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/election_behavior.hpp>
|
||||
#include <nano/node/election_insertion_result.hpp>
|
||||
#include <nano/node/voting.hpp>
|
||||
#include <nano/node/election_status.hpp>
|
||||
#include <nano/node/vote_with_weight_info.hpp>
|
||||
#include <nano/secure/common.hpp>
|
||||
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
|
|
@ -15,6 +16,7 @@
|
|||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mi = boost::multi_index;
|
||||
|
|
@ -26,11 +28,18 @@ class active_transactions;
|
|||
class block;
|
||||
class block_sideband;
|
||||
class block_processor;
|
||||
class confirming_set;
|
||||
class election;
|
||||
class vote;
|
||||
class confirmation_height_processor;
|
||||
class stats;
|
||||
}
|
||||
namespace nano::store
|
||||
{
|
||||
class read_transaction;
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class recently_confirmed_cache final
|
||||
{
|
||||
public:
|
||||
|
|
@ -51,8 +60,9 @@ public: // Tests
|
|||
|
||||
private:
|
||||
// clang-format off
|
||||
class tag_root {};
|
||||
class tag_hash {};
|
||||
class tag_root {};
|
||||
class tag_sequence {};
|
||||
|
||||
using ordered_recent_confirmations = boost::multi_index_container<entry_t,
|
||||
mi::indexed_by<
|
||||
|
|
@ -131,7 +141,7 @@ private: // Elections
|
|||
std::unordered_map<nano::block_hash, std::shared_ptr<nano::election>> blocks;
|
||||
|
||||
public:
|
||||
active_transactions (nano::node &, nano::confirmation_height_processor &, nano::block_processor &);
|
||||
active_transactions (nano::node &, nano::confirming_set &, nano::block_processor &);
|
||||
~active_transactions ();
|
||||
|
||||
void start ();
|
||||
|
|
@ -142,7 +152,7 @@ public:
|
|||
*/
|
||||
nano::election_insertion_result insert (std::shared_ptr<nano::block> const &, nano::election_behavior = nano::election_behavior::normal);
|
||||
// Distinguishes replay votes, cannot be determined if the block is not in any election
|
||||
nano::vote_code vote (std::shared_ptr<nano::vote> const &);
|
||||
std::unordered_map<nano::block_hash, nano::vote_code> vote (std::shared_ptr<nano::vote> const &, nano::vote_source = nano::vote_source::live);
|
||||
// Is the root of this block in the roots container
|
||||
bool active (nano::block const &) const;
|
||||
bool active (nano::qualified_root const &) const;
|
||||
|
|
@ -154,13 +164,13 @@ public:
|
|||
std::shared_ptr<nano::block> winner (nano::block_hash const &) const;
|
||||
// Returns a list of elections sorted by difficulty
|
||||
std::vector<std::shared_ptr<nano::election>> list_active (std::size_t = std::numeric_limits<std::size_t>::max ());
|
||||
void erase (nano::block const &);
|
||||
void erase_hash (nano::block_hash const &);
|
||||
bool erase (nano::block const &);
|
||||
bool erase (nano::qualified_root const &);
|
||||
bool erase_hash (nano::block_hash const &);
|
||||
void erase_oldest ();
|
||||
bool empty () const;
|
||||
std::size_t size () const;
|
||||
bool publish (std::shared_ptr<nano::block> const &);
|
||||
boost::optional<nano::election_status_type> confirm_block (store::transaction const &, std::shared_ptr<nano::block> const &);
|
||||
void block_cemented_callback (std::shared_ptr<nano::block> const &);
|
||||
void block_already_cemented_callback (nano::block_hash const &);
|
||||
|
||||
|
|
@ -177,36 +187,29 @@ public:
|
|||
|
||||
std::size_t election_winner_details_size ();
|
||||
void add_election_winner_details (nano::block_hash const &, std::shared_ptr<nano::election> const &);
|
||||
void remove_election_winner_details (nano::block_hash const &);
|
||||
std::shared_ptr<nano::election> remove_election_winner_details (nano::block_hash const &);
|
||||
|
||||
public: // Events
|
||||
using vote_processed_event_t = nano::observer_set<std::shared_ptr<nano::vote> const &, nano::vote_source, std::unordered_map<nano::block_hash, nano::vote_code> const &>;
|
||||
vote_processed_event_t vote_processed;
|
||||
|
||||
private:
|
||||
// Erase elections if we're over capacity
|
||||
void trim ();
|
||||
void request_loop ();
|
||||
void request_confirm (nano::unique_lock<nano::mutex> &);
|
||||
void erase (nano::qualified_root const &);
|
||||
// Erase all blocks from active and, if not confirmed, clear digests from network filters
|
||||
void cleanup_election (nano::unique_lock<nano::mutex> & lock_a, std::shared_ptr<nano::election>);
|
||||
nano::stat::type completion_type (nano::election const & election) const;
|
||||
// Returns a list of elections sorted by difficulty, mutex must be locked
|
||||
std::vector<std::shared_ptr<nano::election>> list_active_impl (std::size_t) const;
|
||||
/**
|
||||
* Checks if vote passes minimum representative weight threshold and adds it to inactive vote cache
|
||||
* TODO: Should be moved to `vote_cache` class
|
||||
*/
|
||||
void add_vote_cache (nano::block_hash const & hash, std::shared_ptr<nano::vote> vote);
|
||||
boost::optional<nano::election_status_type> election_status (nano::store::read_transaction const & transaction, std::shared_ptr<nano::block> const & block);
|
||||
void process_inactive_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr<nano::block> const & block);
|
||||
void process_active_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr<nano::block> const & block, nano::election_status_type status);
|
||||
void handle_final_votes_confirmation (std::shared_ptr<nano::block> const & block, nano::store::read_transaction const & transaction, nano::election_status_type status);
|
||||
void handle_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr<nano::block> const & block, std::shared_ptr<nano::election> election, nano::election_status_type status);
|
||||
void activate_successors (const nano::account & account, std::shared_ptr<nano::block> const & block, nano::store::read_transaction const & transaction);
|
||||
void handle_block_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr<nano::block> const & block, nano::block_hash const & hash, nano::account & account, nano::uint128_t & amount, bool & is_state_send, bool & is_state_epoch, nano::account & pending_account);
|
||||
void notify_observers (nano::election_status const & status, std::vector<nano::vote_with_weight_info> const & votes, nano::account const & account, nano::uint128_t amount, bool is_state_send, bool is_state_epoch, nano::account const & pending_account);
|
||||
void activate_successors (nano::store::read_transaction const & transaction, std::shared_ptr<nano::block> const & block);
|
||||
void notify_observers (nano::store::read_transaction const & transaction, nano::election_status const & status, std::vector<nano::vote_with_weight_info> const & votes);
|
||||
bool trigger_vote_cache (nano::block_hash);
|
||||
|
||||
private: // Dependencies
|
||||
nano::node & node;
|
||||
nano::confirmation_height_processor & confirmation_height_processor;
|
||||
nano::confirming_set & confirming_set;
|
||||
nano::block_processor & block_processor;
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
#include <nano/node/backlog_population.hpp>
|
||||
#include <nano/node/nodeconfig.hpp>
|
||||
#include <nano/node/scheduler/priority.hpp>
|
||||
#include <nano/store/account.hpp>
|
||||
#include <nano/store/component.hpp>
|
||||
#include <nano/store/confirmation_height.hpp>
|
||||
|
||||
nano::backlog_population::backlog_population (const config & config_a, nano::store::component & store_a, nano::stats & stats_a) :
|
||||
config_m{ config_a },
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/lib/threading.hpp>
|
||||
#include <nano/lib/timer.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/blockprocessor.hpp>
|
||||
#include <nano/node/local_vote_history.hpp>
|
||||
#include <nano/node/node.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/store/component.hpp>
|
||||
|
|
@ -203,7 +205,8 @@ bool nano::block_processor::add_impl (context ctx, std::shared_ptr<nano::transpo
|
|||
void nano::block_processor::rollback_competitor (store::write_transaction const & transaction, nano::block const & block)
|
||||
{
|
||||
auto hash = block.hash ();
|
||||
auto successor = node.ledger.successor (transaction, block.qualified_root ());
|
||||
auto successor_hash = node.ledger.successor (transaction, block.qualified_root ());
|
||||
auto successor = successor_hash ? node.ledger.block (transaction, successor_hash.value ()) : nullptr;
|
||||
if (successor != nullptr && successor->hash () != hash)
|
||||
{
|
||||
// Replace our block with the winner and roll back any dependent blocks
|
||||
|
|
@ -298,7 +301,7 @@ auto nano::block_processor::process_batch (nano::unique_lock<nano::mutex> & lock
|
|||
processed_batch_t processed;
|
||||
|
||||
auto scoped_write_guard = write_database_queue.wait (nano::writer::process_batch);
|
||||
auto transaction (node.store.tx_begin_write ({ tables::accounts, tables::blocks, tables::frontiers, tables::pending }));
|
||||
auto transaction (node.store.tx_begin_write ({ tables::accounts, tables::blocks, tables::pending, tables::rep_weights }));
|
||||
nano::timer<std::chrono::milliseconds> timer_l;
|
||||
|
||||
lock_a.lock ();
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ void nano::bulk_pull_server::set_current_end ()
|
|||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_server, "Bulk pull request for block hash: {}", request->start.to_string ());
|
||||
|
||||
current = ascending () ? node->store.block.successor (transaction, request->start.as_block_hash ()) : request->start.as_block_hash ();
|
||||
current = ascending () ? node->ledger.successor (transaction, request->start.as_block_hash ()).value_or (0) : request->start.as_block_hash ();
|
||||
include_start = true;
|
||||
}
|
||||
else
|
||||
|
|
@ -753,30 +753,16 @@ std::pair<std::unique_ptr<nano::pending_key>, std::unique_ptr<nano::pending_info
|
|||
* destroy a database transaction, to avoid locking the
|
||||
* database for a prolonged period.
|
||||
*/
|
||||
auto stream_transaction (node->store.tx_begin_read ());
|
||||
auto stream (node->store.pending.begin (stream_transaction, current_key));
|
||||
auto tx = node->store.tx_begin_read ();
|
||||
auto & ledger = node->ledger;
|
||||
auto stream = ledger.receivable_upper_bound (tx, current_key.account, current_key.hash);
|
||||
|
||||
if (stream == store::iterator<nano::pending_key, nano::pending_info> (nullptr))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
nano::pending_key key (stream->first);
|
||||
nano::pending_info info (stream->second);
|
||||
|
||||
/*
|
||||
* Get the key for the next value, to use in the next call or iteration
|
||||
*/
|
||||
current_key.account = key.account;
|
||||
current_key.hash = key.hash.number () + 1;
|
||||
|
||||
/*
|
||||
* Finish up if the response is for a different account
|
||||
*/
|
||||
if (key.account != request->account)
|
||||
if (stream == ledger.receivable_end ())
|
||||
{
|
||||
break;
|
||||
}
|
||||
auto const & [key, info] = *stream;
|
||||
current_key = key;
|
||||
|
||||
/*
|
||||
* Skip entries where the amount is less than the requested
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ nano::bootstrap_client::bootstrap_client (std::shared_ptr<nano::node> const & no
|
|||
{
|
||||
++node_a->bootstrap_initiator.connections->connections_count;
|
||||
receive_buffer->resize (256);
|
||||
channel->set_endpoint ();
|
||||
channel->update_endpoint ();
|
||||
}
|
||||
|
||||
nano::bootstrap_client::~bootstrap_client ()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include <nano/lib/utility.hpp>
|
||||
#include <nano/node/bootstrap_ascending/iterators.hpp>
|
||||
#include <nano/secure/common.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/store/account.hpp>
|
||||
#include <nano/store/component.hpp>
|
||||
#include <nano/store/pending.hpp>
|
||||
|
|
@ -9,8 +10,8 @@
|
|||
* database_iterator
|
||||
*/
|
||||
|
||||
nano::bootstrap_ascending::database_iterator::database_iterator (nano::store::component & store_a, table_type table_a) :
|
||||
store{ store_a },
|
||||
nano::bootstrap_ascending::database_iterator::database_iterator (nano::ledger & ledger, table_type table_a) :
|
||||
ledger{ ledger },
|
||||
table{ table_a }
|
||||
{
|
||||
}
|
||||
|
|
@ -27,8 +28,8 @@ void nano::bootstrap_ascending::database_iterator::next (store::transaction & tx
|
|||
case table_type::account:
|
||||
{
|
||||
auto i = current.number () + 1;
|
||||
auto item = store.account.begin (tx, i);
|
||||
if (item != store.account.end ())
|
||||
auto item = ledger.store.account.begin (tx, i);
|
||||
if (item != ledger.store.account.end ())
|
||||
{
|
||||
current = item->first;
|
||||
}
|
||||
|
|
@ -40,9 +41,8 @@ void nano::bootstrap_ascending::database_iterator::next (store::transaction & tx
|
|||
}
|
||||
case table_type::pending:
|
||||
{
|
||||
auto i = current.number () + 1;
|
||||
auto item = store.pending.begin (tx, nano::pending_key{ i, 0 });
|
||||
if (item != store.pending.end ())
|
||||
auto item = ledger.receivable_upper_bound (tx, current);
|
||||
if (item != ledger.receivable_end ())
|
||||
{
|
||||
current = item->first.account;
|
||||
}
|
||||
|
|
@ -59,10 +59,10 @@ void nano::bootstrap_ascending::database_iterator::next (store::transaction & tx
|
|||
* buffered_iterator
|
||||
*/
|
||||
|
||||
nano::bootstrap_ascending::buffered_iterator::buffered_iterator (nano::store::component & store_a) :
|
||||
store{ store_a },
|
||||
accounts_iterator{ store, database_iterator::table_type::account },
|
||||
pending_iterator{ store, database_iterator::table_type::pending }
|
||||
nano::bootstrap_ascending::buffered_iterator::buffered_iterator (nano::ledger & ledger) :
|
||||
ledger{ ledger },
|
||||
accounts_iterator{ ledger, database_iterator::table_type::account },
|
||||
pending_iterator{ ledger, database_iterator::table_type::pending }
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ void nano::bootstrap_ascending::buffered_iterator::fill ()
|
|||
debug_assert (buffer.empty ());
|
||||
|
||||
// Fill half from accounts table and half from pending table
|
||||
auto transaction = store.tx_begin_read ();
|
||||
auto transaction = ledger.store.tx_begin_read ();
|
||||
|
||||
for (int n = 0; n < size / 2; ++n)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@
|
|||
|
||||
#include <deque>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class ledger;
|
||||
}
|
||||
|
||||
namespace nano::store
|
||||
{
|
||||
class component;
|
||||
|
|
@ -21,12 +26,12 @@ public:
|
|||
pending
|
||||
};
|
||||
|
||||
explicit database_iterator (nano::store::component & store, table_type);
|
||||
explicit database_iterator (nano::ledger & ledger, table_type);
|
||||
nano::account operator* () const;
|
||||
void next (nano::store::transaction & tx);
|
||||
|
||||
private:
|
||||
nano::store::component & store;
|
||||
nano::ledger & ledger;
|
||||
nano::account current{ 0 };
|
||||
const table_type table;
|
||||
};
|
||||
|
|
@ -34,7 +39,7 @@ private:
|
|||
class buffered_iterator
|
||||
{
|
||||
public:
|
||||
explicit buffered_iterator (nano::store::component & store);
|
||||
explicit buffered_iterator (nano::ledger & ledger);
|
||||
nano::account operator* () const;
|
||||
nano::account next ();
|
||||
// Indicates if a full ledger iteration has taken place e.g. warmed up
|
||||
|
|
@ -44,7 +49,7 @@ private:
|
|||
void fill ();
|
||||
|
||||
private:
|
||||
nano::store::component & store;
|
||||
nano::ledger & ledger;
|
||||
std::deque<nano::account> buffer;
|
||||
bool warmup_m{ true };
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ nano::bootstrap_ascending::service::service (nano::node_config & config_a, nano:
|
|||
network{ network_a },
|
||||
stats{ stat_a },
|
||||
accounts{ stats },
|
||||
iterator{ ledger.store },
|
||||
iterator{ ledger },
|
||||
throttle{ compute_throttle_size () },
|
||||
scoring{ config.bootstrap_ascending, config.network_params.network },
|
||||
database_limiter{ config.bootstrap_ascending.database_requests_limit, 1.0 }
|
||||
|
|
@ -485,7 +485,7 @@ std::size_t nano::bootstrap_ascending::service::compute_throttle_size () const
|
|||
{
|
||||
// Scales logarithmically with ledger block
|
||||
// Returns: config.throttle_coefficient * sqrt(block_count)
|
||||
std::size_t size_new = config.bootstrap_ascending.throttle_coefficient * std::sqrt (ledger.cache.block_count.load ());
|
||||
std::size_t size_new = config.bootstrap_ascending.throttle_coefficient * std::sqrt (ledger.block_count ());
|
||||
return size_new == 0 ? 16 : size_new;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,580 +0,0 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/lib/stats.hpp>
|
||||
#include <nano/node/confirmation_height_bounded.hpp>
|
||||
#include <nano/node/write_database_queue.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/store/block.hpp>
|
||||
#include <nano/store/confirmation_height.hpp>
|
||||
#include <nano/store/pruned.hpp>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
nano::confirmation_height_bounded::confirmation_height_bounded (nano::ledger & ledger_a, nano::write_database_queue & write_database_queue_a, std::chrono::milliseconds batch_separate_pending_min_time_a, nano::logger & logger_a, std::atomic<bool> & stopped_a, uint64_t & batch_write_size_a, std::function<void (std::vector<std::shared_ptr<nano::block>> const &)> const & notify_observers_callback_a, std::function<void (nano::block_hash const &)> const & notify_block_already_cemented_observers_callback_a, std::function<uint64_t ()> const & awaiting_processing_size_callback_a) :
|
||||
ledger (ledger_a),
|
||||
write_database_queue (write_database_queue_a),
|
||||
batch_separate_pending_min_time (batch_separate_pending_min_time_a),
|
||||
logger (logger_a),
|
||||
stopped (stopped_a),
|
||||
batch_write_size (batch_write_size_a),
|
||||
notify_observers_callback (notify_observers_callback_a),
|
||||
notify_block_already_cemented_observers_callback (notify_block_already_cemented_observers_callback_a),
|
||||
awaiting_processing_size_callback (awaiting_processing_size_callback_a)
|
||||
{
|
||||
}
|
||||
|
||||
// The next block hash to iterate over, the priority is as follows:
|
||||
// 1 - The next block in the account chain for the last processed receive (if there is any)
|
||||
// 2 - The next receive block which is closest to genesis
|
||||
// 3 - The last checkpoint hit.
|
||||
// 4 - The hash that was passed in originally. Either all checkpoints were exhausted (this can happen when there are many accounts to genesis)
|
||||
// or all other blocks have been processed.
|
||||
nano::confirmation_height_bounded::top_and_next_hash nano::confirmation_height_bounded::get_next_block (boost::optional<top_and_next_hash> const & next_in_receive_chain_a, boost::circular_buffer_space_optimized<nano::block_hash> const & checkpoints_a, boost::circular_buffer_space_optimized<receive_source_pair> const & receive_source_pairs, boost::optional<receive_chain_details> & receive_details_a, nano::block const & original_block)
|
||||
{
|
||||
top_and_next_hash next;
|
||||
if (next_in_receive_chain_a.is_initialized ())
|
||||
{
|
||||
next = *next_in_receive_chain_a;
|
||||
}
|
||||
else if (!receive_source_pairs.empty ())
|
||||
{
|
||||
auto next_receive_source_pair = receive_source_pairs.back ();
|
||||
receive_details_a = next_receive_source_pair.receive_details;
|
||||
next = { next_receive_source_pair.source_hash, receive_details_a->next, receive_details_a->height + 1 };
|
||||
}
|
||||
else if (!checkpoints_a.empty ())
|
||||
{
|
||||
next = { checkpoints_a.back (), boost::none, 0 };
|
||||
}
|
||||
else
|
||||
{
|
||||
next = { original_block.hash (), boost::none, 0 };
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
void nano::confirmation_height_bounded::process (std::shared_ptr<nano::block> original_block)
|
||||
{
|
||||
if (pending_empty ())
|
||||
{
|
||||
clear_process_vars ();
|
||||
timer.restart ();
|
||||
}
|
||||
|
||||
boost::optional<top_and_next_hash> next_in_receive_chain;
|
||||
boost::circular_buffer_space_optimized<nano::block_hash> checkpoints{ max_items };
|
||||
boost::circular_buffer_space_optimized<receive_source_pair> receive_source_pairs{ max_items };
|
||||
nano::block_hash current;
|
||||
bool first_iter = true;
|
||||
auto transaction (ledger.store.tx_begin_read ());
|
||||
do
|
||||
{
|
||||
boost::optional<receive_chain_details> receive_details;
|
||||
auto hash_to_process = get_next_block (next_in_receive_chain, checkpoints, receive_source_pairs, receive_details, *original_block);
|
||||
current = hash_to_process.top;
|
||||
|
||||
auto top_level_hash = current;
|
||||
std::shared_ptr<nano::block> block;
|
||||
if (first_iter)
|
||||
{
|
||||
debug_assert (current == original_block->hash ());
|
||||
block = original_block;
|
||||
}
|
||||
else
|
||||
{
|
||||
block = ledger.block (transaction, current);
|
||||
}
|
||||
|
||||
if (!block)
|
||||
{
|
||||
if (ledger.pruning && ledger.store.pruned.exists (transaction, current))
|
||||
{
|
||||
if (!receive_source_pairs.empty ())
|
||||
{
|
||||
receive_source_pairs.pop_back ();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.critical (nano::log::type::conf_processor_bounded, "Ledger mismatch trying to set confirmation height for block {} (bounded processor)", current.to_string ());
|
||||
|
||||
release_assert (block);
|
||||
}
|
||||
}
|
||||
auto account = block->account ();
|
||||
|
||||
// Checks if we have encountered this account before but not commited changes yet, if so then update the cached confirmation height
|
||||
nano::confirmation_height_info confirmation_height_info;
|
||||
auto account_it = accounts_confirmed_info.find (account);
|
||||
if (account_it != accounts_confirmed_info.cend ())
|
||||
{
|
||||
confirmation_height_info.height = account_it->second.confirmed_height;
|
||||
confirmation_height_info.frontier = account_it->second.iterated_frontier;
|
||||
}
|
||||
else
|
||||
{
|
||||
ledger.store.confirmation_height.get (transaction, account, confirmation_height_info);
|
||||
// This block was added to the confirmation height processor but is already confirmed
|
||||
if (first_iter && confirmation_height_info.height >= block->sideband ().height && current == original_block->hash ())
|
||||
{
|
||||
notify_block_already_cemented_observers_callback (original_block->hash ());
|
||||
}
|
||||
}
|
||||
|
||||
auto block_height = block->sideband ().height;
|
||||
bool already_cemented = confirmation_height_info.height >= block_height;
|
||||
|
||||
// If we are not already at the bottom of the account chain (1 above cemented frontier) then find it
|
||||
if (!already_cemented && block_height - confirmation_height_info.height > 1)
|
||||
{
|
||||
if (block_height - confirmation_height_info.height == 2)
|
||||
{
|
||||
// If there is 1 uncemented block in-between this block and the cemented frontier,
|
||||
// we can just use the previous block to get the least unconfirmed hash.
|
||||
current = block->previous ();
|
||||
--block_height;
|
||||
}
|
||||
else if (!next_in_receive_chain.is_initialized ())
|
||||
{
|
||||
current = get_least_unconfirmed_hash_from_top_level (transaction, current, account, confirmation_height_info, block_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the cached successor of the last receive which saves having to do more IO in get_least_unconfirmed_hash_from_top_level
|
||||
// as we already know what the next block we should process should be.
|
||||
current = *hash_to_process.next;
|
||||
block_height = hash_to_process.next_height;
|
||||
}
|
||||
}
|
||||
|
||||
auto top_most_non_receive_block_hash = current;
|
||||
|
||||
bool hit_receive = false;
|
||||
if (!already_cemented)
|
||||
{
|
||||
hit_receive = iterate (transaction, block_height, current, checkpoints, top_most_non_receive_block_hash, top_level_hash, receive_source_pairs, account);
|
||||
}
|
||||
|
||||
// Exit early when the processor has been stopped, otherwise this function may take a
|
||||
// while (and hence keep the process running) if updating a long chain.
|
||||
if (stopped)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// next_in_receive_chain can be modified when writing, so need to cache it here before resetting
|
||||
auto is_set = next_in_receive_chain.is_initialized ();
|
||||
next_in_receive_chain = boost::none;
|
||||
|
||||
// Need to also handle the case where we are hitting receives where the sends below should be confirmed
|
||||
if (!hit_receive || (receive_source_pairs.size () == 1 && top_most_non_receive_block_hash != current))
|
||||
{
|
||||
preparation_data preparation_data{ transaction, top_most_non_receive_block_hash, already_cemented, checkpoints, account_it, confirmation_height_info, account, block_height, current, receive_details, next_in_receive_chain };
|
||||
prepare_iterated_blocks_for_cementing (preparation_data);
|
||||
|
||||
// If used the top level, don't pop off the receive source pair because it wasn't used
|
||||
if (!is_set && !receive_source_pairs.empty ())
|
||||
{
|
||||
receive_source_pairs.pop_back ();
|
||||
}
|
||||
|
||||
auto total_pending_write_block_count = std::accumulate (pending_writes.cbegin (), pending_writes.cend (), uint64_t (0), [] (uint64_t total, auto const & write_details_a) {
|
||||
return total += write_details_a.top_height - write_details_a.bottom_height + 1;
|
||||
});
|
||||
|
||||
auto max_batch_write_size_reached = (total_pending_write_block_count >= batch_write_size);
|
||||
// When there are a lot of pending confirmation height blocks, it is more efficient to
|
||||
// bulk some of them up to enable better write performance which becomes the bottleneck.
|
||||
auto min_time_exceeded = (timer.since_start () >= batch_separate_pending_min_time);
|
||||
auto finished_iterating = current == original_block->hash ();
|
||||
auto non_awaiting_processing = awaiting_processing_size_callback () == 0;
|
||||
auto should_output = finished_iterating && (non_awaiting_processing || min_time_exceeded);
|
||||
auto force_write = pending_writes.size () >= pending_writes_max_size || accounts_confirmed_info.size () >= pending_writes_max_size;
|
||||
|
||||
if ((max_batch_write_size_reached || should_output || force_write) && !pending_writes.empty ())
|
||||
{
|
||||
// If nothing is currently using the database write lock then write the cemented pending blocks otherwise continue iterating
|
||||
if (write_database_queue.process (nano::writer::confirmation_height))
|
||||
{
|
||||
auto scoped_write_guard = write_database_queue.pop ();
|
||||
cement_blocks (scoped_write_guard);
|
||||
}
|
||||
else if (force_write)
|
||||
{
|
||||
auto scoped_write_guard = write_database_queue.wait (nano::writer::confirmation_height);
|
||||
cement_blocks (scoped_write_guard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
first_iter = false;
|
||||
transaction.refresh ();
|
||||
} while ((!receive_source_pairs.empty () || current != original_block->hash ()) && !stopped);
|
||||
|
||||
debug_assert (checkpoints.empty ());
|
||||
}
|
||||
|
||||
nano::block_hash nano::confirmation_height_bounded::get_least_unconfirmed_hash_from_top_level (store::transaction const & transaction_a, nano::block_hash const & hash_a, nano::account const & account_a, nano::confirmation_height_info const & confirmation_height_info_a, uint64_t & block_height_a)
|
||||
{
|
||||
nano::block_hash least_unconfirmed_hash = hash_a;
|
||||
if (confirmation_height_info_a.height != 0)
|
||||
{
|
||||
if (block_height_a > confirmation_height_info_a.height)
|
||||
{
|
||||
auto block = ledger.block (transaction_a, confirmation_height_info_a.frontier);
|
||||
release_assert (block != nullptr);
|
||||
least_unconfirmed_hash = block->sideband ().successor;
|
||||
block_height_a = block->sideband ().height + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No blocks have been confirmed, so the first block will be the open block
|
||||
auto info = ledger.account_info (transaction_a, account_a);
|
||||
release_assert (info);
|
||||
least_unconfirmed_hash = info->open_block;
|
||||
block_height_a = 1;
|
||||
}
|
||||
return least_unconfirmed_hash;
|
||||
}
|
||||
|
||||
bool nano::confirmation_height_bounded::iterate (store::read_transaction const & transaction_a, uint64_t bottom_height_a, nano::block_hash const & bottom_hash_a, boost::circular_buffer_space_optimized<nano::block_hash> & checkpoints_a, nano::block_hash & top_most_non_receive_block_hash_a, nano::block_hash const & top_level_hash_a, boost::circular_buffer_space_optimized<receive_source_pair> & receive_source_pairs_a, nano::account const & account_a)
|
||||
{
|
||||
bool reached_target = false;
|
||||
bool hit_receive = false;
|
||||
auto hash = bottom_hash_a;
|
||||
uint64_t num_blocks = 0;
|
||||
while (!hash.is_zero () && !reached_target && !stopped)
|
||||
{
|
||||
// Keep iterating upwards until we either reach the desired block or the second receive.
|
||||
// Once a receive is cemented, we can cement all blocks above it until the next receive, so store those details for later.
|
||||
++num_blocks;
|
||||
auto block = ledger.block (transaction_a, hash);
|
||||
if (block->is_receive () && ledger.block_exists (transaction_a, block->source ()))
|
||||
{
|
||||
hit_receive = true;
|
||||
reached_target = true;
|
||||
auto const & sideband (block->sideband ());
|
||||
auto next = !sideband.successor.is_zero () && sideband.successor != top_level_hash_a ? boost::optional<nano::block_hash> (sideband.successor) : boost::none;
|
||||
receive_source_pairs_a.push_back ({ receive_chain_details{ account_a, sideband.height, hash, top_level_hash_a, next, bottom_height_a, bottom_hash_a }, block->source () });
|
||||
// Store a checkpoint every max_items so that we can always traverse a long number of accounts to genesis
|
||||
if (receive_source_pairs_a.size () % max_items == 0)
|
||||
{
|
||||
checkpoints_a.push_back (top_level_hash_a);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Found a send/change/epoch block which isn't the desired top level
|
||||
top_most_non_receive_block_hash_a = hash;
|
||||
if (hash == top_level_hash_a)
|
||||
{
|
||||
reached_target = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
hash = block->sideband ().successor;
|
||||
}
|
||||
}
|
||||
|
||||
// We could be traversing a very large account so we don't want to open read transactions for too long.
|
||||
if ((num_blocks > 0) && num_blocks % batch_read_size == 0)
|
||||
{
|
||||
transaction_a.refresh ();
|
||||
}
|
||||
}
|
||||
|
||||
return hit_receive;
|
||||
}
|
||||
|
||||
// Once the path to genesis has been iterated to, we can begin to cement the lowest blocks in the accounts. This sets up
|
||||
// the non-receive blocks which have been iterated for an account, and the associated receive block.
|
||||
void nano::confirmation_height_bounded::prepare_iterated_blocks_for_cementing (preparation_data & preparation_data_a)
|
||||
{
|
||||
if (!preparation_data_a.already_cemented)
|
||||
{
|
||||
// Add the non-receive blocks iterated for this account
|
||||
auto block_height = (ledger.height (preparation_data_a.transaction, preparation_data_a.top_most_non_receive_block_hash));
|
||||
if (block_height > preparation_data_a.confirmation_height_info.height)
|
||||
{
|
||||
confirmed_info confirmed_info_l{ block_height, preparation_data_a.top_most_non_receive_block_hash };
|
||||
if (preparation_data_a.account_it != accounts_confirmed_info.cend ())
|
||||
{
|
||||
preparation_data_a.account_it->second = confirmed_info_l;
|
||||
}
|
||||
else
|
||||
{
|
||||
accounts_confirmed_info.emplace (preparation_data_a.account, confirmed_info_l);
|
||||
++accounts_confirmed_info_size;
|
||||
}
|
||||
|
||||
preparation_data_a.checkpoints.erase (std::remove (preparation_data_a.checkpoints.begin (), preparation_data_a.checkpoints.end (), preparation_data_a.top_most_non_receive_block_hash), preparation_data_a.checkpoints.end ());
|
||||
pending_writes.emplace_back (preparation_data_a.account, preparation_data_a.bottom_height, preparation_data_a.bottom_most, block_height, preparation_data_a.top_most_non_receive_block_hash);
|
||||
++pending_writes_size;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the receive block and all non-receive blocks above that one
|
||||
auto & receive_details = preparation_data_a.receive_details;
|
||||
if (receive_details)
|
||||
{
|
||||
auto receive_confirmed_info_it = accounts_confirmed_info.find (receive_details->account);
|
||||
if (receive_confirmed_info_it != accounts_confirmed_info.cend ())
|
||||
{
|
||||
auto & receive_confirmed_info = receive_confirmed_info_it->second;
|
||||
receive_confirmed_info.confirmed_height = receive_details->height;
|
||||
receive_confirmed_info.iterated_frontier = receive_details->hash;
|
||||
}
|
||||
else
|
||||
{
|
||||
accounts_confirmed_info.emplace (std::piecewise_construct, std::forward_as_tuple (receive_details->account), std::forward_as_tuple (receive_details->height, receive_details->hash));
|
||||
++accounts_confirmed_info_size;
|
||||
}
|
||||
|
||||
if (receive_details->next.is_initialized ())
|
||||
{
|
||||
preparation_data_a.next_in_receive_chain = top_and_next_hash{ receive_details->top_level, receive_details->next, receive_details->height + 1 };
|
||||
}
|
||||
else
|
||||
{
|
||||
preparation_data_a.checkpoints.erase (std::remove (preparation_data_a.checkpoints.begin (), preparation_data_a.checkpoints.end (), receive_details->hash), preparation_data_a.checkpoints.end ());
|
||||
}
|
||||
|
||||
pending_writes.emplace_back (receive_details->account, receive_details->bottom_height, receive_details->bottom_most, receive_details->height, receive_details->hash);
|
||||
++pending_writes_size;
|
||||
}
|
||||
}
|
||||
|
||||
void nano::confirmation_height_bounded::cement_blocks (nano::write_guard & scoped_write_guard_a)
|
||||
{
|
||||
// Will contain all blocks that have been cemented (bounded by batch_write_size)
|
||||
// and will get run through the cemented observer callback
|
||||
std::vector<std::shared_ptr<nano::block>> cemented_blocks;
|
||||
auto const maximum_batch_write_time = 250; // milliseconds
|
||||
auto const maximum_batch_write_time_increase_cutoff = maximum_batch_write_time - (maximum_batch_write_time / 5);
|
||||
auto const amount_to_change = batch_write_size / 10; // 10%
|
||||
auto const minimum_batch_write_size = 16384u;
|
||||
nano::timer<> cemented_batch_timer;
|
||||
auto error = false;
|
||||
{
|
||||
// This only writes to the confirmation_height table and is the only place to do so in a single process
|
||||
auto transaction (ledger.store.tx_begin_write ({}, { nano::tables::confirmation_height }));
|
||||
cemented_batch_timer.start ();
|
||||
// Cement all pending entries, each entry is specific to an account and contains the least amount
|
||||
// of blocks to retain consistent cementing across all account chains to genesis.
|
||||
while (!error && !pending_writes.empty ())
|
||||
{
|
||||
auto const & pending = pending_writes.front ();
|
||||
auto const & account = pending.account;
|
||||
|
||||
auto write_confirmation_height = [&account, &ledger = ledger, &transaction] (uint64_t num_blocks_cemented, uint64_t confirmation_height, nano::block_hash const & confirmed_frontier) {
|
||||
#ifndef NDEBUG
|
||||
// Extra debug checks
|
||||
nano::confirmation_height_info confirmation_height_info;
|
||||
ledger.store.confirmation_height.get (transaction, account, confirmation_height_info);
|
||||
auto block = ledger.block (transaction, confirmed_frontier);
|
||||
debug_assert (block != nullptr);
|
||||
debug_assert (block->sideband ().height == confirmation_height_info.height + num_blocks_cemented);
|
||||
#endif
|
||||
ledger.store.confirmation_height.put (transaction, account, nano::confirmation_height_info{ confirmation_height, confirmed_frontier });
|
||||
ledger.cache.cemented_count += num_blocks_cemented;
|
||||
ledger.stats.add (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in, num_blocks_cemented);
|
||||
ledger.stats.add (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_bounded, nano::stat::dir::in, num_blocks_cemented);
|
||||
};
|
||||
|
||||
nano::confirmation_height_info confirmation_height_info;
|
||||
ledger.store.confirmation_height.get (transaction, pending.account, confirmation_height_info);
|
||||
|
||||
// Some blocks need to be cemented at least
|
||||
if (pending.top_height > confirmation_height_info.height)
|
||||
{
|
||||
// The highest hash which will be cemented
|
||||
nano::block_hash new_cemented_frontier;
|
||||
uint64_t num_blocks_confirmed = 0;
|
||||
uint64_t start_height = 0;
|
||||
if (pending.bottom_height > confirmation_height_info.height)
|
||||
{
|
||||
new_cemented_frontier = pending.bottom_hash;
|
||||
// If we are higher than the cemented frontier, we should be exactly 1 block above
|
||||
debug_assert (pending.bottom_height == confirmation_height_info.height + 1);
|
||||
num_blocks_confirmed = pending.top_height - pending.bottom_height + 1;
|
||||
start_height = pending.bottom_height;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto block = ledger.block (transaction, confirmation_height_info.frontier);
|
||||
new_cemented_frontier = block->sideband ().successor;
|
||||
num_blocks_confirmed = pending.top_height - confirmation_height_info.height;
|
||||
start_height = confirmation_height_info.height + 1;
|
||||
}
|
||||
|
||||
auto total_blocks_cemented = 0;
|
||||
auto block = ledger.block (transaction, new_cemented_frontier);
|
||||
|
||||
// Cementing starts from the bottom of the chain and works upwards. This is because chains can have effectively
|
||||
// an infinite number of send/change blocks in a row. We don't want to hold the write transaction open for too long.
|
||||
for (auto num_blocks_iterated = 0; num_blocks_confirmed - num_blocks_iterated != 0; ++num_blocks_iterated)
|
||||
{
|
||||
if (!block)
|
||||
{
|
||||
logger.critical (nano::log::type::conf_processor_bounded, "Failed to write confirmation height for block {} (bounded processor)", new_cemented_frontier.to_string ());
|
||||
|
||||
// Undo any blocks about to be cemented from this account for this pending write.
|
||||
cemented_blocks.erase (cemented_blocks.end () - num_blocks_iterated, cemented_blocks.end ());
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
auto last_iteration = (num_blocks_confirmed - num_blocks_iterated) == 1;
|
||||
|
||||
cemented_blocks.emplace_back (block);
|
||||
|
||||
// Flush these callbacks and continue as we write in batches (ideally maximum 250ms) to not hold write db transaction for too long.
|
||||
// Include a tolerance to save having to potentially wait on the block processor if the number of blocks to cement is only a bit higher than the max.
|
||||
if (cemented_blocks.size () > batch_write_size + (batch_write_size / 10))
|
||||
{
|
||||
auto time_spent_cementing = cemented_batch_timer.since_start ().count ();
|
||||
auto num_blocks_cemented = num_blocks_iterated - total_blocks_cemented + 1;
|
||||
total_blocks_cemented += num_blocks_cemented;
|
||||
write_confirmation_height (num_blocks_cemented, start_height + total_blocks_cemented - 1, new_cemented_frontier);
|
||||
transaction.commit ();
|
||||
|
||||
// Update the maximum amount of blocks to write next time based on the time it took to cement this batch.
|
||||
if (time_spent_cementing > maximum_batch_write_time)
|
||||
{
|
||||
// Reduce (unless we have hit a floor)
|
||||
batch_write_size = std::max<uint64_t> (minimum_batch_write_size, batch_write_size - amount_to_change);
|
||||
}
|
||||
else if (time_spent_cementing < maximum_batch_write_time_increase_cutoff)
|
||||
{
|
||||
// Increase amount of blocks written for next batch if the time for writing this one is sufficiently lower than the max time to warrant changing
|
||||
batch_write_size += amount_to_change;
|
||||
}
|
||||
|
||||
scoped_write_guard_a.release ();
|
||||
notify_observers_callback (cemented_blocks);
|
||||
cemented_blocks.clear ();
|
||||
|
||||
// Only aquire transaction if there are blocks left
|
||||
if (!(last_iteration && pending_writes.size () == 1))
|
||||
{
|
||||
scoped_write_guard_a = write_database_queue.wait (nano::writer::confirmation_height);
|
||||
transaction.renew ();
|
||||
}
|
||||
cemented_batch_timer.restart ();
|
||||
}
|
||||
|
||||
// Get the next block in the chain until we have reached the final desired one
|
||||
if (!last_iteration)
|
||||
{
|
||||
new_cemented_frontier = block->sideband ().successor;
|
||||
block = ledger.block (transaction, new_cemented_frontier);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Confirm it is indeed the last one
|
||||
debug_assert (new_cemented_frontier == pending.top_hash);
|
||||
}
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
// There was an error writing a block, do not process any more
|
||||
break;
|
||||
}
|
||||
|
||||
auto num_blocks_cemented = num_blocks_confirmed - total_blocks_cemented;
|
||||
if (num_blocks_cemented > 0)
|
||||
{
|
||||
write_confirmation_height (num_blocks_cemented, pending.top_height, new_cemented_frontier);
|
||||
}
|
||||
}
|
||||
|
||||
auto it = accounts_confirmed_info.find (pending.account);
|
||||
if (it != accounts_confirmed_info.cend () && it->second.confirmed_height == pending.top_height)
|
||||
{
|
||||
accounts_confirmed_info.erase (pending.account);
|
||||
--accounts_confirmed_info_size;
|
||||
}
|
||||
pending_writes.pop_front ();
|
||||
--pending_writes_size;
|
||||
}
|
||||
}
|
||||
|
||||
auto time_spent_cementing = cemented_batch_timer.since_start ();
|
||||
|
||||
// Scope guard could have been released earlier (0 cemented_blocks would indicate that)
|
||||
if (scoped_write_guard_a.is_owned () && !cemented_blocks.empty ())
|
||||
{
|
||||
scoped_write_guard_a.release ();
|
||||
notify_observers_callback (cemented_blocks);
|
||||
}
|
||||
|
||||
// Bail if there was an error. This indicates that there was a fatal issue with the ledger
|
||||
// (the blocks probably got rolled back when they shouldn't have).
|
||||
release_assert (!error);
|
||||
|
||||
if (time_spent_cementing.count () > maximum_batch_write_time)
|
||||
{
|
||||
// Reduce (unless we have hit a floor)
|
||||
batch_write_size = std::max<uint64_t> (minimum_batch_write_size, batch_write_size - amount_to_change);
|
||||
}
|
||||
|
||||
debug_assert (pending_writes.empty ());
|
||||
debug_assert (pending_writes_size == 0);
|
||||
timer.restart ();
|
||||
}
|
||||
|
||||
bool nano::confirmation_height_bounded::pending_empty () const
|
||||
{
|
||||
return pending_writes.empty ();
|
||||
}
|
||||
|
||||
void nano::confirmation_height_bounded::clear_process_vars ()
|
||||
{
|
||||
accounts_confirmed_info.clear ();
|
||||
accounts_confirmed_info_size = 0;
|
||||
}
|
||||
|
||||
nano::confirmation_height_bounded::receive_chain_details::receive_chain_details (nano::account const & account_a, uint64_t height_a, nano::block_hash const & hash_a, nano::block_hash const & top_level_a, boost::optional<nano::block_hash> next_a, uint64_t bottom_height_a, nano::block_hash const & bottom_most_a) :
|
||||
account (account_a),
|
||||
height (height_a),
|
||||
hash (hash_a),
|
||||
top_level (top_level_a),
|
||||
next (next_a),
|
||||
bottom_height (bottom_height_a),
|
||||
bottom_most (bottom_most_a)
|
||||
{
|
||||
}
|
||||
|
||||
nano::confirmation_height_bounded::write_details::write_details (nano::account const & account_a, uint64_t bottom_height_a, nano::block_hash const & bottom_hash_a, uint64_t top_height_a, nano::block_hash const & top_hash_a) :
|
||||
account (account_a),
|
||||
bottom_height (bottom_height_a),
|
||||
bottom_hash (bottom_hash_a),
|
||||
top_height (top_height_a),
|
||||
top_hash (top_hash_a)
|
||||
{
|
||||
}
|
||||
|
||||
nano::confirmation_height_bounded::receive_source_pair::receive_source_pair (confirmation_height_bounded::receive_chain_details const & receive_details_a, const block_hash & source_a) :
|
||||
receive_details (receive_details_a),
|
||||
source_hash (source_a)
|
||||
{
|
||||
}
|
||||
|
||||
nano::confirmation_height_bounded::confirmed_info::confirmed_info (uint64_t confirmed_height_a, nano::block_hash const & iterated_frontier_a) :
|
||||
confirmed_height (confirmed_height_a),
|
||||
iterated_frontier (iterated_frontier_a)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<nano::container_info_component> nano::collect_container_info (confirmation_height_bounded & confirmation_height_bounded, std::string const & name_a)
|
||||
{
|
||||
auto composite = std::make_unique<container_info_composite> (name_a);
|
||||
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "pending_writes", confirmation_height_bounded.pending_writes_size, sizeof (decltype (confirmation_height_bounded.pending_writes)::value_type) }));
|
||||
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "accounts_confirmed_info", confirmation_height_bounded.accounts_confirmed_info_size, sizeof (decltype (confirmation_height_bounded.accounts_confirmed_info)::value_type) }));
|
||||
return composite;
|
||||
}
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/logging.hpp>
|
||||
#include <nano/lib/numbers.hpp>
|
||||
#include <nano/lib/relaxed_atomic.hpp>
|
||||
#include <nano/lib/threading.hpp>
|
||||
#include <nano/lib/timer.hpp>
|
||||
#include <nano/store/component.hpp>
|
||||
|
||||
#include <boost/circular_buffer.hpp>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class ledger;
|
||||
class write_database_queue;
|
||||
class write_guard;
|
||||
|
||||
class confirmation_height_bounded final
|
||||
{
|
||||
public:
|
||||
confirmation_height_bounded (nano::ledger &, nano::write_database_queue &, std::chrono::milliseconds batch_separate_pending_min_time, nano::logger &, std::atomic<bool> & stopped, uint64_t & batch_write_size, std::function<void (std::vector<std::shared_ptr<nano::block>> const &)> const & cemented_callback, std::function<void (nano::block_hash const &)> const & already_cemented_callback, std::function<uint64_t ()> const & awaiting_processing_size_query);
|
||||
|
||||
bool pending_empty () const;
|
||||
void clear_process_vars ();
|
||||
void process (std::shared_ptr<nano::block> original_block);
|
||||
void cement_blocks (nano::write_guard & scoped_write_guard_a);
|
||||
|
||||
private:
|
||||
class top_and_next_hash final
|
||||
{
|
||||
public:
|
||||
nano::block_hash top;
|
||||
boost::optional<nano::block_hash> next;
|
||||
uint64_t next_height;
|
||||
};
|
||||
|
||||
class confirmed_info
|
||||
{
|
||||
public:
|
||||
confirmed_info (uint64_t confirmed_height_a, nano::block_hash const & iterated_frontier);
|
||||
uint64_t confirmed_height;
|
||||
nano::block_hash iterated_frontier;
|
||||
};
|
||||
|
||||
class write_details final
|
||||
{
|
||||
public:
|
||||
write_details (nano::account const &, uint64_t, nano::block_hash const &, uint64_t, nano::block_hash const &);
|
||||
nano::account account;
|
||||
// This is the first block hash (bottom most) which is not cemented
|
||||
uint64_t bottom_height;
|
||||
nano::block_hash bottom_hash;
|
||||
// Desired cemented frontier
|
||||
uint64_t top_height;
|
||||
nano::block_hash top_hash;
|
||||
};
|
||||
|
||||
/** The maximum number of blocks to be read in while iterating over a long account chain */
|
||||
uint64_t const batch_read_size = 65536;
|
||||
|
||||
/** The maximum number of various containers to keep the memory bounded */
|
||||
uint32_t const max_items{ 131072 };
|
||||
|
||||
// All of the atomic variables here just track the size for use in collect_container_info.
|
||||
// This is so that no mutexes are needed during the algorithm itself, which would otherwise be needed
|
||||
// for the sake of a rarely used RPC call for debugging purposes. As such the sizes are not being acted
|
||||
// upon in any way (does not synchronize with any other data).
|
||||
// This allows the load and stores to use relaxed atomic memory ordering.
|
||||
std::deque<write_details> pending_writes;
|
||||
nano::relaxed_atomic_integral<uint64_t> pending_writes_size{ 0 };
|
||||
uint32_t const pending_writes_max_size{ max_items };
|
||||
/* Holds confirmation height/cemented frontier in memory for accounts while iterating */
|
||||
std::unordered_map<account, confirmed_info> accounts_confirmed_info;
|
||||
nano::relaxed_atomic_integral<uint64_t> accounts_confirmed_info_size{ 0 };
|
||||
|
||||
class receive_chain_details final
|
||||
{
|
||||
public:
|
||||
receive_chain_details (nano::account const &, uint64_t, nano::block_hash const &, nano::block_hash const &, boost::optional<nano::block_hash>, uint64_t, nano::block_hash const &);
|
||||
nano::account account;
|
||||
uint64_t height;
|
||||
nano::block_hash hash;
|
||||
nano::block_hash top_level;
|
||||
boost::optional<nano::block_hash> next;
|
||||
uint64_t bottom_height;
|
||||
nano::block_hash bottom_most;
|
||||
};
|
||||
|
||||
class preparation_data final
|
||||
{
|
||||
public:
|
||||
store::transaction const & transaction;
|
||||
nano::block_hash const & top_most_non_receive_block_hash;
|
||||
bool already_cemented;
|
||||
boost::circular_buffer_space_optimized<nano::block_hash> & checkpoints;
|
||||
decltype (accounts_confirmed_info.begin ()) account_it;
|
||||
nano::confirmation_height_info const & confirmation_height_info;
|
||||
nano::account const & account;
|
||||
uint64_t bottom_height;
|
||||
nano::block_hash const & bottom_most;
|
||||
boost::optional<receive_chain_details> & receive_details;
|
||||
boost::optional<top_and_next_hash> & next_in_receive_chain;
|
||||
};
|
||||
|
||||
class receive_source_pair final
|
||||
{
|
||||
public:
|
||||
receive_source_pair (receive_chain_details const &, nano::block_hash const &);
|
||||
|
||||
receive_chain_details receive_details;
|
||||
nano::block_hash source_hash;
|
||||
};
|
||||
|
||||
nano::timer<std::chrono::milliseconds> timer;
|
||||
|
||||
top_and_next_hash get_next_block (boost::optional<top_and_next_hash> const &, boost::circular_buffer_space_optimized<nano::block_hash> const &, boost::circular_buffer_space_optimized<receive_source_pair> const & receive_source_pairs, boost::optional<receive_chain_details> &, nano::block const & original_block);
|
||||
nano::block_hash get_least_unconfirmed_hash_from_top_level (store::transaction const &, nano::block_hash const &, nano::account const &, nano::confirmation_height_info const &, uint64_t &);
|
||||
void prepare_iterated_blocks_for_cementing (preparation_data &);
|
||||
bool iterate (store::read_transaction const &, uint64_t, nano::block_hash const &, boost::circular_buffer_space_optimized<nano::block_hash> &, nano::block_hash &, nano::block_hash const &, boost::circular_buffer_space_optimized<receive_source_pair> &, nano::account const &);
|
||||
|
||||
nano::ledger & ledger;
|
||||
nano::write_database_queue & write_database_queue;
|
||||
std::chrono::milliseconds batch_separate_pending_min_time;
|
||||
nano::logger & logger;
|
||||
std::atomic<bool> & stopped;
|
||||
uint64_t & batch_write_size;
|
||||
std::function<void (std::vector<std::shared_ptr<nano::block>> const &)> notify_observers_callback;
|
||||
std::function<void (nano::block_hash const &)> notify_block_already_cemented_observers_callback;
|
||||
std::function<uint64_t ()> awaiting_processing_size_callback;
|
||||
|
||||
friend std::unique_ptr<nano::container_info_component> collect_container_info (confirmation_height_bounded &, std::string const & name_a);
|
||||
};
|
||||
|
||||
std::unique_ptr<nano::container_info_component> collect_container_info (confirmation_height_bounded &, std::string const & name_a);
|
||||
}
|
||||
|
|
@ -1,247 +0,0 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/lib/numbers.hpp>
|
||||
#include <nano/lib/thread_roles.hpp>
|
||||
#include <nano/lib/utility.hpp>
|
||||
#include <nano/node/confirmation_height_processor.hpp>
|
||||
#include <nano/node/write_database_queue.hpp>
|
||||
#include <nano/secure/common.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
|
||||
#include <boost/thread/latch.hpp>
|
||||
|
||||
nano::confirmation_height_processor::confirmation_height_processor (nano::ledger & ledger_a, nano::write_database_queue & write_database_queue_a, std::chrono::milliseconds batch_separate_pending_min_time_a, nano::logger & logger_a, boost::latch & latch, confirmation_height_mode mode_a) :
|
||||
ledger (ledger_a),
|
||||
write_database_queue (write_database_queue_a),
|
||||
unbounded_processor (
|
||||
ledger_a, write_database_queue_a, batch_separate_pending_min_time_a, logger_a, stopped, batch_write_size,
|
||||
/* cemented_callback */ [this] (auto & cemented_blocks) { this->notify_cemented (cemented_blocks); },
|
||||
/* already cemented_callback */ [this] (auto const & block_hash_a) { this->notify_already_cemented (block_hash_a); },
|
||||
/* awaiting_processing_size_query */ [this] () { return this->awaiting_processing_size (); }),
|
||||
bounded_processor (
|
||||
ledger_a, write_database_queue_a, batch_separate_pending_min_time_a, logger_a, stopped, batch_write_size,
|
||||
/* cemented_callback */ [this] (auto & cemented_blocks) { this->notify_cemented (cemented_blocks); },
|
||||
/* already cemented_callback */ [this] (auto const & block_hash_a) { this->notify_already_cemented (block_hash_a); },
|
||||
/* awaiting_processing_size_query */ [this] () { return this->awaiting_processing_size (); }),
|
||||
thread ([this, &latch, mode_a] () {
|
||||
nano::thread_role::set (nano::thread_role::name::confirmation_height_processing);
|
||||
// Do not start running the processing thread until other threads have finished their operations
|
||||
latch.wait ();
|
||||
this->run (mode_a);
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
nano::confirmation_height_processor::~confirmation_height_processor ()
|
||||
{
|
||||
stop ();
|
||||
}
|
||||
|
||||
void nano::confirmation_height_processor::stop ()
|
||||
{
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (mutex);
|
||||
stopped = true;
|
||||
}
|
||||
condition.notify_one ();
|
||||
if (thread.joinable ())
|
||||
{
|
||||
thread.join ();
|
||||
}
|
||||
}
|
||||
|
||||
void nano::confirmation_height_processor::run (confirmation_height_mode mode_a)
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lk (mutex);
|
||||
while (!stopped)
|
||||
{
|
||||
if (!paused && !awaiting_processing.empty ())
|
||||
{
|
||||
lk.unlock ();
|
||||
if (bounded_processor.pending_empty () && unbounded_processor.pending_empty ())
|
||||
{
|
||||
lk.lock ();
|
||||
original_hashes_pending.clear ();
|
||||
lk.unlock ();
|
||||
}
|
||||
|
||||
set_next_hash ();
|
||||
|
||||
auto const num_blocks_to_use_unbounded = confirmation_height::unbounded_cutoff;
|
||||
auto blocks_within_automatic_unbounded_selection = (ledger.cache.block_count < num_blocks_to_use_unbounded || ledger.cache.block_count - num_blocks_to_use_unbounded < ledger.cache.cemented_count);
|
||||
|
||||
// Don't want to mix up pending writes across different processors
|
||||
auto valid_unbounded = (mode_a == confirmation_height_mode::automatic && blocks_within_automatic_unbounded_selection && bounded_processor.pending_empty ());
|
||||
auto force_unbounded = (!unbounded_processor.pending_empty () || mode_a == confirmation_height_mode::unbounded);
|
||||
if (force_unbounded || valid_unbounded)
|
||||
{
|
||||
debug_assert (bounded_processor.pending_empty ());
|
||||
unbounded_processor.process (original_block);
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_assert (mode_a == confirmation_height_mode::bounded || mode_a == confirmation_height_mode::automatic);
|
||||
debug_assert (unbounded_processor.pending_empty ());
|
||||
bounded_processor.process (original_block);
|
||||
}
|
||||
|
||||
lk.lock ();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto lock_and_cleanup = [&lk, this] () {
|
||||
lk.lock ();
|
||||
original_block = nullptr;
|
||||
original_hashes_pending.clear ();
|
||||
bounded_processor.clear_process_vars ();
|
||||
unbounded_processor.clear_process_vars ();
|
||||
};
|
||||
|
||||
if (!paused)
|
||||
{
|
||||
lk.unlock ();
|
||||
|
||||
// If there are blocks pending cementing, then make sure we flush out the remaining writes
|
||||
if (!bounded_processor.pending_empty ())
|
||||
{
|
||||
debug_assert (unbounded_processor.pending_empty ());
|
||||
{
|
||||
auto scoped_write_guard = write_database_queue.wait (nano::writer::confirmation_height);
|
||||
bounded_processor.cement_blocks (scoped_write_guard);
|
||||
}
|
||||
lock_and_cleanup ();
|
||||
}
|
||||
else if (!unbounded_processor.pending_empty ())
|
||||
{
|
||||
debug_assert (bounded_processor.pending_empty ());
|
||||
{
|
||||
auto scoped_write_guard = write_database_queue.wait (nano::writer::confirmation_height);
|
||||
unbounded_processor.cement_blocks (scoped_write_guard);
|
||||
}
|
||||
lock_and_cleanup ();
|
||||
}
|
||||
else
|
||||
{
|
||||
lock_and_cleanup ();
|
||||
// A block could have been confirmed during the re-locking
|
||||
if (awaiting_processing.empty ())
|
||||
{
|
||||
condition.wait (lk);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pausing is only utilised in some tests to help prevent it processing added blocks until required.
|
||||
original_block = nullptr;
|
||||
condition.wait (lk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pausing only affects processing new blocks, not the current one being processed. Currently only used in tests
|
||||
void nano::confirmation_height_processor::pause ()
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lk (mutex);
|
||||
paused = true;
|
||||
}
|
||||
|
||||
void nano::confirmation_height_processor::unpause ()
|
||||
{
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lk (mutex);
|
||||
paused = false;
|
||||
}
|
||||
condition.notify_one ();
|
||||
}
|
||||
|
||||
void nano::confirmation_height_processor::add (std::shared_ptr<nano::block> const & block_a)
|
||||
{
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lk (mutex);
|
||||
awaiting_processing.get<tag_sequence> ().emplace_back (block_a);
|
||||
}
|
||||
condition.notify_one ();
|
||||
}
|
||||
|
||||
void nano::confirmation_height_processor::set_next_hash ()
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (mutex);
|
||||
debug_assert (!awaiting_processing.empty ());
|
||||
original_block = awaiting_processing.get<tag_sequence> ().front ().block;
|
||||
original_hashes_pending.insert (original_block->hash ());
|
||||
awaiting_processing.get<tag_sequence> ().pop_front ();
|
||||
}
|
||||
|
||||
// Not thread-safe, only call before this processor has begun cementing
|
||||
void nano::confirmation_height_processor::add_cemented_observer (std::function<void (std::shared_ptr<nano::block> const &)> const & callback_a)
|
||||
{
|
||||
cemented_observers.push_back (callback_a);
|
||||
}
|
||||
|
||||
// Not thread-safe, only call before this processor has begun cementing
|
||||
void nano::confirmation_height_processor::add_block_already_cemented_observer (std::function<void (nano::block_hash const &)> const & callback_a)
|
||||
{
|
||||
block_already_cemented_observers.push_back (callback_a);
|
||||
}
|
||||
|
||||
void nano::confirmation_height_processor::notify_cemented (std::vector<std::shared_ptr<nano::block>> const & cemented_blocks)
|
||||
{
|
||||
for (auto const & block_callback_data : cemented_blocks)
|
||||
{
|
||||
for (auto const & observer : cemented_observers)
|
||||
{
|
||||
observer (block_callback_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nano::confirmation_height_processor::notify_already_cemented (nano::block_hash const & hash_already_cemented_a)
|
||||
{
|
||||
for (auto const & observer : block_already_cemented_observers)
|
||||
{
|
||||
observer (hash_already_cemented_a);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<nano::container_info_component> nano::collect_container_info (confirmation_height_processor & confirmation_height_processor_a, std::string const & name_a)
|
||||
{
|
||||
auto composite = std::make_unique<container_info_composite> (name_a);
|
||||
|
||||
std::size_t cemented_observers_count = confirmation_height_processor_a.cemented_observers.size ();
|
||||
std::size_t block_already_cemented_observers_count = confirmation_height_processor_a.block_already_cemented_observers.size ();
|
||||
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "cemented_observers", cemented_observers_count, sizeof (decltype (confirmation_height_processor_a.cemented_observers)::value_type) }));
|
||||
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "block_already_cemented_observers", block_already_cemented_observers_count, sizeof (decltype (confirmation_height_processor_a.block_already_cemented_observers)::value_type) }));
|
||||
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "awaiting_processing", confirmation_height_processor_a.awaiting_processing_size (), sizeof (decltype (confirmation_height_processor_a.awaiting_processing)::value_type) }));
|
||||
composite->add_component (collect_container_info (confirmation_height_processor_a.bounded_processor, "bounded_processor"));
|
||||
composite->add_component (collect_container_info (confirmation_height_processor_a.unbounded_processor, "unbounded_processor"));
|
||||
return composite;
|
||||
}
|
||||
|
||||
std::size_t nano::confirmation_height_processor::awaiting_processing_size () const
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (mutex);
|
||||
return awaiting_processing.size ();
|
||||
}
|
||||
|
||||
bool nano::confirmation_height_processor::is_processing_added_block (nano::block_hash const & hash_a) const
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (mutex);
|
||||
return original_hashes_pending.count (hash_a) > 0 || awaiting_processing.get<tag_hash> ().count (hash_a) > 0;
|
||||
}
|
||||
|
||||
bool nano::confirmation_height_processor::is_processing_block (nano::block_hash const & hash_a) const
|
||||
{
|
||||
return is_processing_added_block (hash_a) || unbounded_processor.has_iterated_over_block (hash_a);
|
||||
}
|
||||
|
||||
nano::block_hash nano::confirmation_height_processor::current () const
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lk (mutex);
|
||||
return original_block ? original_block->hash () : 0;
|
||||
}
|
||||
|
||||
std::reference_wrapper<nano::block_hash const> nano::confirmation_height_processor::block_wrapper::hash () const
|
||||
{
|
||||
return block->hash ();
|
||||
}
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/logging.hpp>
|
||||
#include <nano/lib/numbers.hpp>
|
||||
#include <nano/lib/timer.hpp>
|
||||
#include <nano/node/confirmation_height_bounded.hpp>
|
||||
#include <nano/node/confirmation_height_unbounded.hpp>
|
||||
#include <nano/secure/common.hpp>
|
||||
#include <nano/store/component.hpp>
|
||||
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/mem_fun.hpp>
|
||||
#include <boost/multi_index/sequenced_index.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace mi = boost::multi_index;
|
||||
namespace boost
|
||||
{
|
||||
class latch;
|
||||
}
|
||||
namespace nano
|
||||
{
|
||||
class ledger;
|
||||
class write_database_queue;
|
||||
|
||||
class confirmation_height_processor final
|
||||
{
|
||||
public:
|
||||
confirmation_height_processor (nano::ledger &, nano::write_database_queue &, std::chrono::milliseconds, nano::logger &, boost::latch & initialized_latch, confirmation_height_mode = confirmation_height_mode::automatic);
|
||||
~confirmation_height_processor ();
|
||||
|
||||
void pause ();
|
||||
void unpause ();
|
||||
void stop ();
|
||||
void add (std::shared_ptr<nano::block> const &);
|
||||
void run (confirmation_height_mode);
|
||||
std::size_t awaiting_processing_size () const;
|
||||
bool is_processing_added_block (nano::block_hash const & hash_a) const;
|
||||
bool is_processing_block (nano::block_hash const &) const;
|
||||
nano::block_hash current () const;
|
||||
|
||||
/*
|
||||
* Called for each newly cemented block
|
||||
* Called from confirmation height processor thread
|
||||
*/
|
||||
void add_cemented_observer (std::function<void (std::shared_ptr<nano::block> const &)> const &);
|
||||
/*
|
||||
* Called when the block was added to the confirmation height processor but is already confirmed
|
||||
* Called from confirmation height processor thread
|
||||
*/
|
||||
void add_block_already_cemented_observer (std::function<void (nano::block_hash const &)> const &);
|
||||
|
||||
private:
|
||||
mutable nano::mutex mutex{ mutex_identifier (mutexes::confirmation_height_processor) };
|
||||
|
||||
// Hashes which have been added to the confirmation height processor, but not yet processed
|
||||
struct block_wrapper
|
||||
{
|
||||
explicit block_wrapper (std::shared_ptr<nano::block> const & block_a) :
|
||||
block (block_a)
|
||||
{
|
||||
}
|
||||
|
||||
std::reference_wrapper<nano::block_hash const> hash () const;
|
||||
|
||||
std::shared_ptr<nano::block> block;
|
||||
};
|
||||
// clang-format off
|
||||
class tag_sequence {};
|
||||
class tag_hash {};
|
||||
boost::multi_index_container<block_wrapper,
|
||||
mi::indexed_by<
|
||||
mi::sequenced<mi::tag<tag_sequence>>,
|
||||
mi::hashed_unique<mi::tag<tag_hash>,
|
||||
mi::const_mem_fun<block_wrapper, std::reference_wrapper<nano::block_hash const>, &block_wrapper::hash>>>> awaiting_processing;
|
||||
// clang-format on
|
||||
|
||||
// Hashes which have been added and processed, but have not been cemented
|
||||
std::unordered_set<nano::block_hash> original_hashes_pending;
|
||||
bool paused{ false };
|
||||
|
||||
/** This is the last block popped off the confirmation height pending collection */
|
||||
std::shared_ptr<nano::block> original_block;
|
||||
|
||||
nano::condition_variable condition;
|
||||
std::atomic<bool> stopped{ false };
|
||||
// No mutex needed for the observers as these should be set up during initialization of the node
|
||||
std::vector<std::function<void (std::shared_ptr<nano::block> const &)>> cemented_observers;
|
||||
std::vector<std::function<void (nano::block_hash const &)>> block_already_cemented_observers;
|
||||
|
||||
nano::ledger & ledger;
|
||||
nano::write_database_queue & write_database_queue;
|
||||
/** The maximum amount of blocks to write at once. This is dynamically modified by the bounded processor based on previous write performance **/
|
||||
uint64_t batch_write_size{ 16384 };
|
||||
|
||||
confirmation_height_unbounded unbounded_processor;
|
||||
confirmation_height_bounded bounded_processor;
|
||||
std::thread thread;
|
||||
|
||||
void set_next_hash ();
|
||||
void notify_cemented (std::vector<std::shared_ptr<nano::block>> const &);
|
||||
void notify_already_cemented (nano::block_hash const &);
|
||||
|
||||
friend std::unique_ptr<container_info_component> collect_container_info (confirmation_height_processor &, std::string const &);
|
||||
|
||||
private: // Tests
|
||||
friend class confirmation_height_pending_observer_callbacks_Test;
|
||||
friend class confirmation_height_dependent_election_Test;
|
||||
friend class confirmation_height_dependent_election_after_already_cemented_Test;
|
||||
friend class confirmation_height_dynamic_algorithm_no_transition_while_pending_Test;
|
||||
friend class confirmation_height_many_accounts_many_confirmations_Test;
|
||||
friend class confirmation_height_long_chains_Test;
|
||||
friend class confirmation_height_many_accounts_single_confirmation_Test;
|
||||
friend class request_aggregator_cannot_vote_Test;
|
||||
friend class active_transactions_pessimistic_elections_Test;
|
||||
};
|
||||
|
||||
std::unique_ptr<container_info_component> collect_container_info (confirmation_height_processor &, std::string const &);
|
||||
}
|
||||
|
|
@ -1,498 +0,0 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/lib/stats.hpp>
|
||||
#include <nano/node/confirmation_height_unbounded.hpp>
|
||||
#include <nano/node/write_database_queue.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/store/block.hpp>
|
||||
#include <nano/store/confirmation_height.hpp>
|
||||
#include <nano/store/pruned.hpp>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
nano::confirmation_height_unbounded::confirmation_height_unbounded (nano::ledger & ledger_a, nano::write_database_queue & write_database_queue_a, std::chrono::milliseconds batch_separate_pending_min_time_a, nano::logger & logger_a, std::atomic<bool> & stopped_a, uint64_t & batch_write_size_a, std::function<void (std::vector<std::shared_ptr<nano::block>> const &)> const & notify_observers_callback_a, std::function<void (nano::block_hash const &)> const & notify_block_already_cemented_observers_callback_a, std::function<uint64_t ()> const & awaiting_processing_size_callback_a) :
|
||||
ledger (ledger_a),
|
||||
write_database_queue (write_database_queue_a),
|
||||
batch_separate_pending_min_time (batch_separate_pending_min_time_a),
|
||||
logger (logger_a),
|
||||
stopped (stopped_a),
|
||||
batch_write_size (batch_write_size_a),
|
||||
notify_observers_callback (notify_observers_callback_a),
|
||||
notify_block_already_cemented_observers_callback (notify_block_already_cemented_observers_callback_a),
|
||||
awaiting_processing_size_callback (awaiting_processing_size_callback_a)
|
||||
{
|
||||
}
|
||||
|
||||
void nano::confirmation_height_unbounded::process (std::shared_ptr<nano::block> original_block)
|
||||
{
|
||||
if (pending_empty ())
|
||||
{
|
||||
clear_process_vars ();
|
||||
timer.restart ();
|
||||
}
|
||||
std::shared_ptr<conf_height_details> receive_details;
|
||||
auto current = original_block->hash ();
|
||||
std::vector<nano::block_hash> orig_block_callback_data;
|
||||
|
||||
std::vector<receive_source_pair> receive_source_pairs;
|
||||
release_assert (receive_source_pairs.empty ());
|
||||
|
||||
bool first_iter = true;
|
||||
auto read_transaction (ledger.store.tx_begin_read ());
|
||||
|
||||
do
|
||||
{
|
||||
if (!receive_source_pairs.empty ())
|
||||
{
|
||||
receive_details = receive_source_pairs.back ().receive_details;
|
||||
current = receive_source_pairs.back ().source_hash;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If receive_details is set then this is the final iteration and we are back to the original chain.
|
||||
// We need to confirm any blocks below the original hash (incl self) and the first receive block
|
||||
// (if the original block is not already a receive)
|
||||
if (receive_details)
|
||||
{
|
||||
current = original_block->hash ();
|
||||
receive_details = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<nano::block> block;
|
||||
if (first_iter)
|
||||
{
|
||||
debug_assert (current == original_block->hash ());
|
||||
// This is the original block passed so can use it directly
|
||||
block = original_block;
|
||||
nano::lock_guard<nano::mutex> guard (block_cache_mutex);
|
||||
block_cache[original_block->hash ()] = original_block;
|
||||
}
|
||||
else
|
||||
{
|
||||
block = get_block_and_sideband (current, read_transaction);
|
||||
}
|
||||
if (!block)
|
||||
{
|
||||
logger.critical (nano::log::type::conf_processor_unbounded, "Ledger mismatch trying to set confirmation height for block {} (unbounded processor)", current.to_string ());
|
||||
}
|
||||
release_assert (block);
|
||||
|
||||
auto account = block->account ();
|
||||
|
||||
auto block_height = block->sideband ().height;
|
||||
uint64_t confirmation_height = 0;
|
||||
auto account_it = confirmed_iterated_pairs.find (account);
|
||||
if (account_it != confirmed_iterated_pairs.cend ())
|
||||
{
|
||||
confirmation_height = account_it->second.confirmed_height;
|
||||
}
|
||||
else
|
||||
{
|
||||
nano::confirmation_height_info confirmation_height_info;
|
||||
ledger.store.confirmation_height.get (read_transaction, account, confirmation_height_info);
|
||||
confirmation_height = confirmation_height_info.height;
|
||||
|
||||
// This block was added to the confirmation height processor but is already confirmed
|
||||
if (first_iter && confirmation_height >= block_height)
|
||||
{
|
||||
debug_assert (current == original_block->hash ());
|
||||
notify_block_already_cemented_observers_callback (original_block->hash ());
|
||||
}
|
||||
}
|
||||
auto iterated_height = confirmation_height;
|
||||
if (account_it != confirmed_iterated_pairs.cend () && account_it->second.iterated_height > iterated_height)
|
||||
{
|
||||
iterated_height = account_it->second.iterated_height;
|
||||
}
|
||||
|
||||
auto count_before_receive = receive_source_pairs.size ();
|
||||
std::vector<nano::block_hash> block_callback_datas_required;
|
||||
auto already_traversed = iterated_height >= block_height;
|
||||
if (!already_traversed)
|
||||
{
|
||||
collect_unconfirmed_receive_and_sources_for_account (block_height, iterated_height, block, current, account, read_transaction, receive_source_pairs, block_callback_datas_required, orig_block_callback_data, original_block);
|
||||
}
|
||||
|
||||
// Exit early when the processor has been stopped, otherwise this function may take a
|
||||
// while (and hence keep the process running) if updating a long chain.
|
||||
if (stopped)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// No longer need the read transaction
|
||||
read_transaction.reset ();
|
||||
|
||||
// If this adds no more open or receive blocks, then we can now confirm this account as well as the linked open/receive block
|
||||
// Collect as pending any writes to the database and do them in bulk after a certain time.
|
||||
auto confirmed_receives_pending = (count_before_receive != receive_source_pairs.size ());
|
||||
if (!confirmed_receives_pending)
|
||||
{
|
||||
preparation_data preparation_data{ block_height, confirmation_height, iterated_height, account_it, account, receive_details, already_traversed, current, block_callback_datas_required, orig_block_callback_data };
|
||||
prepare_iterated_blocks_for_cementing (preparation_data);
|
||||
|
||||
if (!receive_source_pairs.empty ())
|
||||
{
|
||||
// Pop from the end
|
||||
receive_source_pairs.erase (receive_source_pairs.end () - 1);
|
||||
}
|
||||
}
|
||||
else if (block_height > iterated_height)
|
||||
{
|
||||
if (account_it != confirmed_iterated_pairs.cend ())
|
||||
{
|
||||
account_it->second.iterated_height = block_height;
|
||||
}
|
||||
else
|
||||
{
|
||||
confirmed_iterated_pairs.emplace (std::piecewise_construct, std::forward_as_tuple (account), std::forward_as_tuple (confirmation_height, block_height));
|
||||
++confirmed_iterated_pairs_size;
|
||||
}
|
||||
}
|
||||
|
||||
auto max_write_size_reached = (pending_writes.size () >= confirmation_height::unbounded_cutoff);
|
||||
// When there are a lot of pending confirmation height blocks, it is more efficient to
|
||||
// bulk some of them up to enable better write performance which becomes the bottleneck.
|
||||
auto min_time_exceeded = (timer.since_start () >= batch_separate_pending_min_time);
|
||||
auto finished_iterating = receive_source_pairs.empty ();
|
||||
auto no_pending = awaiting_processing_size_callback () == 0;
|
||||
auto should_output = finished_iterating && (no_pending || min_time_exceeded);
|
||||
|
||||
auto total_pending_write_block_count = std::accumulate (pending_writes.cbegin (), pending_writes.cend (), uint64_t (0), [] (uint64_t total, conf_height_details const & receive_details_a) {
|
||||
return total += receive_details_a.num_blocks_confirmed;
|
||||
});
|
||||
auto force_write = total_pending_write_block_count > batch_write_size;
|
||||
|
||||
if ((max_write_size_reached || should_output || force_write) && !pending_writes.empty ())
|
||||
{
|
||||
if (write_database_queue.process (nano::writer::confirmation_height))
|
||||
{
|
||||
auto scoped_write_guard = write_database_queue.pop ();
|
||||
cement_blocks (scoped_write_guard);
|
||||
}
|
||||
else if (force_write)
|
||||
{
|
||||
// Unbounded processor has grown too large, force a write
|
||||
auto scoped_write_guard = write_database_queue.wait (nano::writer::confirmation_height);
|
||||
cement_blocks (scoped_write_guard);
|
||||
}
|
||||
}
|
||||
|
||||
first_iter = false;
|
||||
read_transaction.renew ();
|
||||
} while ((!receive_source_pairs.empty () || current != original_block->hash ()) && !stopped);
|
||||
}
|
||||
|
||||
void nano::confirmation_height_unbounded::collect_unconfirmed_receive_and_sources_for_account (uint64_t block_height_a, uint64_t confirmation_height_a, std::shared_ptr<nano::block> const & block_a, nano::block_hash const & hash_a, nano::account const & account_a, store::read_transaction const & transaction_a, std::vector<receive_source_pair> & receive_source_pairs_a, std::vector<nano::block_hash> & block_callback_data_a, std::vector<nano::block_hash> & orig_block_callback_data_a, std::shared_ptr<nano::block> original_block)
|
||||
{
|
||||
debug_assert (block_a->hash () == hash_a);
|
||||
auto hash (hash_a);
|
||||
auto num_to_confirm = block_height_a - confirmation_height_a;
|
||||
|
||||
// Handle any sends above a receive
|
||||
auto is_original_block = (hash == original_block->hash ());
|
||||
auto hit_receive = false;
|
||||
auto first_iter = true;
|
||||
while ((num_to_confirm > 0) && !hash.is_zero () && !stopped)
|
||||
{
|
||||
std::shared_ptr<nano::block> block;
|
||||
if (first_iter)
|
||||
{
|
||||
debug_assert (hash == hash_a);
|
||||
block = block_a;
|
||||
nano::lock_guard<nano::mutex> guard (block_cache_mutex);
|
||||
block_cache[hash] = block_a;
|
||||
}
|
||||
else
|
||||
{
|
||||
block = get_block_and_sideband (hash, transaction_a);
|
||||
}
|
||||
|
||||
if (block)
|
||||
{
|
||||
if (block->is_receive () && ledger.block_exists (transaction_a, block->source ()))
|
||||
{
|
||||
if (!hit_receive && !block_callback_data_a.empty ())
|
||||
{
|
||||
// Add the callbacks to the associated receive to retrieve later
|
||||
debug_assert (!receive_source_pairs_a.empty ());
|
||||
auto & last_receive_details = receive_source_pairs_a.back ().receive_details;
|
||||
last_receive_details->source_block_callback_data.assign (block_callback_data_a.begin (), block_callback_data_a.end ());
|
||||
block_callback_data_a.clear ();
|
||||
}
|
||||
|
||||
is_original_block = false;
|
||||
hit_receive = true;
|
||||
|
||||
auto block_height = confirmation_height_a + num_to_confirm;
|
||||
receive_source_pairs_a.emplace_back (std::make_shared<conf_height_details> (account_a, hash, block_height, 1, std::vector<nano::block_hash>{ hash }), block->source ());
|
||||
}
|
||||
else if (is_original_block)
|
||||
{
|
||||
orig_block_callback_data_a.push_back (hash);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!hit_receive)
|
||||
{
|
||||
// This block is cemented via a recieve, as opposed to below a receive being cemented
|
||||
block_callback_data_a.push_back (hash);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have hit a receive before, add the block to it
|
||||
auto & last_receive_details = receive_source_pairs_a.back ().receive_details;
|
||||
++last_receive_details->num_blocks_confirmed;
|
||||
last_receive_details->block_callback_data.push_back (hash);
|
||||
|
||||
implicit_receive_cemented_mapping[hash] = std::weak_ptr<conf_height_details> (last_receive_details);
|
||||
implicit_receive_cemented_mapping_size = implicit_receive_cemented_mapping.size ();
|
||||
}
|
||||
}
|
||||
|
||||
hash = block->previous ();
|
||||
}
|
||||
|
||||
--num_to_confirm;
|
||||
first_iter = false;
|
||||
}
|
||||
}
|
||||
|
||||
void nano::confirmation_height_unbounded::prepare_iterated_blocks_for_cementing (preparation_data & preparation_data_a)
|
||||
{
|
||||
auto receive_details = preparation_data_a.receive_details;
|
||||
auto block_height = preparation_data_a.block_height;
|
||||
if (block_height > preparation_data_a.confirmation_height)
|
||||
{
|
||||
// Check whether the previous block has been seen. If so, the rest of sends below have already been seen so don't count them
|
||||
if (preparation_data_a.account_it != confirmed_iterated_pairs.cend ())
|
||||
{
|
||||
preparation_data_a.account_it->second.confirmed_height = block_height;
|
||||
if (block_height > preparation_data_a.iterated_height)
|
||||
{
|
||||
preparation_data_a.account_it->second.iterated_height = block_height;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
confirmed_iterated_pairs.emplace (std::piecewise_construct, std::forward_as_tuple (preparation_data_a.account), std::forward_as_tuple (block_height, block_height));
|
||||
++confirmed_iterated_pairs_size;
|
||||
}
|
||||
|
||||
auto num_blocks_confirmed = block_height - preparation_data_a.confirmation_height;
|
||||
auto block_callback_data = preparation_data_a.block_callback_data;
|
||||
if (block_callback_data.empty ())
|
||||
{
|
||||
if (!receive_details)
|
||||
{
|
||||
block_callback_data = preparation_data_a.orig_block_callback_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_assert (receive_details);
|
||||
|
||||
if (preparation_data_a.already_traversed && receive_details->source_block_callback_data.empty ())
|
||||
{
|
||||
// We are confirming a block which has already been traversed and found no associated receive details for it.
|
||||
auto & above_receive_details_w = implicit_receive_cemented_mapping[preparation_data_a.current];
|
||||
debug_assert (!above_receive_details_w.expired ());
|
||||
auto above_receive_details = above_receive_details_w.lock ();
|
||||
|
||||
auto num_blocks_already_confirmed = above_receive_details->num_blocks_confirmed - (above_receive_details->height - preparation_data_a.confirmation_height);
|
||||
|
||||
auto end_it = above_receive_details->block_callback_data.begin () + above_receive_details->block_callback_data.size () - (num_blocks_already_confirmed);
|
||||
auto start_it = end_it - num_blocks_confirmed;
|
||||
|
||||
block_callback_data.assign (start_it, end_it);
|
||||
}
|
||||
else
|
||||
{
|
||||
block_callback_data = receive_details->source_block_callback_data;
|
||||
}
|
||||
|
||||
auto num_to_remove = block_callback_data.size () - num_blocks_confirmed;
|
||||
block_callback_data.erase (std::next (block_callback_data.rbegin (), num_to_remove).base (), block_callback_data.end ());
|
||||
receive_details->source_block_callback_data.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
pending_writes.emplace_back (preparation_data_a.account, preparation_data_a.current, block_height, num_blocks_confirmed, block_callback_data);
|
||||
++pending_writes_size;
|
||||
}
|
||||
|
||||
if (receive_details)
|
||||
{
|
||||
// Check whether the previous block has been seen. If so, the rest of sends below have already been seen so don't count them
|
||||
auto const & receive_account = receive_details->account;
|
||||
auto receive_account_it = confirmed_iterated_pairs.find (receive_account);
|
||||
if (receive_account_it != confirmed_iterated_pairs.cend ())
|
||||
{
|
||||
// Get current height
|
||||
auto current_height = receive_account_it->second.confirmed_height;
|
||||
receive_account_it->second.confirmed_height = receive_details->height;
|
||||
auto const orig_num_blocks_confirmed = receive_details->num_blocks_confirmed;
|
||||
receive_details->num_blocks_confirmed = receive_details->height - current_height;
|
||||
|
||||
// Get the difference and remove the callbacks
|
||||
auto block_callbacks_to_remove = orig_num_blocks_confirmed - receive_details->num_blocks_confirmed;
|
||||
receive_details->block_callback_data.erase (std::next (receive_details->block_callback_data.rbegin (), block_callbacks_to_remove).base (), receive_details->block_callback_data.end ());
|
||||
debug_assert (receive_details->block_callback_data.size () == receive_details->num_blocks_confirmed);
|
||||
}
|
||||
else
|
||||
{
|
||||
confirmed_iterated_pairs.emplace (std::piecewise_construct, std::forward_as_tuple (receive_account), std::forward_as_tuple (receive_details->height, receive_details->height));
|
||||
++confirmed_iterated_pairs_size;
|
||||
}
|
||||
|
||||
pending_writes.push_back (*receive_details);
|
||||
++pending_writes_size;
|
||||
}
|
||||
}
|
||||
|
||||
void nano::confirmation_height_unbounded::cement_blocks (nano::write_guard & scoped_write_guard_a)
|
||||
{
|
||||
nano::timer<std::chrono::milliseconds> cemented_batch_timer;
|
||||
std::vector<std::shared_ptr<nano::block>> cemented_blocks;
|
||||
auto error = false;
|
||||
{
|
||||
auto transaction (ledger.store.tx_begin_write ({}, { nano::tables::confirmation_height }));
|
||||
cemented_batch_timer.start ();
|
||||
while (!pending_writes.empty ())
|
||||
{
|
||||
auto & pending = pending_writes.front ();
|
||||
nano::confirmation_height_info confirmation_height_info;
|
||||
ledger.store.confirmation_height.get (transaction, pending.account, confirmation_height_info);
|
||||
auto confirmation_height = confirmation_height_info.height;
|
||||
if (pending.height > confirmation_height)
|
||||
{
|
||||
auto block = ledger.block (transaction, pending.hash);
|
||||
debug_assert (ledger.pruning || block != nullptr);
|
||||
debug_assert (ledger.pruning || block->sideband ().height == pending.height);
|
||||
|
||||
if (!block)
|
||||
{
|
||||
if (ledger.pruning && ledger.store.pruned.exists (transaction, pending.hash))
|
||||
{
|
||||
pending_writes.erase (pending_writes.begin ());
|
||||
--pending_writes_size;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.critical (nano::log::type::conf_processor_unbounded, "Failed to write confirmation height for block {} (unbounded processor)", pending.hash.to_string ());
|
||||
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ledger.stats.add (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in, pending.height - confirmation_height);
|
||||
ledger.stats.add (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_unbounded, nano::stat::dir::in, pending.height - confirmation_height);
|
||||
debug_assert (pending.num_blocks_confirmed == pending.height - confirmation_height);
|
||||
confirmation_height = pending.height;
|
||||
ledger.cache.cemented_count += pending.num_blocks_confirmed;
|
||||
ledger.store.confirmation_height.put (transaction, pending.account, { confirmation_height, pending.hash });
|
||||
|
||||
// Reverse it so that the callbacks start from the lowest newly cemented block and move upwards
|
||||
std::reverse (pending.block_callback_data.begin (), pending.block_callback_data.end ());
|
||||
|
||||
nano::lock_guard<nano::mutex> guard (block_cache_mutex);
|
||||
std::transform (pending.block_callback_data.begin (), pending.block_callback_data.end (), std::back_inserter (cemented_blocks), [&block_cache = block_cache] (auto const & hash_a) {
|
||||
debug_assert (block_cache.count (hash_a) == 1);
|
||||
return block_cache.at (hash_a);
|
||||
});
|
||||
}
|
||||
pending_writes.erase (pending_writes.begin ());
|
||||
--pending_writes_size;
|
||||
}
|
||||
}
|
||||
|
||||
auto time_spent_cementing = cemented_batch_timer.since_start ().count ();
|
||||
|
||||
scoped_write_guard_a.release ();
|
||||
notify_observers_callback (cemented_blocks);
|
||||
release_assert (!error);
|
||||
|
||||
debug_assert (pending_writes.empty ());
|
||||
debug_assert (pending_writes_size == 0);
|
||||
timer.restart ();
|
||||
}
|
||||
|
||||
std::shared_ptr<nano::block> nano::confirmation_height_unbounded::get_block_and_sideband (nano::block_hash const & hash_a, store::transaction const & transaction_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (block_cache_mutex);
|
||||
auto block_cache_it = block_cache.find (hash_a);
|
||||
if (block_cache_it != block_cache.cend ())
|
||||
{
|
||||
return block_cache_it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto block = ledger.block (transaction_a, hash_a);
|
||||
block_cache.emplace (hash_a, block);
|
||||
return block;
|
||||
}
|
||||
}
|
||||
|
||||
bool nano::confirmation_height_unbounded::pending_empty () const
|
||||
{
|
||||
return pending_writes.empty ();
|
||||
}
|
||||
|
||||
void nano::confirmation_height_unbounded::clear_process_vars ()
|
||||
{
|
||||
// Separate blocks which are pending confirmation height can be batched by a minimum processing time (to improve lmdb disk write performance),
|
||||
// so make sure the slate is clean when a new batch is starting.
|
||||
confirmed_iterated_pairs.clear ();
|
||||
confirmed_iterated_pairs_size = 0;
|
||||
implicit_receive_cemented_mapping.clear ();
|
||||
implicit_receive_cemented_mapping_size = 0;
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (block_cache_mutex);
|
||||
block_cache.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
bool nano::confirmation_height_unbounded::has_iterated_over_block (nano::block_hash const & hash_a) const
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (block_cache_mutex);
|
||||
return block_cache.count (hash_a) == 1;
|
||||
}
|
||||
|
||||
uint64_t nano::confirmation_height_unbounded::block_cache_size () const
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (block_cache_mutex);
|
||||
return block_cache.size ();
|
||||
}
|
||||
|
||||
nano::confirmation_height_unbounded::conf_height_details::conf_height_details (nano::account const & account_a, nano::block_hash const & hash_a, uint64_t height_a, uint64_t num_blocks_confirmed_a, std::vector<nano::block_hash> const & block_callback_data_a) :
|
||||
account (account_a),
|
||||
hash (hash_a),
|
||||
height (height_a),
|
||||
num_blocks_confirmed (num_blocks_confirmed_a),
|
||||
block_callback_data (block_callback_data_a)
|
||||
{
|
||||
}
|
||||
|
||||
nano::confirmation_height_unbounded::receive_source_pair::receive_source_pair (std::shared_ptr<conf_height_details> const & receive_details_a, const block_hash & source_a) :
|
||||
receive_details (receive_details_a),
|
||||
source_hash (source_a)
|
||||
{
|
||||
}
|
||||
|
||||
nano::confirmation_height_unbounded::confirmed_iterated_pair::confirmed_iterated_pair (uint64_t confirmed_height_a, uint64_t iterated_height_a) :
|
||||
confirmed_height (confirmed_height_a),
|
||||
iterated_height (iterated_height_a)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<nano::container_info_component> nano::collect_container_info (confirmation_height_unbounded & confirmation_height_unbounded, std::string const & name_a)
|
||||
{
|
||||
auto composite = std::make_unique<container_info_composite> (name_a);
|
||||
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "confirmed_iterated_pairs", confirmation_height_unbounded.confirmed_iterated_pairs_size, sizeof (decltype (confirmation_height_unbounded.confirmed_iterated_pairs)::value_type) }));
|
||||
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "pending_writes", confirmation_height_unbounded.pending_writes_size, sizeof (decltype (confirmation_height_unbounded.pending_writes)::value_type) }));
|
||||
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "implicit_receive_cemented_mapping", confirmation_height_unbounded.implicit_receive_cemented_mapping_size, sizeof (decltype (confirmation_height_unbounded.implicit_receive_cemented_mapping)::value_type) }));
|
||||
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "block_cache", confirmation_height_unbounded.block_cache_size (), sizeof (decltype (confirmation_height_unbounded.block_cache)::value_type) }));
|
||||
return composite;
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/logging.hpp>
|
||||
#include <nano/lib/numbers.hpp>
|
||||
#include <nano/lib/relaxed_atomic.hpp>
|
||||
#include <nano/lib/threading.hpp>
|
||||
#include <nano/lib/timer.hpp>
|
||||
#include <nano/store/component.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class ledger;
|
||||
class write_database_queue;
|
||||
class write_guard;
|
||||
|
||||
class confirmation_height_unbounded final
|
||||
{
|
||||
public:
|
||||
confirmation_height_unbounded (nano::ledger &, nano::write_database_queue &, std::chrono::milliseconds batch_separate_pending_min_time, nano::logger &, std::atomic<bool> & stopped, uint64_t & batch_write_size, std::function<void (std::vector<std::shared_ptr<nano::block>> const &)> const & cemented_callback, std::function<void (nano::block_hash const &)> const & already_cemented_callback, std::function<uint64_t ()> const & awaiting_processing_size_query);
|
||||
|
||||
bool pending_empty () const;
|
||||
void clear_process_vars ();
|
||||
void process (std::shared_ptr<nano::block> original_block);
|
||||
void cement_blocks (nano::write_guard &);
|
||||
bool has_iterated_over_block (nano::block_hash const &) const;
|
||||
|
||||
private:
|
||||
class confirmed_iterated_pair
|
||||
{
|
||||
public:
|
||||
confirmed_iterated_pair (uint64_t confirmed_height_a, uint64_t iterated_height_a);
|
||||
uint64_t confirmed_height;
|
||||
uint64_t iterated_height;
|
||||
};
|
||||
|
||||
class conf_height_details final
|
||||
{
|
||||
public:
|
||||
conf_height_details (nano::account const &, nano::block_hash const &, uint64_t, uint64_t, std::vector<nano::block_hash> const &);
|
||||
|
||||
nano::account account;
|
||||
nano::block_hash hash;
|
||||
uint64_t height;
|
||||
uint64_t num_blocks_confirmed;
|
||||
std::vector<nano::block_hash> block_callback_data;
|
||||
std::vector<nano::block_hash> source_block_callback_data;
|
||||
};
|
||||
|
||||
class receive_source_pair final
|
||||
{
|
||||
public:
|
||||
receive_source_pair (std::shared_ptr<conf_height_details> const &, nano::block_hash const &);
|
||||
|
||||
std::shared_ptr<conf_height_details> receive_details;
|
||||
nano::block_hash source_hash;
|
||||
};
|
||||
|
||||
// All of the atomic variables here just track the size for use in collect_container_info.
|
||||
// This is so that no mutexes are needed during the algorithm itself, which would otherwise be needed
|
||||
// for the sake of a rarely used RPC call for debugging purposes. As such the sizes are not being acted
|
||||
// upon in any way (does not synchronize with any other data).
|
||||
// This allows the load and stores to use relaxed atomic memory ordering.
|
||||
std::unordered_map<account, confirmed_iterated_pair> confirmed_iterated_pairs;
|
||||
nano::relaxed_atomic_integral<uint64_t> confirmed_iterated_pairs_size{ 0 };
|
||||
std::shared_ptr<nano::block> get_block_and_sideband (nano::block_hash const &, store::transaction const &);
|
||||
std::deque<conf_height_details> pending_writes;
|
||||
nano::relaxed_atomic_integral<uint64_t> pending_writes_size{ 0 };
|
||||
std::unordered_map<nano::block_hash, std::weak_ptr<conf_height_details>> implicit_receive_cemented_mapping;
|
||||
nano::relaxed_atomic_integral<uint64_t> implicit_receive_cemented_mapping_size{ 0 };
|
||||
|
||||
mutable nano::mutex block_cache_mutex;
|
||||
std::unordered_map<nano::block_hash, std::shared_ptr<nano::block>> block_cache;
|
||||
uint64_t block_cache_size () const;
|
||||
|
||||
nano::timer<std::chrono::milliseconds> timer;
|
||||
|
||||
class preparation_data final
|
||||
{
|
||||
public:
|
||||
uint64_t block_height;
|
||||
uint64_t confirmation_height;
|
||||
uint64_t iterated_height;
|
||||
decltype (confirmed_iterated_pairs.begin ()) account_it;
|
||||
nano::account const & account;
|
||||
std::shared_ptr<conf_height_details> receive_details;
|
||||
bool already_traversed;
|
||||
nano::block_hash const & current;
|
||||
std::vector<nano::block_hash> const & block_callback_data;
|
||||
std::vector<nano::block_hash> const & orig_block_callback_data;
|
||||
};
|
||||
|
||||
void collect_unconfirmed_receive_and_sources_for_account (uint64_t, uint64_t, std::shared_ptr<nano::block> const &, nano::block_hash const &, nano::account const &, store::read_transaction const &, std::vector<receive_source_pair> &, std::vector<nano::block_hash> &, std::vector<nano::block_hash> &, std::shared_ptr<nano::block> original_block);
|
||||
void prepare_iterated_blocks_for_cementing (preparation_data &);
|
||||
|
||||
nano::ledger & ledger;
|
||||
nano::write_database_queue & write_database_queue;
|
||||
std::chrono::milliseconds batch_separate_pending_min_time;
|
||||
nano::logger & logger;
|
||||
std::atomic<bool> & stopped;
|
||||
uint64_t & batch_write_size;
|
||||
|
||||
std::function<void (std::vector<std::shared_ptr<nano::block>> const &)> notify_observers_callback;
|
||||
std::function<void (nano::block_hash const &)> notify_block_already_cemented_observers_callback;
|
||||
std::function<uint64_t ()> awaiting_processing_size_callback;
|
||||
|
||||
friend class confirmation_height_dynamic_algorithm_no_transition_while_pending_Test;
|
||||
friend std::unique_ptr<nano::container_info_component> collect_container_info (confirmation_height_unbounded &, std::string const & name_a);
|
||||
};
|
||||
|
||||
std::unique_ptr<nano::container_info_component> collect_container_info (confirmation_height_unbounded &, std::string const & name_a);
|
||||
}
|
||||
120
nano/node/confirming_set.cpp
Normal file
120
nano/node/confirming_set.cpp
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
#include <nano/lib/thread_roles.hpp>
|
||||
#include <nano/node/confirming_set.hpp>
|
||||
#include <nano/node/write_database_queue.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/store/component.hpp>
|
||||
|
||||
nano::confirming_set::confirming_set (nano::ledger & ledger, nano::write_database_queue & write_queue, std::chrono::milliseconds batch_time) :
|
||||
ledger{ ledger },
|
||||
write_queue{ write_queue },
|
||||
batch_time{ batch_time }
|
||||
{
|
||||
}
|
||||
|
||||
nano::confirming_set::~confirming_set ()
|
||||
{
|
||||
debug_assert (!thread.joinable ());
|
||||
}
|
||||
|
||||
void nano::confirming_set::add (nano::block_hash const & hash)
|
||||
{
|
||||
std::lock_guard lock{ mutex };
|
||||
set.insert (hash);
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
void nano::confirming_set::start ()
|
||||
{
|
||||
thread = std::thread{ [this] () { run (); } };
|
||||
}
|
||||
|
||||
void nano::confirming_set::stop ()
|
||||
{
|
||||
{
|
||||
std::lock_guard lock{ mutex };
|
||||
stopped = true;
|
||||
condition.notify_all ();
|
||||
}
|
||||
if (thread.joinable ())
|
||||
{
|
||||
thread.join ();
|
||||
}
|
||||
}
|
||||
|
||||
bool nano::confirming_set::exists (nano::block_hash const & hash) const
|
||||
{
|
||||
std::lock_guard lock{ mutex };
|
||||
return set.count (hash) != 0 || processing.count (hash) != 0;
|
||||
}
|
||||
|
||||
std::size_t nano::confirming_set::size () const
|
||||
{
|
||||
std::lock_guard lock{ mutex };
|
||||
return set.size () + processing.size ();
|
||||
}
|
||||
|
||||
void nano::confirming_set::run ()
|
||||
{
|
||||
nano::thread_role::set (nano::thread_role::name::confirmation_height_processing);
|
||||
std::unique_lock lock{ mutex };
|
||||
// Run the confirmation loop until stopped
|
||||
while (!stopped)
|
||||
{
|
||||
condition.wait (lock, [&] () { return !set.empty () || stopped; });
|
||||
// Loop if there are items to process
|
||||
if (!stopped && !set.empty ())
|
||||
{
|
||||
std::deque<std::shared_ptr<nano::block>> cemented;
|
||||
std::deque<nano::block_hash> already;
|
||||
// Move items in to back buffer and release lock so more items can be added to the front buffer
|
||||
processing = std::move (this->set);
|
||||
// Process all items in the back buffer
|
||||
for (auto i = processing.begin (), n = processing.end (); !stopped && i != n;)
|
||||
{
|
||||
lock.unlock (); // Waiting for db write is potentially slow
|
||||
auto guard = write_queue.wait (nano::writer::confirmation_height);
|
||||
auto tx = ledger.store.tx_begin_write ({ nano::tables::confirmation_height });
|
||||
lock.lock ();
|
||||
// Process items in the back buffer within a single transaction for a limited amount of time
|
||||
for (auto timeout = std::chrono::steady_clock::now () + batch_time; !stopped && std::chrono::steady_clock::now () < timeout && i != n; ++i)
|
||||
{
|
||||
auto item = *i;
|
||||
lock.unlock ();
|
||||
auto added = ledger.confirm (tx, item);
|
||||
if (!added.empty ())
|
||||
{
|
||||
// Confirming this block may implicitly confirm more
|
||||
cemented.insert (cemented.end (), added.begin (), added.end ());
|
||||
}
|
||||
else
|
||||
{
|
||||
already.push_back (item);
|
||||
}
|
||||
lock.lock ();
|
||||
}
|
||||
}
|
||||
lock.unlock ();
|
||||
for (auto const & i : cemented)
|
||||
{
|
||||
cemented_observers.notify (i);
|
||||
}
|
||||
for (auto const & i : already)
|
||||
{
|
||||
block_already_cemented_observers.notify (i);
|
||||
}
|
||||
lock.lock ();
|
||||
// Clear and free back buffer by re-initializing
|
||||
processing = decltype (processing){};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<nano::container_info_component> nano::confirming_set::collect_container_info (std::string const & name) const
|
||||
{
|
||||
std::lock_guard guard{ mutex };
|
||||
|
||||
auto composite = std::make_unique<container_info_composite> (name);
|
||||
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "set", set.size (), sizeof (typename decltype (set)::value_type) }));
|
||||
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "processing", processing.size (), sizeof (typename decltype (processing)::value_type) }));
|
||||
return composite;
|
||||
}
|
||||
57
nano/node/confirming_set.hpp
Normal file
57
nano/node/confirming_set.hpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/numbers.hpp>
|
||||
#include <nano/lib/observer_set.hpp>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class block;
|
||||
class ledger;
|
||||
class write_database_queue;
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
/**
|
||||
* Set of blocks to be durably confirmed
|
||||
*/
|
||||
class confirming_set final
|
||||
{
|
||||
friend class confirmation_heightDeathTest_missing_block_Test;
|
||||
friend class confirmation_height_pruned_source_Test;
|
||||
|
||||
public:
|
||||
confirming_set (nano::ledger & ledger, nano::write_database_queue & write_queue, std::chrono::milliseconds batch_time = std::chrono::milliseconds{ 500 });
|
||||
~confirming_set ();
|
||||
// Adds a block to the set of blocks to be confirmed
|
||||
void add (nano::block_hash const & hash);
|
||||
void start ();
|
||||
void stop ();
|
||||
// Added blocks will remain in this set until after ledger has them marked as confirmed.
|
||||
bool exists (nano::block_hash const & hash) const;
|
||||
std::size_t size () const;
|
||||
std::unique_ptr<container_info_component> collect_container_info (std::string const & name) const;
|
||||
|
||||
// Observers will be called once ledger has blocks marked as confirmed
|
||||
nano::observer_set<std::shared_ptr<nano::block>> cemented_observers;
|
||||
nano::observer_set<nano::block_hash const &> block_already_cemented_observers;
|
||||
|
||||
private:
|
||||
void run ();
|
||||
nano::ledger & ledger;
|
||||
nano::write_database_queue & write_queue;
|
||||
std::chrono::milliseconds batch_time;
|
||||
std::unordered_set<nano::block_hash> set;
|
||||
std::unordered_set<nano::block_hash> processing;
|
||||
bool stopped{ false };
|
||||
mutable std::mutex mutex;
|
||||
std::condition_variable condition;
|
||||
std::thread thread;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/confirmation_solicitor.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/local_vote_history.hpp>
|
||||
#include <nano/node/network.hpp>
|
||||
#include <nano/node/node.hpp>
|
||||
#include <nano/node/vote_generator.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
|
@ -16,12 +19,6 @@ std::chrono::milliseconds nano::election::base_latency () const
|
|||
return node.network_params.network.is_dev_network () ? 25ms : 1000ms;
|
||||
}
|
||||
|
||||
nano::election_vote_result::election_vote_result (bool replay_a, bool processed_a)
|
||||
{
|
||||
replay = replay_a;
|
||||
processed = processed_a;
|
||||
}
|
||||
|
||||
/*
|
||||
* election
|
||||
*/
|
||||
|
|
@ -40,7 +37,7 @@ nano::election::election (nano::node & node_a, std::shared_ptr<nano::block> cons
|
|||
last_blocks.emplace (block_a->hash (), block_a);
|
||||
}
|
||||
|
||||
void nano::election::confirm_once (nano::unique_lock<nano::mutex> & lock_a, nano::election_status_type type_a)
|
||||
void nano::election::confirm_once (nano::unique_lock<nano::mutex> & lock_a)
|
||||
{
|
||||
debug_assert (lock_a.owns_lock ());
|
||||
|
||||
|
|
@ -57,7 +54,6 @@ void nano::election::confirm_once (nano::unique_lock<nano::mutex> & lock_a, nano
|
|||
status.confirmation_request_count = confirmation_request_count;
|
||||
status.block_count = nano::narrow_cast<decltype (status.block_count)> (last_blocks.size ());
|
||||
status.voter_count = nano::narrow_cast<decltype (status.voter_count)> (last_votes.size ());
|
||||
status.type = type_a;
|
||||
auto const status_l = status;
|
||||
|
||||
node.active.recently_confirmed.put (qualified_root, status_l.winner->hash ());
|
||||
|
|
@ -403,50 +399,28 @@ void nano::election::confirm_if_quorum (nano::unique_lock<nano::mutex> & lock_a)
|
|||
}
|
||||
if (have_quorum (tally_l))
|
||||
{
|
||||
if (node.ledger.cache.final_votes_confirmation_canary.load () && !is_quorum.exchange (true) && node.config.enable_voting && node.wallets.reps ().voting > 0)
|
||||
if (!is_quorum.exchange (true) && node.config.enable_voting && node.wallets.reps ().voting > 0)
|
||||
{
|
||||
node.final_generator.add (root, status.winner->hash ());
|
||||
}
|
||||
if (!node.ledger.cache.final_votes_confirmation_canary.load () || final_weight >= node.online_reps.delta ())
|
||||
if (final_weight >= node.online_reps.delta ())
|
||||
{
|
||||
confirm_once (lock_a, nano::election_status_type::active_confirmed_quorum);
|
||||
confirm_once (lock_a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<nano::election_status_type> nano::election::try_confirm (nano::block_hash const & hash)
|
||||
void nano::election::try_confirm (nano::block_hash const & hash)
|
||||
{
|
||||
boost::optional<nano::election_status_type> status_type;
|
||||
nano::unique_lock<nano::mutex> election_lock{ mutex };
|
||||
auto winner = status.winner;
|
||||
if (winner && winner->hash () == hash)
|
||||
{
|
||||
// Determine if the block was confirmed explicitly via election confirmation or implicitly via confirmation height
|
||||
if (!confirmed_locked ())
|
||||
{
|
||||
confirm_once (election_lock, nano::election_status_type::active_confirmation_height);
|
||||
status_type = nano::election_status_type::active_confirmation_height;
|
||||
}
|
||||
else
|
||||
{
|
||||
status_type = nano::election_status_type::active_confirmed_quorum;
|
||||
confirm_once (election_lock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
status_type = boost::optional<nano::election_status_type>{};
|
||||
}
|
||||
return status_type;
|
||||
}
|
||||
|
||||
nano::election_status nano::election::set_status_type (nano::election_status_type status_type)
|
||||
{
|
||||
nano::unique_lock<nano::mutex> election_lk{ mutex };
|
||||
status.type = status_type;
|
||||
status.confirmation_request_count = confirmation_request_count;
|
||||
nano::election_status status_l{ status };
|
||||
election_lk.unlock ();
|
||||
return status_l;
|
||||
}
|
||||
|
||||
std::shared_ptr<nano::block> nano::election::find (nano::block_hash const & hash_a) const
|
||||
|
|
@ -460,13 +434,14 @@ std::shared_ptr<nano::block> nano::election::find (nano::block_hash const & hash
|
|||
return result;
|
||||
}
|
||||
|
||||
nano::election_vote_result nano::election::vote (nano::account const & rep, uint64_t timestamp_a, nano::block_hash const & block_hash_a, vote_source vote_source_a)
|
||||
nano::vote_code nano::election::vote (nano::account const & rep, uint64_t timestamp_a, nano::block_hash const & block_hash_a, nano::vote_source vote_source_a)
|
||||
{
|
||||
auto weight = node.ledger.weight (rep);
|
||||
if (!node.network_params.network.is_dev_network () && weight <= node.minimum_principal_weight ())
|
||||
{
|
||||
return nano::election_vote_result (false, false);
|
||||
return vote_code::indeterminate;
|
||||
}
|
||||
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
|
||||
auto last_vote_it (last_votes.find (rep));
|
||||
|
|
@ -475,18 +450,17 @@ nano::election_vote_result nano::election::vote (nano::account const & rep, uint
|
|||
auto last_vote_l (last_vote_it->second);
|
||||
if (last_vote_l.timestamp > timestamp_a)
|
||||
{
|
||||
return nano::election_vote_result (true, false);
|
||||
return vote_code::replay;
|
||||
}
|
||||
if (last_vote_l.timestamp == timestamp_a && !(last_vote_l.hash < block_hash_a))
|
||||
{
|
||||
return nano::election_vote_result (true, false);
|
||||
return vote_code::replay;
|
||||
}
|
||||
|
||||
auto max_vote = timestamp_a == std::numeric_limits<uint64_t>::max () && last_vote_l.timestamp < timestamp_a;
|
||||
|
||||
bool past_cooldown = true;
|
||||
// Only cooldown live votes
|
||||
if (vote_source_a == vote_source::live)
|
||||
if (vote_source_a == vote_source::live) // Only cooldown live votes
|
||||
{
|
||||
const auto cooldown = cooldown_time (weight);
|
||||
past_cooldown = last_vote_l.time <= std::chrono::steady_clock::now () - cooldown;
|
||||
|
|
@ -494,7 +468,7 @@ nano::election_vote_result nano::election::vote (nano::account const & rep, uint
|
|||
|
||||
if (!max_vote && !past_cooldown)
|
||||
{
|
||||
return nano::election_vote_result (false, false);
|
||||
return vote_code::ignored;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -519,7 +493,8 @@ nano::election_vote_result nano::election::vote (nano::account const & rep, uint
|
|||
{
|
||||
confirm_if_quorum (lock);
|
||||
}
|
||||
return nano::election_vote_result (false, true);
|
||||
|
||||
return vote_code::vote;
|
||||
}
|
||||
|
||||
bool nano::election::publish (std::shared_ptr<nano::block> const & block_a)
|
||||
|
|
@ -669,11 +644,22 @@ bool nano::election::replace_by_weight (nano::unique_lock<nano::mutex> & lock_a,
|
|||
sorted.reserve (last_tally.size ());
|
||||
std::copy (last_tally.begin (), last_tally.end (), std::back_inserter (sorted));
|
||||
lock_a.unlock ();
|
||||
|
||||
// Sort in ascending order
|
||||
std::sort (sorted.begin (), sorted.end (), [] (auto const & left, auto const & right) { return left.second < right.second; });
|
||||
|
||||
auto votes_tally = [this] (std::vector<std::shared_ptr<nano::vote>> const & votes) {
|
||||
nano::uint128_t result{ 0 };
|
||||
for (auto const & vote : votes)
|
||||
{
|
||||
result += node.ledger.weight (vote->account);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// Replace if lowest tally is below inactive cache new block weight
|
||||
auto inactive_existing = node.vote_cache.find (hash_a);
|
||||
auto inactive_tally = inactive_existing ? inactive_existing->tally () : 0;
|
||||
auto inactive_tally = votes_tally (inactive_existing);
|
||||
if (inactive_tally > 0 && sorted.size () < max_blocks)
|
||||
{
|
||||
// If count of tally items is less than 10, remove any block without tally
|
||||
|
|
@ -714,11 +700,11 @@ bool nano::election::replace_by_weight (nano::unique_lock<nano::mutex> & lock_a,
|
|||
return replaced;
|
||||
}
|
||||
|
||||
void nano::election::force_confirm (nano::election_status_type type_a)
|
||||
void nano::election::force_confirm ()
|
||||
{
|
||||
release_assert (node.network_params.network.is_dev_network ());
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
confirm_once (lock, type_a);
|
||||
confirm_once (lock);
|
||||
}
|
||||
|
||||
std::unordered_map<nano::block_hash, std::shared_ptr<nano::block>> nano::election::blocks () const
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
#include <nano/lib/id_dispenser.hpp>
|
||||
#include <nano/lib/logging.hpp>
|
||||
#include <nano/lib/stats_enums.hpp>
|
||||
#include <nano/node/election_behavior.hpp>
|
||||
#include <nano/node/election_status.hpp>
|
||||
#include <nano/node/vote_with_weight_info.hpp>
|
||||
#include <nano/secure/common.hpp>
|
||||
#include <nano/store/component.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
|
@ -11,6 +14,7 @@
|
|||
|
||||
namespace nano
|
||||
{
|
||||
class block;
|
||||
class channel;
|
||||
class confirmation_solicitor;
|
||||
class inactive_cache_information;
|
||||
|
|
@ -24,43 +28,6 @@ public:
|
|||
nano::block_hash hash;
|
||||
};
|
||||
|
||||
class vote_with_weight_info final
|
||||
{
|
||||
public:
|
||||
nano::account representative;
|
||||
std::chrono::steady_clock::time_point time;
|
||||
uint64_t timestamp;
|
||||
nano::block_hash hash;
|
||||
nano::uint128_t weight;
|
||||
};
|
||||
|
||||
class election_vote_result final
|
||||
{
|
||||
public:
|
||||
election_vote_result () = default;
|
||||
election_vote_result (bool, bool);
|
||||
bool replay{ false };
|
||||
bool processed{ false };
|
||||
};
|
||||
|
||||
enum class election_behavior
|
||||
{
|
||||
normal,
|
||||
/**
|
||||
* Hinted elections:
|
||||
* - shorter timespan
|
||||
* - limited space inside AEC
|
||||
*/
|
||||
hinted,
|
||||
/**
|
||||
* Optimistic elections:
|
||||
* - shorter timespan
|
||||
* - limited space inside AEC
|
||||
* - more frequent confirmation requests
|
||||
*/
|
||||
optimistic,
|
||||
};
|
||||
|
||||
nano::stat::detail to_stat_detail (nano::election_behavior);
|
||||
|
||||
// map of vote weight per block, ordered greater first
|
||||
|
|
@ -76,17 +43,10 @@ struct election_extended_status final
|
|||
void operator() (nano::object_stream &) const;
|
||||
};
|
||||
|
||||
class election final : public std::enable_shared_from_this<nano::election>
|
||||
class election final : public std::enable_shared_from_this<election>
|
||||
{
|
||||
nano::id_t const id{ nano::next_id () }; // Track individual objects when tracing
|
||||
|
||||
public:
|
||||
enum class vote_source
|
||||
{
|
||||
live,
|
||||
cache,
|
||||
};
|
||||
|
||||
private:
|
||||
// Minimum time between broadcasts of the current winner of an election, as a backup to requesting confirmations
|
||||
std::chrono::milliseconds base_latency () const;
|
||||
|
|
@ -144,12 +104,11 @@ public: // Interface
|
|||
* Process vote. Internally uses cooldown to throttle non-final votes
|
||||
* If the election reaches consensus, it will be confirmed
|
||||
*/
|
||||
nano::election_vote_result vote (nano::account const & representative, uint64_t timestamp, nano::block_hash const & block_hash, vote_source = vote_source::live);
|
||||
nano::vote_code vote (nano::account const & representative, uint64_t timestamp, nano::block_hash const & block_hash, nano::vote_source = nano::vote_source::live);
|
||||
bool publish (std::shared_ptr<nano::block> const & block_a);
|
||||
// Confirm this block if quorum is met
|
||||
void confirm_if_quorum (nano::unique_lock<nano::mutex> &);
|
||||
boost::optional<nano::election_status_type> try_confirm (nano::block_hash const & hash);
|
||||
nano::election_status set_status_type (nano::election_status_type status_type);
|
||||
void try_confirm (nano::block_hash const & hash);
|
||||
|
||||
/**
|
||||
* Broadcasts vote for the current winner of this election
|
||||
|
|
@ -175,7 +134,7 @@ private:
|
|||
bool confirmed_locked () const;
|
||||
nano::election_extended_status current_status_locked () const;
|
||||
// lock_a does not own the mutex on return
|
||||
void confirm_once (nano::unique_lock<nano::mutex> & lock_a, nano::election_status_type = nano::election_status_type::active_confirmed_quorum);
|
||||
void confirm_once (nano::unique_lock<nano::mutex> & lock_a);
|
||||
bool broadcast_block_predicate () const;
|
||||
void broadcast_block (nano::confirmation_solicitor &);
|
||||
void send_confirm_req (nano::confirmation_solicitor &);
|
||||
|
|
@ -219,7 +178,7 @@ private: // Constants
|
|||
friend class confirmation_solicitor;
|
||||
|
||||
public: // Only used in tests
|
||||
void force_confirm (nano::election_status_type = nano::election_status_type::active_confirmed_quorum);
|
||||
void force_confirm ();
|
||||
std::unordered_map<nano::account, nano::vote_info> votes () const;
|
||||
std::unordered_map<nano::block_hash, std::shared_ptr<nano::block>> blocks () const;
|
||||
|
||||
|
|
|
|||
22
nano/node/election_behavior.hpp
Normal file
22
nano/node/election_behavior.hpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
namespace nano
|
||||
{
|
||||
enum class election_behavior
|
||||
{
|
||||
normal,
|
||||
/**
|
||||
* Hinted elections:
|
||||
* - shorter timespan
|
||||
* - limited space inside AEC
|
||||
*/
|
||||
hinted,
|
||||
/**
|
||||
* Optimistic elections:
|
||||
* - shorter timespan
|
||||
* - limited space inside AEC
|
||||
* - more frequent confirmation requests
|
||||
*/
|
||||
optimistic,
|
||||
};
|
||||
}
|
||||
39
nano/node/election_status.hpp
Normal file
39
nano/node/election_status.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/numbers.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class block;
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
/* Defines the possible states for an election to stop in */
|
||||
enum class election_status_type : uint8_t
|
||||
{
|
||||
ongoing = 0,
|
||||
active_confirmed_quorum = 1,
|
||||
active_confirmation_height = 2,
|
||||
inactive_confirmation_height = 3,
|
||||
stopped = 5
|
||||
};
|
||||
|
||||
/* Holds a summary of an election */
|
||||
class election_status final
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<nano::block> winner;
|
||||
nano::amount tally{ 0 };
|
||||
nano::amount final_tally{ 0 };
|
||||
std::chrono::milliseconds election_end{ std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now ().time_since_epoch ()) };
|
||||
std::chrono::milliseconds election_duration{ std::chrono::duration_values<std::chrono::milliseconds>::zero () };
|
||||
unsigned confirmation_request_count{ 0 };
|
||||
unsigned block_count{ 0 };
|
||||
unsigned voter_count{ 0 };
|
||||
election_status_type type{ nano::election_status_type::inactive_confirmation_height };
|
||||
};
|
||||
}
|
||||
|
|
@ -210,14 +210,12 @@ void nano::epoch_upgrader::upgrade_impl (nano::raw_key const & prv_a, nano::epoc
|
|||
std::atomic<uint64_t> upgraded_pending (0);
|
||||
uint64_t workers (0);
|
||||
uint64_t attempts (0);
|
||||
auto transaction (store.tx_begin_read ());
|
||||
for (auto i (store.pending.begin (transaction, nano::pending_key (1, 0))), n (store.pending.end ()); i != n && attempts < upgrade_batch_size && attempts < count_limit && !stopped;)
|
||||
auto transaction = store.tx_begin_read ();
|
||||
for (auto current = ledger.receivable_upper_bound (transaction, 0), end = ledger.receivable_end (); current != end && attempts < upgrade_batch_size && attempts < count_limit && !stopped;)
|
||||
{
|
||||
bool to_next_account (false);
|
||||
nano::pending_key const & key (i->first);
|
||||
auto const & [key, info] = *current;
|
||||
if (!store.account.exists (transaction, key.account))
|
||||
{
|
||||
nano::pending_info const & info (i->second);
|
||||
if (info.epoch < epoch_a)
|
||||
{
|
||||
++attempts;
|
||||
|
|
@ -258,12 +256,10 @@ void nano::epoch_upgrader::upgrade_impl (nano::raw_key const & prv_a, nano::epoc
|
|||
upgrader_process (upgraded_pending, epoch, difficulty, signer, root, account);
|
||||
}
|
||||
}
|
||||
// Move to next pending item
|
||||
current = ledger.receivable_upper_bound (transaction, key.account, key.hash);
|
||||
}
|
||||
else
|
||||
{
|
||||
to_next_account = true;
|
||||
}
|
||||
if (to_next_account)
|
||||
{
|
||||
// Move to next account if pending account exists or was upgraded
|
||||
if (key.account.number () == std::numeric_limits<nano::uint256_t>::max ())
|
||||
|
|
@ -272,14 +268,9 @@ void nano::epoch_upgrader::upgrade_impl (nano::raw_key const & prv_a, nano::epoc
|
|||
}
|
||||
else
|
||||
{
|
||||
i = store.pending.begin (transaction, nano::pending_key (key.account.number () + 1, 0));
|
||||
current = ledger.receivable_upper_bound (transaction, key.account);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move to next pending item
|
||||
++i;
|
||||
}
|
||||
}
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ upgrader_mutex };
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/inactive_cache_information.hpp>
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
std::string nano::inactive_cache_information::to_string () const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "hash=" << hash.to_string ();
|
||||
ss << ", arrival=" << std::chrono::duration_cast<std::chrono::seconds> (arrival.time_since_epoch ()).count ();
|
||||
ss << ", " << status.to_string ();
|
||||
ss << ", " << voters.size () << " voters";
|
||||
for (auto const & [rep, timestamp] : voters)
|
||||
{
|
||||
ss << " " << rep.to_account () << "/" << timestamp;
|
||||
}
|
||||
return ss.str ();
|
||||
}
|
||||
|
||||
std::size_t nano::inactive_cache_information::fill (std::shared_ptr<nano::election> election) const
|
||||
{
|
||||
std::size_t inserted = 0;
|
||||
for (auto const & [rep, timestamp] : voters)
|
||||
{
|
||||
auto [is_replay, processed] = election->vote (rep, timestamp, hash, nano::election::vote_source::cache);
|
||||
if (processed)
|
||||
{
|
||||
inserted++;
|
||||
}
|
||||
}
|
||||
return inserted;
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/numbers.hpp>
|
||||
#include <nano/node/inactive_cache_status.hpp>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class inactive_cache_information final
|
||||
{
|
||||
public:
|
||||
inactive_cache_information () = default;
|
||||
inactive_cache_information (std::chrono::steady_clock::time_point arrival, nano::block_hash hash, nano::account initial_rep_a, uint64_t initial_timestamp_a, nano::inactive_cache_status status) :
|
||||
arrival (arrival),
|
||||
hash (hash),
|
||||
status (status)
|
||||
{
|
||||
voters.reserve (8);
|
||||
voters.emplace_back (initial_rep_a, initial_timestamp_a);
|
||||
}
|
||||
|
||||
std::chrono::steady_clock::time_point arrival;
|
||||
nano::block_hash hash;
|
||||
nano::inactive_cache_status status;
|
||||
std::vector<std::pair<nano::account, uint64_t>> voters;
|
||||
|
||||
bool needs_eval () const
|
||||
{
|
||||
return !status.bootstrap_started || !status.election_started || !status.confirmed;
|
||||
}
|
||||
|
||||
std::string to_string () const;
|
||||
|
||||
/**
|
||||
* Inserts votes stored in this entry into an election
|
||||
* @return number of votes inserted
|
||||
*/
|
||||
std::size_t fill (std::shared_ptr<nano::election> election) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
#include <nano/node/inactive_cache_status.hpp>
|
||||
|
||||
bool nano::inactive_cache_status::operator!= (inactive_cache_status const other) const
|
||||
{
|
||||
return bootstrap_started != other.bootstrap_started
|
||||
|| election_started != other.election_started
|
||||
|| confirmed != other.confirmed
|
||||
|| tally != other.tally;
|
||||
}
|
||||
|
||||
std::string nano::inactive_cache_status::to_string () const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "bootstrap_started=" << bootstrap_started;
|
||||
ss << ", election_started=" << election_started;
|
||||
ss << ", confirmed=" << confirmed;
|
||||
ss << ", tally=" << nano::uint128_union (tally).to_string ();
|
||||
return ss.str ();
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/numbers.hpp>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class inactive_cache_status final
|
||||
{
|
||||
public:
|
||||
bool bootstrap_started{ false };
|
||||
|
||||
/** Did item reach config threshold to start an impromptu election? */
|
||||
bool election_started{ false };
|
||||
|
||||
/** Did item reach votes quorum? (minimum config value) */
|
||||
bool confirmed{ false };
|
||||
|
||||
/** Last votes tally for block */
|
||||
nano::uint128_t tally{ 0 };
|
||||
|
||||
bool operator!= (inactive_cache_status const other) const;
|
||||
|
||||
std::string to_string () const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -463,12 +463,13 @@ class socket_transport : public nano::ipc::transport
|
|||
{
|
||||
public:
|
||||
socket_transport (nano::ipc::ipc_server & server_a, ENDPOINT_TYPE endpoint_a, nano::ipc::ipc_config_transport & config_transport_a, int concurrency_a) :
|
||||
server (server_a), config_transport (config_transport_a)
|
||||
server (server_a),
|
||||
config_transport (config_transport_a)
|
||||
{
|
||||
// Using a per-transport event dispatcher?
|
||||
if (concurrency_a > 0)
|
||||
{
|
||||
io_ctx = std::make_unique<boost::asio::io_context> ();
|
||||
io_ctx = std::make_shared<boost::asio::io_context> ();
|
||||
}
|
||||
|
||||
boost::asio::socket_base::reuse_address option (true);
|
||||
|
|
@ -482,7 +483,7 @@ public:
|
|||
// A separate io_context for domain sockets may facilitate better performance on some systems.
|
||||
if (concurrency_a > 0)
|
||||
{
|
||||
runner = std::make_unique<nano::thread_runner> (*io_ctx, static_cast<unsigned> (concurrency_a));
|
||||
runner = std::make_unique<nano::thread_runner> (io_ctx, static_cast<unsigned> (concurrency_a));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -510,7 +511,7 @@ public:
|
|||
}
|
||||
else
|
||||
{
|
||||
node->logger.error (nano::log::type::ipc, "Acceptor error: ", ec.message ());
|
||||
node->logger.error (nano::log::type::ipc, "Acceptor error: {}", ec.message ());
|
||||
}
|
||||
|
||||
if (ec != boost::asio::error::operation_aborted && acceptor->is_open ())
|
||||
|
|
@ -544,7 +545,7 @@ private:
|
|||
nano::ipc::ipc_server & server;
|
||||
nano::ipc::ipc_config_transport & config_transport;
|
||||
std::unique_ptr<nano::thread_runner> runner;
|
||||
std::unique_ptr<boost::asio::io_context> io_ctx;
|
||||
std::shared_ptr<boost::asio::io_context> io_ctx;
|
||||
std::unique_ptr<ACCEPTOR_TYPE> acceptor;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
#include <nano/lib/config.hpp>
|
||||
#include <nano/lib/json_error_response.hpp>
|
||||
#include <nano/lib/timer.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_lazy.hpp>
|
||||
#include <nano/node/bootstrap_ascending/service.hpp>
|
||||
#include <nano/node/common.hpp>
|
||||
#include <nano/node/confirming_set.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/json_handler.hpp>
|
||||
#include <nano/node/node.hpp>
|
||||
|
|
@ -417,7 +419,7 @@ uint64_t nano::json_handler::difficulty_ledger (nano::block const & block_a)
|
|||
{
|
||||
auto block_link = node.ledger.block (transaction, link.value ().as_block_hash ());
|
||||
auto account = block_a.account_field ().value (); // Link is non-zero therefore it's a state block and has an account field;
|
||||
if (block_link != nullptr && node.store.pending.exists (transaction, nano::pending_key (account, link.value ().as_block_hash ())))
|
||||
if (block_link != nullptr && node.ledger.pending_info (transaction, nano::pending_key{ account, link.value ().as_block_hash () }))
|
||||
{
|
||||
details.epoch = std::max (details.epoch, block_link->sideband ().details.epoch);
|
||||
details.is_receive = true;
|
||||
|
|
@ -698,7 +700,7 @@ void nano::json_handler::account_info ()
|
|||
}
|
||||
if (weight)
|
||||
{
|
||||
auto account_weight (node.ledger.weight (account));
|
||||
auto account_weight (node.ledger.weight_exact (transaction, account));
|
||||
response_l.put ("weight", account_weight.convert_to<std::string> ());
|
||||
}
|
||||
if (receivable)
|
||||
|
|
@ -1041,42 +1043,41 @@ void nano::json_handler::accounts_receivable ()
|
|||
bool const sorting = request.get<bool> ("sorting", false);
|
||||
auto simple (threshold.is_zero () && !source && !sorting); // if simple, response is a list of hashes for each account
|
||||
boost::property_tree::ptree pending;
|
||||
auto transaction (node.store.tx_begin_read ());
|
||||
auto transaction = node.store.tx_begin_read ();
|
||||
for (auto & accounts : request.get_child ("accounts"))
|
||||
{
|
||||
auto account (account_impl (accounts.second.data ()));
|
||||
if (!ec)
|
||||
{
|
||||
boost::property_tree::ptree peers_l;
|
||||
for (auto i (node.store.pending.begin (transaction, nano::pending_key (account, 0))), n (node.store.pending.end ()); i != n && nano::pending_key (i->first).account == account && peers_l.size () < count; ++i)
|
||||
for (auto current = node.ledger.receivable_upper_bound (transaction, account, 0), end = node.ledger.receivable_end (); current != end && peers_l.size () < count; ++current)
|
||||
{
|
||||
nano::pending_key const & key (i->first);
|
||||
if (block_confirmed (node, transaction, key.hash, include_active, include_only_confirmed))
|
||||
auto const & [key, info] = *current;
|
||||
if (include_only_confirmed && !node.ledger.block_confirmed (transaction, key.hash))
|
||||
{
|
||||
if (simple)
|
||||
{
|
||||
boost::property_tree::ptree entry;
|
||||
entry.put ("", key.hash.to_string ());
|
||||
peers_l.push_back (std::make_pair ("", entry));
|
||||
}
|
||||
else
|
||||
{
|
||||
nano::pending_info const & info (i->second);
|
||||
if (info.amount.number () >= threshold.number ())
|
||||
{
|
||||
if (source)
|
||||
{
|
||||
boost::property_tree::ptree pending_tree;
|
||||
pending_tree.put ("amount", info.amount.number ().convert_to<std::string> ());
|
||||
pending_tree.put ("source", info.source.to_account ());
|
||||
peers_l.add_child (key.hash.to_string (), pending_tree);
|
||||
}
|
||||
else
|
||||
{
|
||||
peers_l.put (key.hash.to_string (), info.amount.number ().convert_to<std::string> ());
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (simple)
|
||||
{
|
||||
boost::property_tree::ptree entry;
|
||||
entry.put ("", key.hash.to_string ());
|
||||
peers_l.push_back (std::make_pair ("", entry));
|
||||
continue;
|
||||
}
|
||||
if (info.amount.number () < threshold.number ())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (source)
|
||||
{
|
||||
boost::property_tree::ptree pending_tree;
|
||||
pending_tree.put ("amount", info.amount.number ().template convert_to<std::string> ());
|
||||
pending_tree.put ("source", info.source.to_account ());
|
||||
peers_l.add_child (key.hash.to_string (), pending_tree);
|
||||
}
|
||||
else
|
||||
{
|
||||
peers_l.put (key.hash.to_string (), info.amount.number ().template convert_to<std::string> ());
|
||||
}
|
||||
}
|
||||
if (sorting && !simple)
|
||||
|
|
@ -1205,7 +1206,7 @@ void nano::json_handler::block_confirm ()
|
|||
if (!node.ledger.block_confirmed (transaction, hash))
|
||||
{
|
||||
// Start new confirmation for unconfirmed (or not being confirmed) block
|
||||
if (!node.confirmation_height_processor.is_processing_block (hash))
|
||||
if (!node.confirming_set.exists (hash))
|
||||
{
|
||||
node.start_election (std::move (block_l));
|
||||
}
|
||||
|
|
@ -1342,8 +1343,7 @@ void nano::json_handler::blocks_info ()
|
|||
}
|
||||
if (receivable || receive_hash)
|
||||
{
|
||||
auto destination (node.ledger.block_destination (transaction, *block));
|
||||
if (destination.is_zero ())
|
||||
if (!block->is_send ())
|
||||
{
|
||||
if (receivable)
|
||||
{
|
||||
|
|
@ -1355,7 +1355,7 @@ void nano::json_handler::blocks_info ()
|
|||
entry.put ("receive_hash", nano::block_hash (0).to_string ());
|
||||
}
|
||||
}
|
||||
else if (node.store.pending.exists (transaction, nano::pending_key (destination, hash)))
|
||||
else if (node.ledger.pending_info (transaction, nano::pending_key{ block->destination (), hash }))
|
||||
{
|
||||
if (receivable)
|
||||
{
|
||||
|
|
@ -1376,7 +1376,7 @@ void nano::json_handler::blocks_info ()
|
|||
}
|
||||
if (receive_hash)
|
||||
{
|
||||
std::shared_ptr<nano::block> receive_block = node.ledger.find_receive_block_by_send_hash (transaction, destination, hash);
|
||||
std::shared_ptr<nano::block> receive_block = node.ledger.find_receive_block_by_send_hash (transaction, block->destination (), hash);
|
||||
std::string receive_hash = receive_block ? receive_block->hash ().to_string () : nano::block_hash (0).to_string ();
|
||||
entry.put ("receive_hash", receive_hash);
|
||||
}
|
||||
|
|
@ -1446,13 +1446,13 @@ void nano::json_handler::block_account ()
|
|||
|
||||
void nano::json_handler::block_count ()
|
||||
{
|
||||
response_l.put ("count", std::to_string (node.ledger.cache.block_count));
|
||||
response_l.put ("count", std::to_string (node.ledger.block_count ()));
|
||||
response_l.put ("unchecked", std::to_string (node.unchecked.count ()));
|
||||
response_l.put ("cemented", std::to_string (node.ledger.cache.cemented_count));
|
||||
response_l.put ("cemented", std::to_string (node.ledger.cemented_count ()));
|
||||
if (node.flags.enable_pruning)
|
||||
{
|
||||
response_l.put ("full", std::to_string (node.ledger.cache.block_count - node.ledger.cache.pruned_count));
|
||||
response_l.put ("pruned", std::to_string (node.ledger.cache.pruned_count));
|
||||
response_l.put ("full", std::to_string (node.ledger.block_count () - node.ledger.pruned_count ()));
|
||||
response_l.put ("pruned", std::to_string (node.ledger.pruned_count ()));
|
||||
}
|
||||
response_errors ();
|
||||
}
|
||||
|
|
@ -1957,7 +1957,7 @@ void nano::json_handler::chain (bool successors)
|
|||
entry.put ("", hash.to_string ());
|
||||
blocks.push_back (std::make_pair ("", entry));
|
||||
}
|
||||
hash = successors ? node.store.block.successor (transaction, hash) : block_l->previous ();
|
||||
hash = successors ? node.ledger.successor (transaction, hash).value_or (0) : block_l->previous ();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -2002,17 +2002,41 @@ void nano::json_handler::confirmation_active ()
|
|||
response_errors ();
|
||||
}
|
||||
|
||||
void nano::json_handler::confirmation_height_currently_processing ()
|
||||
void nano::json_handler::election_statistics ()
|
||||
{
|
||||
auto hash = node.confirmation_height_processor.current ();
|
||||
if (!hash.is_zero ())
|
||||
auto active_elections = node.active.list_active ();
|
||||
unsigned normal_count = 0;
|
||||
unsigned hinted_count = 0;
|
||||
unsigned optimistic_count = 0;
|
||||
unsigned total_count = 0;
|
||||
|
||||
for (auto const & election : active_elections)
|
||||
{
|
||||
response_l.put ("hash", hash.to_string ());
|
||||
}
|
||||
else
|
||||
{
|
||||
ec = nano::error_rpc::confirmation_height_not_processing;
|
||||
total_count++;
|
||||
switch (election->behavior ())
|
||||
{
|
||||
case election_behavior::normal:
|
||||
normal_count++;
|
||||
break;
|
||||
case election_behavior::hinted:
|
||||
hinted_count++;
|
||||
break;
|
||||
case election_behavior::optimistic:
|
||||
optimistic_count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto utilization_percentage = (static_cast<double> (total_count * 100) / node.config.active_elections_size);
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision (2) << utilization_percentage;
|
||||
|
||||
response_l.put ("normal", normal_count);
|
||||
response_l.put ("hinted", hinted_count);
|
||||
response_l.put ("optimistic", optimistic_count);
|
||||
response_l.put ("total", total_count);
|
||||
response_l.put ("aec_utilization_percentage", stream.str ());
|
||||
|
||||
response_errors ();
|
||||
}
|
||||
|
||||
|
|
@ -2365,7 +2389,7 @@ void nano::json_handler::frontiers ()
|
|||
|
||||
void nano::json_handler::account_count ()
|
||||
{
|
||||
auto size (node.ledger.cache.account_count.load ());
|
||||
auto size (node.ledger.account_count ());
|
||||
response_l.put ("count", std::to_string (size));
|
||||
response_errors ();
|
||||
}
|
||||
|
|
@ -2666,7 +2690,7 @@ void nano::json_handler::account_history ()
|
|||
--count;
|
||||
}
|
||||
}
|
||||
hash = reverse ? node.store.block.successor (transaction, hash) : block->previous ();
|
||||
hash = reverse ? node.ledger.successor (transaction, hash).value_or (0) : block->previous ();
|
||||
block = node.ledger.block (transaction, hash);
|
||||
}
|
||||
response_l.add_child ("history", history);
|
||||
|
|
@ -2786,7 +2810,7 @@ void nano::json_handler::ledger ()
|
|||
}
|
||||
if (weight)
|
||||
{
|
||||
auto account_weight (node.ledger.weight (account));
|
||||
auto account_weight (node.ledger.weight_exact (transaction, account));
|
||||
response_a.put ("weight", account_weight.convert_to<std::string> ());
|
||||
}
|
||||
accounts.push_back (std::make_pair (account.to_account (), response_a));
|
||||
|
|
@ -2839,7 +2863,7 @@ void nano::json_handler::ledger ()
|
|||
}
|
||||
if (weight)
|
||||
{
|
||||
auto account_weight (node.ledger.weight (account));
|
||||
auto account_weight (node.ledger.weight_exact (transaction, account));
|
||||
response_a.put ("weight", account_weight.convert_to<std::string> ());
|
||||
}
|
||||
accounts.push_back (std::make_pair (account.to_account (), response_a));
|
||||
|
|
@ -3053,66 +3077,65 @@ void nano::json_handler::receivable ()
|
|||
{
|
||||
auto offset_counter = offset;
|
||||
boost::property_tree::ptree peers_l;
|
||||
auto transaction (node.store.tx_begin_read ());
|
||||
auto transaction = node.store.tx_begin_read ();
|
||||
// The ptree container is used if there are any children nodes (e.g source/min_version) otherwise the amount container is used.
|
||||
std::vector<std::pair<std::string, boost::property_tree::ptree>> hash_ptree_pairs;
|
||||
std::vector<std::pair<std::string, nano::uint128_t>> hash_amount_pairs;
|
||||
for (auto i (node.store.pending.begin (transaction, nano::pending_key (account, 0))), n (node.store.pending.end ()); i != n && nano::pending_key (i->first).account == account && (should_sort || peers_l.size () < count); ++i)
|
||||
for (auto current = node.ledger.receivable_upper_bound (transaction, account, 0), end = node.ledger.receivable_end (); current != end && (should_sort || peers_l.size () < count); ++current)
|
||||
{
|
||||
nano::pending_key const & key (i->first);
|
||||
if (block_confirmed (node, transaction, key.hash, include_active, include_only_confirmed))
|
||||
auto const & [key, info] = *current;
|
||||
if (include_only_confirmed && !node.ledger.block_confirmed (transaction, key.hash))
|
||||
{
|
||||
if (!should_sort && offset_counter > 0)
|
||||
continue;
|
||||
}
|
||||
if (!should_sort && offset_counter > 0)
|
||||
{
|
||||
--offset_counter;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (simple)
|
||||
{
|
||||
boost::property_tree::ptree entry;
|
||||
entry.put ("", key.hash.to_string ());
|
||||
peers_l.push_back (std::make_pair ("", entry));
|
||||
continue;
|
||||
}
|
||||
if (info.amount.number () < threshold.number ())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (source || min_version)
|
||||
{
|
||||
boost::property_tree::ptree pending_tree;
|
||||
pending_tree.put ("amount", info.amount.number ().template convert_to<std::string> ());
|
||||
if (source)
|
||||
{
|
||||
--offset_counter;
|
||||
continue;
|
||||
pending_tree.put ("source", info.source.to_account ());
|
||||
}
|
||||
if (min_version)
|
||||
{
|
||||
pending_tree.put ("min_version", epoch_as_string (info.epoch));
|
||||
}
|
||||
|
||||
if (simple)
|
||||
if (should_sort)
|
||||
{
|
||||
boost::property_tree::ptree entry;
|
||||
entry.put ("", key.hash.to_string ());
|
||||
peers_l.push_back (std::make_pair ("", entry));
|
||||
hash_ptree_pairs.emplace_back (key.hash.to_string (), pending_tree);
|
||||
}
|
||||
else
|
||||
{
|
||||
nano::pending_info const & info (i->second);
|
||||
if (info.amount.number () >= threshold.number ())
|
||||
{
|
||||
if (source || min_version)
|
||||
{
|
||||
boost::property_tree::ptree pending_tree;
|
||||
pending_tree.put ("amount", info.amount.number ().convert_to<std::string> ());
|
||||
if (source)
|
||||
{
|
||||
pending_tree.put ("source", info.source.to_account ());
|
||||
}
|
||||
if (min_version)
|
||||
{
|
||||
pending_tree.put ("min_version", epoch_as_string (info.epoch));
|
||||
}
|
||||
|
||||
if (should_sort)
|
||||
{
|
||||
hash_ptree_pairs.emplace_back (key.hash.to_string (), pending_tree);
|
||||
}
|
||||
else
|
||||
{
|
||||
peers_l.add_child (key.hash.to_string (), pending_tree);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (should_sort)
|
||||
{
|
||||
hash_amount_pairs.emplace_back (key.hash.to_string (), info.amount.number ());
|
||||
}
|
||||
else
|
||||
{
|
||||
peers_l.put (key.hash.to_string (), info.amount.number ().convert_to<std::string> ());
|
||||
}
|
||||
}
|
||||
}
|
||||
peers_l.add_child (key.hash.to_string (), pending_tree);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (should_sort)
|
||||
{
|
||||
hash_amount_pairs.emplace_back (key.hash.to_string (), info.amount.number ());
|
||||
}
|
||||
else
|
||||
{
|
||||
peers_l.put (key.hash.to_string (), info.amount.number ().template convert_to<std::string> ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3163,10 +3186,9 @@ void nano::json_handler::receivable_exists ()
|
|||
if (block != nullptr)
|
||||
{
|
||||
auto exists (false);
|
||||
auto destination (node.ledger.block_destination (transaction, *block));
|
||||
if (!destination.is_zero ())
|
||||
if (block->is_send ())
|
||||
{
|
||||
exists = node.store.pending.exists (transaction, nano::pending_key (destination, hash));
|
||||
exists = node.ledger.pending_info (transaction, nano::pending_key{ block->destination (), hash }).has_value ();
|
||||
}
|
||||
exists = exists && (block_confirmed (node, transaction, block->hash (), include_active, include_only_confirmed));
|
||||
response_l.put ("exists", exists ? "1" : "0");
|
||||
|
|
@ -3668,10 +3690,10 @@ void nano::json_handler::republish ()
|
|||
if (destinations != 0) // Republish destination chain
|
||||
{
|
||||
auto block_b = node.ledger.block (transaction, hash);
|
||||
auto destination (node.ledger.block_destination (transaction, *block_b));
|
||||
auto destination = block_b->destination ();
|
||||
if (!destination.is_zero ())
|
||||
{
|
||||
if (!node.store.pending.exists (transaction, nano::pending_key (destination, hash)))
|
||||
if (!node.ledger.pending_info (transaction, nano::pending_key{ destination, hash }))
|
||||
{
|
||||
nano::block_hash previous (node.ledger.latest (transaction, destination));
|
||||
auto block_d = node.ledger.block (transaction, previous);
|
||||
|
|
@ -3700,7 +3722,7 @@ void nano::json_handler::republish ()
|
|||
}
|
||||
}
|
||||
}
|
||||
hash = node.store.block.successor (transaction, hash);
|
||||
hash = node.ledger.successor (transaction, hash).value_or (0);
|
||||
}
|
||||
node.network.flood_block_many (std::move (republish_bundle), nullptr, 25);
|
||||
response_l.put ("success", ""); // obsolete
|
||||
|
|
@ -4242,7 +4264,7 @@ void nano::json_handler::unopened ()
|
|||
{
|
||||
auto count (count_optional_impl ());
|
||||
auto threshold (threshold_optional_impl ());
|
||||
nano::account start (1); // exclude burn account by default
|
||||
nano::account start{ 1 }; // exclude burn account by default
|
||||
boost::optional<std::string> account_text (request.get_optional<std::string> ("account"));
|
||||
if (account_text.is_initialized ())
|
||||
{
|
||||
|
|
@ -4250,48 +4272,28 @@ void nano::json_handler::unopened ()
|
|||
}
|
||||
if (!ec)
|
||||
{
|
||||
auto transaction (node.store.tx_begin_read ());
|
||||
auto iterator (node.store.pending.begin (transaction, nano::pending_key (start, 0)));
|
||||
auto end (node.store.pending.end ());
|
||||
nano::account current_account (start);
|
||||
nano::uint128_t current_account_sum{ 0 };
|
||||
auto transaction = node.store.tx_begin_read ();
|
||||
auto & ledger = node.ledger;
|
||||
boost::property_tree::ptree accounts;
|
||||
while (iterator != end && accounts.size () < count)
|
||||
for (auto iterator = ledger.receivable_upper_bound (transaction, start, 0), end = ledger.receivable_end (); iterator != end && accounts.size () < count;)
|
||||
{
|
||||
nano::pending_key key (iterator->first);
|
||||
nano::account account (key.account);
|
||||
nano::pending_info info (iterator->second);
|
||||
if (node.store.account.exists (transaction, account))
|
||||
auto const & [key, info] = *iterator;
|
||||
nano::account account = key.account;
|
||||
if (!node.store.account.exists (transaction, account))
|
||||
{
|
||||
if (account.number () == std::numeric_limits<nano::uint256_t>::max ())
|
||||
nano::uint128_t current_account_sum{ 0 };
|
||||
while (iterator != end)
|
||||
{
|
||||
break;
|
||||
auto const & [key, info] = *iterator;
|
||||
current_account_sum += info.amount.number ();
|
||||
++iterator;
|
||||
}
|
||||
// Skip existing accounts
|
||||
iterator = node.store.pending.begin (transaction, nano::pending_key (account.number () + 1, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (account != current_account)
|
||||
if (current_account_sum >= threshold.number ())
|
||||
{
|
||||
if (current_account_sum > 0)
|
||||
{
|
||||
if (current_account_sum >= threshold.number ())
|
||||
{
|
||||
accounts.put (current_account.to_account (), current_account_sum.convert_to<std::string> ());
|
||||
}
|
||||
current_account_sum = 0;
|
||||
}
|
||||
current_account = account;
|
||||
accounts.put (account.to_account (), current_account_sum.convert_to<std::string> ());
|
||||
}
|
||||
current_account_sum += info.amount.number ();
|
||||
++iterator;
|
||||
}
|
||||
}
|
||||
// last one after iterator reaches end
|
||||
if (accounts.size () < count && current_account_sum > 0 && current_account_sum >= threshold.number ())
|
||||
{
|
||||
accounts.put (current_account.to_account (), current_account_sum.convert_to<std::string> ());
|
||||
iterator = ledger.receivable_upper_bound (transaction, account);
|
||||
}
|
||||
response_l.add_child ("accounts", accounts);
|
||||
}
|
||||
|
|
@ -4740,7 +4742,7 @@ void nano::json_handler::wallet_ledger ()
|
|||
}
|
||||
if (weight)
|
||||
{
|
||||
auto account_weight (node.ledger.weight (account));
|
||||
auto account_weight (node.ledger.weight_exact (block_transaction, account));
|
||||
entry.put ("weight", account_weight.convert_to<std::string> ());
|
||||
}
|
||||
if (receivable)
|
||||
|
|
@ -4792,47 +4794,46 @@ void nano::json_handler::wallet_receivable ()
|
|||
{
|
||||
boost::property_tree::ptree pending;
|
||||
auto transaction (node.wallets.tx_begin_read ());
|
||||
auto block_transaction (node.store.tx_begin_read ());
|
||||
auto block_transaction = node.store.tx_begin_read ();
|
||||
for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i)
|
||||
{
|
||||
nano::account const & account (i->first);
|
||||
boost::property_tree::ptree peers_l;
|
||||
for (auto ii (node.store.pending.begin (block_transaction, nano::pending_key (account, 0))), nn (node.store.pending.end ()); ii != nn && nano::pending_key (ii->first).account == account && peers_l.size () < count; ++ii)
|
||||
for (auto current = node.ledger.receivable_upper_bound (block_transaction, account, 0), end = node.ledger.receivable_end (); current != end && (peers_l.size () < count); ++current)
|
||||
{
|
||||
nano::pending_key key (ii->first);
|
||||
if (block_confirmed (node, block_transaction, key.hash, include_active, include_only_confirmed))
|
||||
auto const & [key, info] = *current;
|
||||
if (include_only_confirmed && !node.ledger.block_confirmed (block_transaction, key.hash))
|
||||
{
|
||||
if (threshold.is_zero () && !source)
|
||||
continue;
|
||||
}
|
||||
if (threshold.is_zero () && !source)
|
||||
{
|
||||
boost::property_tree::ptree entry;
|
||||
entry.put ("", key.hash.to_string ());
|
||||
peers_l.push_back (std::make_pair ("", entry));
|
||||
continue;
|
||||
}
|
||||
if (info.amount.number () < threshold.number ())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (source || min_version)
|
||||
{
|
||||
boost::property_tree::ptree pending_tree;
|
||||
pending_tree.put ("amount", info.amount.number ().template convert_to<std::string> ());
|
||||
if (source)
|
||||
{
|
||||
boost::property_tree::ptree entry;
|
||||
entry.put ("", key.hash.to_string ());
|
||||
peers_l.push_back (std::make_pair ("", entry));
|
||||
pending_tree.put ("source", info.source.to_account ());
|
||||
}
|
||||
else
|
||||
if (min_version)
|
||||
{
|
||||
nano::pending_info info (ii->second);
|
||||
if (info.amount.number () >= threshold.number ())
|
||||
{
|
||||
if (source || min_version)
|
||||
{
|
||||
boost::property_tree::ptree pending_tree;
|
||||
pending_tree.put ("amount", info.amount.number ().convert_to<std::string> ());
|
||||
if (source)
|
||||
{
|
||||
pending_tree.put ("source", info.source.to_account ());
|
||||
}
|
||||
if (min_version)
|
||||
{
|
||||
pending_tree.put ("min_version", epoch_as_string (info.epoch));
|
||||
}
|
||||
peers_l.add_child (key.hash.to_string (), pending_tree);
|
||||
}
|
||||
else
|
||||
{
|
||||
peers_l.put (key.hash.to_string (), info.amount.number ().convert_to<std::string> ());
|
||||
}
|
||||
}
|
||||
pending_tree.put ("min_version", epoch_as_string (info.epoch));
|
||||
}
|
||||
peers_l.add_child (key.hash.to_string (), pending_tree);
|
||||
}
|
||||
else
|
||||
{
|
||||
peers_l.put (key.hash.to_string (), info.amount.number ().template convert_to<std::string> ());
|
||||
}
|
||||
}
|
||||
if (!peers_l.empty ())
|
||||
|
|
@ -5357,7 +5358,6 @@ ipc_json_handler_no_arg_func_map create_ipc_json_handler_no_arg_func_map ()
|
|||
no_arg_funcs.emplace ("bootstrap_lazy", &nano::json_handler::bootstrap_lazy);
|
||||
no_arg_funcs.emplace ("bootstrap_status", &nano::json_handler::bootstrap_status);
|
||||
no_arg_funcs.emplace ("confirmation_active", &nano::json_handler::confirmation_active);
|
||||
no_arg_funcs.emplace ("confirmation_height_currently_processing", &nano::json_handler::confirmation_height_currently_processing);
|
||||
no_arg_funcs.emplace ("confirmation_history", &nano::json_handler::confirmation_history);
|
||||
no_arg_funcs.emplace ("confirmation_info", &nano::json_handler::confirmation_info);
|
||||
no_arg_funcs.emplace ("confirmation_quorum", &nano::json_handler::confirmation_quorum);
|
||||
|
|
@ -5365,6 +5365,7 @@ ipc_json_handler_no_arg_func_map create_ipc_json_handler_no_arg_func_map ()
|
|||
no_arg_funcs.emplace ("delegators", &nano::json_handler::delegators);
|
||||
no_arg_funcs.emplace ("delegators_count", &nano::json_handler::delegators_count);
|
||||
no_arg_funcs.emplace ("deterministic_key", &nano::json_handler::deterministic_key);
|
||||
no_arg_funcs.emplace ("election_statistics", &nano::json_handler::election_statistics);
|
||||
no_arg_funcs.emplace ("epoch_upgrade", &nano::json_handler::epoch_upgrade);
|
||||
no_arg_funcs.emplace ("frontiers", &nano::json_handler::frontiers);
|
||||
no_arg_funcs.emplace ("frontier_count", &nano::json_handler::account_count);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ public:
|
|||
void accounts_pending ();
|
||||
void accounts_receivable ();
|
||||
void active_difficulty ();
|
||||
void election_statistics ();
|
||||
void available_supply ();
|
||||
void block_info ();
|
||||
void block_confirm ();
|
||||
|
|
@ -64,7 +65,6 @@ public:
|
|||
void confirmation_history ();
|
||||
void confirmation_info ();
|
||||
void confirmation_quorum ();
|
||||
void confirmation_height_currently_processing ();
|
||||
void debug_bootstrap_priority_info ();
|
||||
void database_txn_tracker ();
|
||||
void delegators ();
|
||||
|
|
|
|||
115
nano/node/local_vote_history.cpp
Normal file
115
nano/node/local_vote_history.cpp
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#include <nano/node/local_vote_history.hpp>
|
||||
#include <nano/secure/common.hpp>
|
||||
#include <nano/secure/vote.hpp>
|
||||
|
||||
bool nano::local_vote_history::consistency_check (nano::root const & root_a) const
|
||||
{
|
||||
auto & history_by_root (history.get<tag_root> ());
|
||||
auto const range (history_by_root.equal_range (root_a));
|
||||
// All cached votes for a root must be for the same hash, this is actively enforced in local_vote_history::add
|
||||
auto consistent_same = std::all_of (range.first, range.second, [hash = range.first->hash] (auto const & info_a) { return info_a.hash == hash; });
|
||||
std::vector<nano::account> accounts;
|
||||
std::transform (range.first, range.second, std::back_inserter (accounts), [] (auto const & info_a) { return info_a.vote->account; });
|
||||
std::sort (accounts.begin (), accounts.end ());
|
||||
// All cached votes must be unique by account, this is actively enforced in local_vote_history::add
|
||||
auto consistent_unique = accounts.size () == std::unique (accounts.begin (), accounts.end ()) - accounts.begin ();
|
||||
auto result = consistent_same && consistent_unique;
|
||||
debug_assert (result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::local_vote_history::add (nano::root const & root_a, nano::block_hash const & hash_a, std::shared_ptr<nano::vote> const & vote_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ mutex };
|
||||
clean ();
|
||||
auto add_vote (true);
|
||||
auto & history_by_root (history.get<tag_root> ());
|
||||
// Erase any vote that is not for this hash, or duplicate by account, and if new timestamp is higher
|
||||
auto range (history_by_root.equal_range (root_a));
|
||||
for (auto i (range.first); i != range.second;)
|
||||
{
|
||||
if (i->hash != hash_a || (vote_a->account == i->vote->account && i->vote->timestamp () <= vote_a->timestamp ()))
|
||||
{
|
||||
i = history_by_root.erase (i);
|
||||
}
|
||||
else if (vote_a->account == i->vote->account && i->vote->timestamp () > vote_a->timestamp ())
|
||||
{
|
||||
add_vote = false;
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
// Do not add new vote to cache if representative account is same and timestamp is lower
|
||||
if (add_vote)
|
||||
{
|
||||
auto result (history_by_root.emplace (root_a, hash_a, vote_a));
|
||||
(void)result;
|
||||
debug_assert (result.second);
|
||||
}
|
||||
debug_assert (consistency_check (root_a));
|
||||
}
|
||||
|
||||
void nano::local_vote_history::erase (nano::root const & root_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ mutex };
|
||||
auto & history_by_root (history.get<tag_root> ());
|
||||
auto range (history_by_root.equal_range (root_a));
|
||||
history_by_root.erase (range.first, range.second);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<nano::vote>> nano::local_vote_history::votes (nano::root const & root_a) const
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ mutex };
|
||||
std::vector<std::shared_ptr<nano::vote>> result;
|
||||
auto range (history.get<tag_root> ().equal_range (root_a));
|
||||
std::transform (range.first, range.second, std::back_inserter (result), [] (auto const & entry) { return entry.vote; });
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<nano::vote>> nano::local_vote_history::votes (nano::root const & root_a, nano::block_hash const & hash_a, bool const is_final_a) const
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ mutex };
|
||||
std::vector<std::shared_ptr<nano::vote>> result;
|
||||
auto range (history.get<tag_root> ().equal_range (root_a));
|
||||
// clang-format off
|
||||
nano::transform_if (range.first, range.second, std::back_inserter (result),
|
||||
[&hash_a, is_final_a](auto const & entry) { return entry.hash == hash_a && (!is_final_a || entry.vote->timestamp () == std::numeric_limits<uint64_t>::max ()); },
|
||||
[](auto const & entry) { return entry.vote; });
|
||||
// clang-format on
|
||||
return result;
|
||||
}
|
||||
|
||||
bool nano::local_vote_history::exists (nano::root const & root_a) const
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ mutex };
|
||||
return history.get<tag_root> ().find (root_a) != history.get<tag_root> ().end ();
|
||||
}
|
||||
|
||||
void nano::local_vote_history::clean ()
|
||||
{
|
||||
debug_assert (constants.max_cache > 0);
|
||||
auto & history_by_sequence (history.get<tag_sequence> ());
|
||||
while (history_by_sequence.size () > constants.max_cache)
|
||||
{
|
||||
history_by_sequence.erase (history_by_sequence.begin ());
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t nano::local_vote_history::size () const
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ mutex };
|
||||
return history.size ();
|
||||
}
|
||||
|
||||
std::unique_ptr<nano::container_info_component> nano::local_vote_history::collect_container_info (std::string const & name) const
|
||||
{
|
||||
std::size_t history_count = size ();
|
||||
auto sizeof_element = sizeof (decltype (history)::value_type);
|
||||
auto composite = std::make_unique<container_info_composite> (name);
|
||||
/* This does not currently loop over each element inside the cache to get the sizes of the votes inside history*/
|
||||
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "history", history_count, sizeof_element }));
|
||||
return composite;
|
||||
}
|
||||
74
nano/node/local_vote_history.hpp
Normal file
74
nano/node/local_vote_history.hpp
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/locks.hpp>
|
||||
#include <nano/lib/numbers.hpp>
|
||||
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/multi_index/sequenced_index.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace mi = boost::multi_index;
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class container_info_component;
|
||||
class vote;
|
||||
class voting_constants;
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class local_vote_history final
|
||||
{
|
||||
class local_vote final
|
||||
{
|
||||
public:
|
||||
local_vote (nano::root const & root_a, nano::block_hash const & hash_a, std::shared_ptr<nano::vote> const & vote_a) :
|
||||
root (root_a),
|
||||
hash (hash_a),
|
||||
vote (vote_a)
|
||||
{
|
||||
}
|
||||
nano::root root;
|
||||
nano::block_hash hash;
|
||||
std::shared_ptr<nano::vote> vote;
|
||||
};
|
||||
|
||||
public:
|
||||
local_vote_history (nano::voting_constants const & constants) :
|
||||
constants{ constants }
|
||||
{
|
||||
}
|
||||
void add (nano::root const & root_a, nano::block_hash const & hash_a, std::shared_ptr<nano::vote> const & vote_a);
|
||||
void erase (nano::root const & root_a);
|
||||
|
||||
std::vector<std::shared_ptr<nano::vote>> votes (nano::root const & root_a, nano::block_hash const & hash_a, bool const is_final_a = false) const;
|
||||
bool exists (nano::root const &) const;
|
||||
std::size_t size () const;
|
||||
|
||||
std::unique_ptr<container_info_component> collect_container_info (std::string const & name) const;
|
||||
|
||||
private:
|
||||
// clang-format off
|
||||
boost::multi_index_container<local_vote,
|
||||
mi::indexed_by<
|
||||
mi::hashed_non_unique<mi::tag<class tag_root>,
|
||||
mi::member<local_vote, nano::root, &local_vote::root>>,
|
||||
mi::sequenced<mi::tag<class tag_sequence>>>>
|
||||
history;
|
||||
// clang-format on
|
||||
|
||||
nano::voting_constants const & constants;
|
||||
void clean ();
|
||||
std::vector<std::shared_ptr<nano::vote>> votes (nano::root const & root_a) const;
|
||||
// Only used in Debug
|
||||
bool consistency_check (nano::root const &) const;
|
||||
mutable nano::mutex mutex;
|
||||
|
||||
friend class local_vote_history_basic_Test;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#include <nano/lib/logging.hpp>
|
||||
#include <nano/node/make_store.hpp>
|
||||
#include <nano/store/lmdb/lmdb.hpp>
|
||||
#include <nano/store/rocksdb/rocksdb.hpp>
|
||||
|
|
|
|||
|
|
@ -133,7 +133,6 @@ void nano::message_header::count_v2_set (uint8_t count)
|
|||
{
|
||||
debug_assert (type == nano::message_type::confirm_ack || type == nano::message_type::confirm_req);
|
||||
debug_assert (flag_test (confirm_v2_flag)); // Only valid for v2
|
||||
debug_assert (count < 256); // Max 8 bits
|
||||
|
||||
extensions &= ~(count_v2_mask_left | count_v2_mask_right);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,89 +9,208 @@
|
|||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
/*
|
||||
* network
|
||||
*/
|
||||
|
||||
nano::network::network (nano::node & node_a, uint16_t port_a) :
|
||||
id (nano::network_constants::active_network),
|
||||
syn_cookies (node_a.network_params.network.max_peers_per_ip),
|
||||
inbound{ [this] (nano::message const & message, std::shared_ptr<nano::transport::channel> const & channel) {
|
||||
debug_assert (message.header.network == node.network_params.network.current_network);
|
||||
debug_assert (message.header.version_using >= node.network_params.network.protocol_version_min);
|
||||
process_message (message, channel);
|
||||
} },
|
||||
resolver (node_a.io_ctx),
|
||||
tcp_message_manager (node_a.config.tcp_incoming_connections_max),
|
||||
node (node_a),
|
||||
publish_filter (256 * 1024),
|
||||
tcp_channels (node_a, inbound),
|
||||
port (port_a),
|
||||
disconnect_observer ([] () {})
|
||||
nano::network::network (nano::node & node, uint16_t port) :
|
||||
node{ node },
|
||||
id{ nano::network_constants::active_network },
|
||||
syn_cookies{ node.network_params.network.max_peers_per_ip, node.logger },
|
||||
resolver{ node.io_ctx },
|
||||
publish_filter{ 256 * 1024 },
|
||||
tcp_channels{ node, [this] (nano::message const & message, std::shared_ptr<nano::transport::channel> const & channel) {
|
||||
inbound (message, channel);
|
||||
} },
|
||||
port{ port }
|
||||
{
|
||||
for (std::size_t i = 0; i < node.config.network_threads && !node.flags.disable_tcp_realtime; ++i)
|
||||
{
|
||||
packet_processing_threads.emplace_back (nano::thread_attributes::get_default (), [this, i] () {
|
||||
nano::thread_role::set (nano::thread_role::name::packet_processing);
|
||||
try
|
||||
{
|
||||
tcp_channels.process_messages ();
|
||||
}
|
||||
catch (boost::system::error_code & ec)
|
||||
{
|
||||
node.logger.critical (nano::log::type::network, "Error: {}", ec.message ());
|
||||
release_assert (false);
|
||||
}
|
||||
catch (std::error_code & ec)
|
||||
{
|
||||
node.logger.critical (nano::log::type::network, "Error: {}", ec.message ());
|
||||
release_assert (false);
|
||||
}
|
||||
catch (std::runtime_error & err)
|
||||
{
|
||||
node.logger.critical (nano::log::type::network, "Error: {}", err.what ());
|
||||
release_assert (false);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
node.logger.critical (nano::log::type::network, "Unknown error");
|
||||
release_assert (false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
nano::network::~network ()
|
||||
{
|
||||
stop ();
|
||||
// All threads must be stopped before this destructor
|
||||
debug_assert (processing_threads.empty ());
|
||||
debug_assert (!cleanup_thread.joinable ());
|
||||
debug_assert (!keepalive_thread.joinable ());
|
||||
}
|
||||
|
||||
void nano::network::start ()
|
||||
{
|
||||
if (!node.flags.disable_connection_cleanup)
|
||||
{
|
||||
ongoing_cleanup ();
|
||||
}
|
||||
ongoing_syn_cookie_cleanup ();
|
||||
cleanup_thread = std::thread ([this] () {
|
||||
nano::thread_role::set (nano::thread_role::name::network_cleanup);
|
||||
run_cleanup ();
|
||||
});
|
||||
|
||||
keepalive_thread = std::thread ([this] () {
|
||||
nano::thread_role::set (nano::thread_role::name::network_keepalive);
|
||||
run_keepalive ();
|
||||
});
|
||||
|
||||
reachout_thread = std::thread ([this] () {
|
||||
nano::thread_role::set (nano::thread_role::name::network_reachout);
|
||||
run_reachout ();
|
||||
});
|
||||
|
||||
if (!node.flags.disable_tcp_realtime)
|
||||
{
|
||||
tcp_channels.start ();
|
||||
|
||||
for (std::size_t i = 0; i < node.config.network_threads; ++i)
|
||||
{
|
||||
processing_threads.emplace_back (nano::thread_attributes::get_default (), [this] () {
|
||||
nano::thread_role::set (nano::thread_role::name::packet_processing);
|
||||
run_processing ();
|
||||
});
|
||||
}
|
||||
}
|
||||
ongoing_keepalive ();
|
||||
}
|
||||
|
||||
void nano::network::stop ()
|
||||
{
|
||||
if (!stopped.exchange (true))
|
||||
{
|
||||
tcp_channels.stop ();
|
||||
resolver.cancel ();
|
||||
tcp_message_manager.stop ();
|
||||
port = 0;
|
||||
for (auto & thread : packet_processing_threads)
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
stopped = true;
|
||||
}
|
||||
condition.notify_all ();
|
||||
|
||||
tcp_channels.stop ();
|
||||
resolver.cancel ();
|
||||
|
||||
for (auto & thread : processing_threads)
|
||||
{
|
||||
thread.join ();
|
||||
}
|
||||
processing_threads.clear ();
|
||||
|
||||
if (keepalive_thread.joinable ())
|
||||
{
|
||||
keepalive_thread.join ();
|
||||
}
|
||||
if (cleanup_thread.joinable ())
|
||||
{
|
||||
cleanup_thread.join ();
|
||||
}
|
||||
if (reachout_thread.joinable ())
|
||||
{
|
||||
reachout_thread.join ();
|
||||
}
|
||||
|
||||
port = 0;
|
||||
}
|
||||
|
||||
void nano::network::run_processing ()
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: Move responsibility of packet queuing and processing to the message_processor class
|
||||
tcp_channels.process_messages ();
|
||||
}
|
||||
catch (boost::system::error_code & ec)
|
||||
{
|
||||
node.logger.critical (nano::log::type::network, "Error: {}", ec.message ());
|
||||
release_assert (false);
|
||||
}
|
||||
catch (std::error_code & ec)
|
||||
{
|
||||
node.logger.critical (nano::log::type::network, "Error: {}", ec.message ());
|
||||
release_assert (false);
|
||||
}
|
||||
catch (std::runtime_error & err)
|
||||
{
|
||||
node.logger.critical (nano::log::type::network, "Error: {}", err.what ());
|
||||
release_assert (false);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
node.logger.critical (nano::log::type::network, "Unknown error");
|
||||
release_assert (false);
|
||||
}
|
||||
}
|
||||
|
||||
void nano::network::run_cleanup ()
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
while (!stopped)
|
||||
{
|
||||
condition.wait_for (lock, node.network_params.network.is_dev_network () ? 1s : 5s);
|
||||
if (stopped)
|
||||
{
|
||||
thread.join ();
|
||||
return;
|
||||
}
|
||||
lock.unlock ();
|
||||
|
||||
node.stats.inc (nano::stat::type::network, nano::stat::detail::loop_cleanup);
|
||||
|
||||
if (!node.flags.disable_connection_cleanup)
|
||||
{
|
||||
auto const cutoff = std::chrono::steady_clock::now () - node.network_params.network.cleanup_cutoff ();
|
||||
cleanup (cutoff);
|
||||
}
|
||||
|
||||
auto const syn_cookie_cutoff = std::chrono::steady_clock::now () - node.network_params.network.syn_cookie_cutoff;
|
||||
syn_cookies.purge (syn_cookie_cutoff);
|
||||
|
||||
lock.lock ();
|
||||
}
|
||||
}
|
||||
|
||||
void nano::network::run_keepalive ()
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
while (!stopped)
|
||||
{
|
||||
condition.wait_for (lock, node.network_params.network.keepalive_period);
|
||||
if (stopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
lock.unlock ();
|
||||
|
||||
node.stats.inc (nano::stat::type::network, nano::stat::detail::loop_keepalive);
|
||||
|
||||
flood_keepalive (0.75f);
|
||||
flood_keepalive_self (0.25f);
|
||||
|
||||
tcp_channels.keepalive ();
|
||||
|
||||
lock.lock ();
|
||||
}
|
||||
}
|
||||
|
||||
void nano::network::run_reachout ()
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
while (!stopped)
|
||||
{
|
||||
condition.wait_for (lock, node.network_params.network.merge_period);
|
||||
if (stopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
lock.unlock ();
|
||||
|
||||
node.stats.inc (nano::stat::type::network, nano::stat::detail::loop_reachout);
|
||||
|
||||
auto keepalive = tcp_channels.sample_keepalive ();
|
||||
if (keepalive)
|
||||
{
|
||||
for (auto const & peer : keepalive->peers)
|
||||
{
|
||||
if (stopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
merge_peer (peer);
|
||||
|
||||
// Throttle reachout attempts
|
||||
std::this_thread::sleep_for (node.network_params.network.merge_period);
|
||||
}
|
||||
}
|
||||
|
||||
lock.lock ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -109,34 +228,6 @@ void nano::network::send_keepalive_self (std::shared_ptr<nano::transport::channe
|
|||
channel_a->send (message);
|
||||
}
|
||||
|
||||
void nano::network::send_node_id_handshake (std::shared_ptr<nano::transport::channel> const & channel_a, std::optional<nano::uint256_union> const & cookie, std::optional<nano::uint256_union> const & respond_to)
|
||||
{
|
||||
std::optional<nano::node_id_handshake::response_payload> response;
|
||||
if (respond_to)
|
||||
{
|
||||
nano::node_id_handshake::response_payload pld{ node.node_id.pub, nano::sign_message (node.node_id.prv, node.node_id.pub, *respond_to) };
|
||||
debug_assert (!nano::validate_message (pld.node_id, *respond_to, pld.signature));
|
||||
response = pld;
|
||||
}
|
||||
|
||||
std::optional<nano::node_id_handshake::query_payload> query;
|
||||
if (cookie)
|
||||
{
|
||||
nano::node_id_handshake::query_payload pld{ *cookie };
|
||||
query = pld;
|
||||
}
|
||||
|
||||
nano::node_id_handshake message{ node.network_params.network, query, response };
|
||||
|
||||
node.logger.debug (nano::log::type::network, "Node ID handshake sent to: {} (query: {}, respond to: {}, signature: {})",
|
||||
nano::util::to_str (channel_a->get_endpoint ()),
|
||||
(query ? query->cookie.to_string () : "<none>"),
|
||||
(respond_to ? respond_to->to_string () : "<none>"),
|
||||
(response ? response->signature.to_string () : "<none>"));
|
||||
|
||||
channel_a->send (message);
|
||||
}
|
||||
|
||||
void nano::network::flood_message (nano::message & message_a, nano::transport::buffer_drop_policy const drop_policy_a, float const scale_a)
|
||||
{
|
||||
for (auto & i : list (fanout (scale_a)))
|
||||
|
|
@ -220,14 +311,6 @@ void nano::network::flood_block_many (std::deque<std::shared_ptr<nano::block>> b
|
|||
}
|
||||
}
|
||||
|
||||
void nano::network::send_confirm_req (std::shared_ptr<nano::transport::channel> const & channel_a, std::pair<nano::block_hash, nano::root> const & hash_root_a)
|
||||
{
|
||||
auto & [hash, root] = hash_root_a;
|
||||
// Confirmation request with hash + root
|
||||
nano::confirm_req req (node.network_params.network, hash, root);
|
||||
channel_a->send (req);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class network_message_visitor : public nano::message_visitor
|
||||
|
|
@ -351,6 +434,13 @@ void nano::network::process_message (nano::message const & message, std::shared_
|
|||
message.visit (visitor);
|
||||
}
|
||||
|
||||
void nano::network::inbound (const nano::message & message, const std::shared_ptr<nano::transport::channel> & channel)
|
||||
{
|
||||
debug_assert (message.header.network == node.network_params.network.current_network);
|
||||
debug_assert (message.header.version_using >= node.network_params.network.protocol_version_min);
|
||||
process_message (message, channel);
|
||||
}
|
||||
|
||||
// Send keepalives to all the peers we've been notified of
|
||||
void nano::network::merge_peers (std::array<nano::endpoint, 8> const & peers_a)
|
||||
{
|
||||
|
|
@ -362,10 +452,11 @@ void nano::network::merge_peers (std::array<nano::endpoint, 8> const & peers_a)
|
|||
|
||||
void nano::network::merge_peer (nano::endpoint const & peer_a)
|
||||
{
|
||||
if (!reachout (peer_a, node.config.allow_local_peers))
|
||||
if (track_reachout (peer_a))
|
||||
{
|
||||
std::weak_ptr<nano::node> node_w (node.shared ());
|
||||
node.network.tcp_channels.start_tcp (peer_a);
|
||||
node.stats.inc (nano::stat::type::network, nano::stat::detail::merge_peer);
|
||||
|
||||
tcp_channels.start_tcp (peer_a);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -387,15 +478,14 @@ bool nano::network::not_a_peer (nano::endpoint const & endpoint_a, bool allow_lo
|
|||
return result;
|
||||
}
|
||||
|
||||
bool nano::network::reachout (nano::endpoint const & endpoint_a, bool allow_local_peers)
|
||||
bool nano::network::track_reachout (nano::endpoint const & endpoint_a)
|
||||
{
|
||||
// Don't contact invalid IPs
|
||||
bool error = not_a_peer (endpoint_a, allow_local_peers);
|
||||
if (!error)
|
||||
if (not_a_peer (endpoint_a, node.config.allow_local_peers))
|
||||
{
|
||||
error = tcp_channels.reachout (endpoint_a);
|
||||
return false;
|
||||
}
|
||||
return error;
|
||||
return tcp_channels.track_reachout (endpoint_a);
|
||||
}
|
||||
|
||||
std::deque<std::shared_ptr<nano::transport::channel>> nano::network::list (std::size_t count_a, uint8_t minimum_version_a, bool include_tcp_temporary_channels_a)
|
||||
|
|
@ -503,52 +593,18 @@ nano::endpoint nano::network::endpoint () const
|
|||
return nano::endpoint (boost::asio::ip::address_v6::loopback (), port);
|
||||
}
|
||||
|
||||
void nano::network::cleanup (std::chrono::steady_clock::time_point const & cutoff_a)
|
||||
void nano::network::cleanup (std::chrono::steady_clock::time_point const & cutoff)
|
||||
{
|
||||
tcp_channels.purge (cutoff_a);
|
||||
node.logger.debug (nano::log::type::network, "Performing cleanup, cutoff: {}s", nano::log::seconds_delta (cutoff));
|
||||
|
||||
tcp_channels.purge (cutoff);
|
||||
|
||||
if (node.network.empty ())
|
||||
{
|
||||
disconnect_observer ();
|
||||
}
|
||||
}
|
||||
|
||||
void nano::network::ongoing_cleanup ()
|
||||
{
|
||||
cleanup (std::chrono::steady_clock::now () - node.network_params.network.cleanup_cutoff ());
|
||||
std::weak_ptr<nano::node> node_w (node.shared ());
|
||||
node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (node.network_params.network.is_dev_network () ? 1 : 5), [node_w] () {
|
||||
if (auto node_l = node_w.lock ())
|
||||
{
|
||||
node_l->network.ongoing_cleanup ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void nano::network::ongoing_syn_cookie_cleanup ()
|
||||
{
|
||||
syn_cookies.purge (std::chrono::steady_clock::now () - nano::transport::syn_cookie_cutoff);
|
||||
std::weak_ptr<nano::node> node_w (node.shared ());
|
||||
node.workers.add_timed_task (std::chrono::steady_clock::now () + (nano::transport::syn_cookie_cutoff * 2), [node_w] () {
|
||||
if (auto node_l = node_w.lock ())
|
||||
{
|
||||
node_l->network.ongoing_syn_cookie_cleanup ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void nano::network::ongoing_keepalive ()
|
||||
{
|
||||
flood_keepalive (0.75f);
|
||||
flood_keepalive_self (0.25f);
|
||||
std::weak_ptr<nano::node> node_w (node.shared ());
|
||||
node.workers.add_timed_task (std::chrono::steady_clock::now () + node.network_params.network.keepalive_period, [node_w] () {
|
||||
if (auto node_l = node_w.lock ())
|
||||
{
|
||||
node_l->network.ongoing_keepalive ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::size_t nano::network::size () const
|
||||
{
|
||||
return tcp_channels.size ();
|
||||
|
|
@ -699,18 +755,19 @@ void nano::tcp_message_manager::stop ()
|
|||
* syn_cookies
|
||||
*/
|
||||
|
||||
nano::syn_cookies::syn_cookies (std::size_t max_cookies_per_ip_a) :
|
||||
max_cookies_per_ip (max_cookies_per_ip_a)
|
||||
nano::syn_cookies::syn_cookies (std::size_t max_cookies_per_ip_a, nano::logger & logger_a) :
|
||||
max_cookies_per_ip (max_cookies_per_ip_a),
|
||||
logger (logger_a)
|
||||
{
|
||||
}
|
||||
|
||||
boost::optional<nano::uint256_union> nano::syn_cookies::assign (nano::endpoint const & endpoint_a)
|
||||
std::optional<nano::uint256_union> nano::syn_cookies::assign (nano::endpoint const & endpoint_a)
|
||||
{
|
||||
auto ip_addr (endpoint_a.address ());
|
||||
debug_assert (ip_addr.is_v6 ());
|
||||
nano::lock_guard<nano::mutex> lock{ syn_cookie_mutex };
|
||||
unsigned & ip_cookies = cookies_per_ip[ip_addr];
|
||||
boost::optional<nano::uint256_union> result;
|
||||
std::optional<nano::uint256_union> result;
|
||||
if (ip_cookies < max_cookies_per_ip)
|
||||
{
|
||||
if (cookies.find (endpoint_a) == cookies.end ())
|
||||
|
|
@ -752,6 +809,8 @@ bool nano::syn_cookies::validate (nano::endpoint const & endpoint_a, nano::accou
|
|||
|
||||
void nano::syn_cookies::purge (std::chrono::steady_clock::time_point const & cutoff_a)
|
||||
{
|
||||
logger.debug (nano::log::type::syn_cookies, "Purging syn cookies, cutoff: {}s", nano::log::seconds_delta (cutoff_a));
|
||||
|
||||
nano::lock_guard<nano::mutex> lock{ syn_cookie_mutex };
|
||||
auto it (cookies.begin ());
|
||||
while (it != cookies.end ())
|
||||
|
|
|
|||
|
|
@ -16,39 +16,18 @@ namespace nano
|
|||
{
|
||||
class node;
|
||||
|
||||
class tcp_message_manager final
|
||||
{
|
||||
public:
|
||||
tcp_message_manager (unsigned incoming_connections_max_a);
|
||||
void put_message (nano::tcp_message_item const & item_a);
|
||||
nano::tcp_message_item get_message ();
|
||||
// Stop container and notify waiting threads
|
||||
void stop ();
|
||||
|
||||
private:
|
||||
nano::mutex mutex;
|
||||
nano::condition_variable producer_condition;
|
||||
nano::condition_variable consumer_condition;
|
||||
std::deque<nano::tcp_message_item> entries;
|
||||
unsigned max_entries;
|
||||
static unsigned const max_entries_per_connection = 16;
|
||||
bool stopped{ false };
|
||||
|
||||
friend class network_tcp_message_manager_Test;
|
||||
};
|
||||
|
||||
/**
|
||||
* Node ID cookies for node ID handshakes
|
||||
*/
|
||||
class syn_cookies final
|
||||
{
|
||||
public:
|
||||
explicit syn_cookies (std::size_t);
|
||||
syn_cookies (std::size_t max_peers_per_ip, nano::logger &);
|
||||
|
||||
void purge (std::chrono::steady_clock::time_point const &);
|
||||
// Returns boost::none if the IP is rate capped on syn cookie requests,
|
||||
// or if the endpoint already has a syn cookie query
|
||||
boost::optional<nano::uint256_union> assign (nano::endpoint const &);
|
||||
std::optional<nano::uint256_union> assign (nano::endpoint const &);
|
||||
// Returns false if valid, true if invalid (true on error convention)
|
||||
// Also removes the syn cookie from the store if valid
|
||||
bool validate (nano::endpoint const &, nano::account const &, nano::signature const &);
|
||||
|
|
@ -58,6 +37,9 @@ public:
|
|||
std::unique_ptr<container_info_component> collect_container_info (std::string const &);
|
||||
std::size_t cookies_size ();
|
||||
|
||||
private: // Dependencies
|
||||
nano::logger & logger;
|
||||
|
||||
private:
|
||||
class syn_cookie_info final
|
||||
{
|
||||
|
|
@ -74,12 +56,12 @@ private:
|
|||
class network final
|
||||
{
|
||||
public:
|
||||
network (nano::node &, uint16_t);
|
||||
network (nano::node &, uint16_t port);
|
||||
~network ();
|
||||
|
||||
nano::networks id;
|
||||
void start ();
|
||||
void stop ();
|
||||
|
||||
void flood_message (nano::message &, nano::transport::buffer_drop_policy const = nano::transport::buffer_drop_policy::limiter, float const = 1.0f);
|
||||
void flood_keepalive (float const scale_a = 1.0f);
|
||||
void flood_keepalive_self (float const scale_a = 0.5f);
|
||||
|
|
@ -94,13 +76,11 @@ public:
|
|||
void merge_peer (nano::endpoint const &);
|
||||
void send_keepalive (std::shared_ptr<nano::transport::channel> const &);
|
||||
void send_keepalive_self (std::shared_ptr<nano::transport::channel> const &);
|
||||
void send_node_id_handshake (std::shared_ptr<nano::transport::channel> const &, std::optional<nano::uint256_union> const & cookie, std::optional<nano::uint256_union> const & respond_to);
|
||||
void send_confirm_req (std::shared_ptr<nano::transport::channel> const & channel_a, std::pair<nano::block_hash, nano::root> const & hash_root_a);
|
||||
std::shared_ptr<nano::transport::channel> find_node_id (nano::account const &);
|
||||
std::shared_ptr<nano::transport::channel> find_channel (nano::endpoint const &);
|
||||
bool not_a_peer (nano::endpoint const &, bool);
|
||||
// Should we reach out to this endpoint with a keepalive message
|
||||
bool reachout (nano::endpoint const &, bool = false);
|
||||
bool not_a_peer (nano::endpoint const &, bool allow_local_peers);
|
||||
// Should we reach out to this endpoint with a keepalive message? If yes, register a new reachout attempt
|
||||
bool track_reachout (nano::endpoint const &);
|
||||
std::deque<std::shared_ptr<nano::transport::channel>> list (std::size_t max_count = 0, uint8_t = 0, bool = true);
|
||||
std::deque<std::shared_ptr<nano::transport::channel>> list_non_pr (std::size_t);
|
||||
// Desired fanout for a given scale
|
||||
|
|
@ -112,41 +92,55 @@ public:
|
|||
// Get the next peer for attempting a tcp bootstrap connection
|
||||
nano::tcp_endpoint bootstrap_peer ();
|
||||
nano::endpoint endpoint () const;
|
||||
void cleanup (std::chrono::steady_clock::time_point const &);
|
||||
void ongoing_cleanup ();
|
||||
// Node ID cookies cleanup
|
||||
nano::syn_cookies syn_cookies;
|
||||
void ongoing_syn_cookie_cleanup ();
|
||||
void ongoing_keepalive ();
|
||||
void cleanup (std::chrono::steady_clock::time_point const & cutoff);
|
||||
std::size_t size () const;
|
||||
float size_sqrt () const;
|
||||
bool empty () const;
|
||||
void erase (nano::transport::channel const &);
|
||||
/** Disconnects and adds peer to exclusion list */
|
||||
void exclude (std::shared_ptr<nano::transport::channel> const & channel);
|
||||
void inbound (nano::message const &, std::shared_ptr<nano::transport::channel> const &);
|
||||
|
||||
public: // Handshake
|
||||
/** Verifies that handshake response matches our query. @returns true if OK */
|
||||
bool verify_handshake_response (nano::node_id_handshake::response_payload const & response, nano::endpoint const & remote_endpoint);
|
||||
std::optional<nano::node_id_handshake::query_payload> prepare_handshake_query (nano::endpoint const & remote_endpoint);
|
||||
nano::node_id_handshake::response_payload prepare_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2) const;
|
||||
|
||||
private:
|
||||
void run_processing ();
|
||||
void run_cleanup ();
|
||||
void run_keepalive ();
|
||||
void run_reachout ();
|
||||
void process_message (nano::message const &, std::shared_ptr<nano::transport::channel> const &);
|
||||
|
||||
public:
|
||||
std::function<void (nano::message const &, std::shared_ptr<nano::transport::channel> const &)> inbound;
|
||||
boost::asio::ip::udp::resolver resolver;
|
||||
std::vector<boost::thread> packet_processing_threads;
|
||||
nano::peer_exclusion excluded_peers;
|
||||
nano::tcp_message_manager tcp_message_manager;
|
||||
private: // Dependencies
|
||||
nano::node & node;
|
||||
|
||||
public:
|
||||
nano::networks const id;
|
||||
nano::syn_cookies syn_cookies;
|
||||
boost::asio::ip::udp::resolver resolver;
|
||||
nano::peer_exclusion excluded_peers;
|
||||
nano::network_filter publish_filter;
|
||||
nano::transport::tcp_channels tcp_channels;
|
||||
std::atomic<uint16_t> port{ 0 };
|
||||
std::function<void ()> disconnect_observer;
|
||||
|
||||
public: // Callbacks
|
||||
std::function<void ()> disconnect_observer{ [] () {} };
|
||||
// Called when a new channel is observed
|
||||
std::function<void (std::shared_ptr<nano::transport::channel>)> channel_observer;
|
||||
std::function<void (std::shared_ptr<nano::transport::channel>)> channel_observer{ [] (auto) {} };
|
||||
|
||||
private:
|
||||
std::atomic<bool> stopped{ false };
|
||||
mutable nano::mutex mutex;
|
||||
nano::condition_variable condition;
|
||||
std::vector<boost::thread> processing_threads; // Using boost::thread to enable increased stack size
|
||||
std::thread cleanup_thread;
|
||||
std::thread keepalive_thread;
|
||||
std::thread reachout_thread;
|
||||
|
||||
public:
|
||||
static unsigned const broadcast_interval_ms = 10;
|
||||
static std::size_t const buffer_size = 512;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@
|
|||
#include <nano/lib/stream.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
#include <nano/lib/utility.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/common.hpp>
|
||||
#include <nano/node/confirming_set.hpp>
|
||||
#include <nano/node/daemonconfig.hpp>
|
||||
#include <nano/node/election_status.hpp>
|
||||
#include <nano/node/local_vote_history.hpp>
|
||||
#include <nano/node/make_store.hpp>
|
||||
#include <nano/node/node.hpp>
|
||||
#include <nano/node/scheduler/component.hpp>
|
||||
|
|
@ -12,6 +16,8 @@
|
|||
#include <nano/node/scheduler/optimistic.hpp>
|
||||
#include <nano/node/scheduler/priority.hpp>
|
||||
#include <nano/node/telemetry.hpp>
|
||||
#include <nano/node/transport/tcp_listener.hpp>
|
||||
#include <nano/node/vote_generator.hpp>
|
||||
#include <nano/node/websocket.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/store/component.hpp>
|
||||
|
|
@ -120,15 +126,16 @@ nano::keypair nano::load_or_create_node_id (std::filesystem::path const & applic
|
|||
}
|
||||
}
|
||||
|
||||
nano::node::node (boost::asio::io_context & io_ctx_a, uint16_t peering_port_a, std::filesystem::path const & application_path_a, nano::work_pool & work_a, nano::node_flags flags_a, unsigned seq) :
|
||||
nano::node::node (std::shared_ptr<boost::asio::io_context> io_ctx_a, uint16_t peering_port_a, std::filesystem::path const & application_path_a, nano::work_pool & work_a, nano::node_flags flags_a, unsigned seq) :
|
||||
node (io_ctx_a, application_path_a, nano::node_config (peering_port_a), work_a, flags_a, seq)
|
||||
{
|
||||
}
|
||||
|
||||
nano::node::node (boost::asio::io_context & io_ctx_a, std::filesystem::path const & application_path_a, nano::node_config const & config_a, nano::work_pool & work_a, nano::node_flags flags_a, unsigned seq) :
|
||||
nano::node::node (std::shared_ptr<boost::asio::io_context> io_ctx_a, std::filesystem::path const & application_path_a, nano::node_config const & config_a, nano::work_pool & work_a, nano::node_flags flags_a, unsigned seq) :
|
||||
io_ctx_shared{ io_ctx_a },
|
||||
io_ctx{ *io_ctx_shared },
|
||||
node_id{ load_or_create_node_id (application_path_a) },
|
||||
write_database_queue (!flags_a.force_use_write_database_queue && (config_a.rocksdb_config.enable)),
|
||||
io_ctx (io_ctx_a),
|
||||
node_initialized_latch (1),
|
||||
config (config_a),
|
||||
network_params{ config.network_params },
|
||||
|
|
@ -144,7 +151,7 @@ nano::node::node (boost::asio::io_context & io_ctx_a, std::filesystem::path cons
|
|||
unchecked{ config.max_unchecked_blocks, stats, flags.disable_block_processor_unchecked_deletion },
|
||||
wallets_store_impl (std::make_unique<nano::mdb_wallets_store> (application_path_a / "wallets.ldb", config_a.lmdb_config)),
|
||||
wallets_store (*wallets_store_impl),
|
||||
ledger_impl{ std::make_unique<nano::ledger> (store, stats, network_params.ledger, flags_a.generate_cache) },
|
||||
ledger_impl{ std::make_unique<nano::ledger> (store, stats, network_params.ledger, flags_a.generate_cache, config_a.representative_vote_weight_minimum.number ()) },
|
||||
ledger{ *ledger_impl },
|
||||
outbound_limiter{ outbound_bandwidth_limiter_config (config) },
|
||||
// empty `config.peering_port` means the user made no port choice at all;
|
||||
|
|
@ -164,19 +171,24 @@ nano::node::node (boost::asio::io_context & io_ctx_a, std::filesystem::path cons
|
|||
tcp_listener{ std::make_shared<nano::transport::tcp_listener> (network.port, *this, config.tcp_incoming_connections_max) },
|
||||
application_path (application_path_a),
|
||||
port_mapping (*this),
|
||||
block_processor (*this, write_database_queue),
|
||||
confirming_set_impl{ std::make_unique<nano::confirming_set> (ledger, write_database_queue, config.confirming_set_batch_time) },
|
||||
confirming_set{ *confirming_set_impl },
|
||||
active_impl{ std::make_unique<nano::active_transactions> (*this, confirming_set, block_processor) },
|
||||
active{ *active_impl },
|
||||
rep_crawler (config.rep_crawler, *this),
|
||||
rep_tiers{ ledger, network_params, online_reps, stats, logger },
|
||||
vote_processor{ active, observers, stats, config, flags, logger, online_reps, rep_crawler, ledger, network_params, rep_tiers },
|
||||
warmed_up (0),
|
||||
block_processor (*this, write_database_queue),
|
||||
online_reps (ledger, config),
|
||||
history{ config.network_params.voting },
|
||||
history_impl{ std::make_unique<nano::local_vote_history> (config.network_params.voting) },
|
||||
history{ *history_impl },
|
||||
vote_uniquer{},
|
||||
confirmation_height_processor (ledger, write_database_queue, config.conf_height_processor_batch_min_time, logger, node_initialized_latch, flags.confirmation_height_processor_mode),
|
||||
vote_cache{ config.vote_cache, stats },
|
||||
generator{ config, ledger, wallets, vote_processor, history, network, stats, logger, /* non-final */ false },
|
||||
final_generator{ config, ledger, wallets, vote_processor, history, network, stats, logger, /* final */ true },
|
||||
active{ *this, confirmation_height_processor, block_processor },
|
||||
generator_impl{ std::make_unique<nano::vote_generator> (config, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* non-final */ false) },
|
||||
generator{ *generator_impl },
|
||||
final_generator_impl{ std::make_unique<nano::vote_generator> (config, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* final */ true) },
|
||||
final_generator{ *final_generator_impl },
|
||||
scheduler_impl{ std::make_unique<nano::scheduler::component> (*this) },
|
||||
scheduler{ *scheduler_impl },
|
||||
aggregator (config, stats, generator, final_generator, history, ledger, wallets, active),
|
||||
|
|
@ -207,6 +219,25 @@ nano::node::node (boost::asio::io_context & io_ctx_a, std::filesystem::path cons
|
|||
scheduler.optimistic.activate (account, account_info, conf_info);
|
||||
});
|
||||
|
||||
active.vote_processed.add ([this] (std::shared_ptr<nano::vote> const & vote, nano::vote_source source, std::unordered_map<nano::block_hash, nano::vote_code> const & results) {
|
||||
vote_cache.observe (vote, source, results);
|
||||
});
|
||||
|
||||
// Republish vote if it is new and the node does not host a principal representative (or close to)
|
||||
active.vote_processed.add ([this] (std::shared_ptr<nano::vote> const & vote, nano::vote_source source, std::unordered_map<nano::block_hash, nano::vote_code> const & results) {
|
||||
bool processed = std::any_of (results.begin (), results.end (), [] (auto const & result) {
|
||||
return result.second == nano::vote_code::vote;
|
||||
});
|
||||
if (processed)
|
||||
{
|
||||
auto const reps = wallets.reps ();
|
||||
if (!reps.have_half_rep () && !reps.exists (vote->account))
|
||||
{
|
||||
network.flood_vote (vote, 0.5f);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!init_error ())
|
||||
{
|
||||
// Notify election schedulers when AEC frees election slot
|
||||
|
|
@ -354,7 +385,7 @@ nano::node::node (boost::asio::io_context & io_ctx_a, std::filesystem::path cons
|
|||
|
||||
if (!is_initialized && !flags.read_only)
|
||||
{
|
||||
auto const transaction (store.tx_begin_write ({ tables::accounts, tables::blocks, tables::confirmation_height, tables::frontiers }));
|
||||
auto const transaction (store.tx_begin_write ({ tables::accounts, tables::blocks, tables::confirmation_height, tables::rep_weights }));
|
||||
// Store was empty meaning we just created it, add the genesis block
|
||||
store.initialize (transaction, ledger.cache, ledger.constants);
|
||||
}
|
||||
|
|
@ -391,10 +422,10 @@ nano::node::node (boost::asio::io_context & io_ctx_a, std::filesystem::path cons
|
|||
ledger.bootstrap_weight_max_blocks = bootstrap_weights.first;
|
||||
|
||||
logger.info (nano::log::type::node, "Initial bootstrap height: {}", ledger.bootstrap_weight_max_blocks);
|
||||
logger.info (nano::log::type::node, "Current ledger height: {}", ledger.cache.block_count.load ());
|
||||
logger.info (nano::log::type::node, "Current ledger height: {}", ledger.block_count ());
|
||||
|
||||
// Use bootstrap weights if initial bootstrap is not completed
|
||||
const bool use_bootstrap_weight = ledger.cache.block_count < bootstrap_weights.first;
|
||||
const bool use_bootstrap_weight = ledger.block_count () < bootstrap_weights.first;
|
||||
if (use_bootstrap_weight)
|
||||
{
|
||||
logger.info (nano::log::type::node, "Using predefined representative weights, since block count is less than bootstrap threshold");
|
||||
|
|
@ -435,6 +466,13 @@ nano::node::node (boost::asio::io_context & io_ctx_a, std::filesystem::path cons
|
|||
std::exit (1);
|
||||
}
|
||||
}
|
||||
confirming_set.cemented_observers.add ([this] (auto const & block) {
|
||||
if (block->is_send ())
|
||||
{
|
||||
auto transaction = store.tx_begin_read ();
|
||||
receive_confirmed (transaction, block->hash (), block->destination ());
|
||||
}
|
||||
});
|
||||
}
|
||||
node_initialized_latch.count_down ();
|
||||
}
|
||||
|
|
@ -516,10 +554,10 @@ std::unique_ptr<nano::container_info_component> nano::collect_container_info (no
|
|||
{
|
||||
auto composite = std::make_unique<container_info_composite> (name);
|
||||
composite->add_component (collect_container_info (node.work, "work"));
|
||||
composite->add_component (collect_container_info (node.ledger, "ledger"));
|
||||
composite->add_component (node.ledger.collect_container_info ("ledger"));
|
||||
composite->add_component (collect_container_info (node.active, "active"));
|
||||
composite->add_component (collect_container_info (node.bootstrap_initiator, "bootstrap_initiator"));
|
||||
composite->add_component (collect_container_info (*node.tcp_listener, "tcp_listener"));
|
||||
composite->add_component (node.tcp_listener->collect_container_info ("tcp_listener"));
|
||||
composite->add_component (collect_container_info (node.network, "network"));
|
||||
composite->add_component (node.telemetry.collect_container_info ("telemetry"));
|
||||
composite->add_component (collect_container_info (node.workers, "workers"));
|
||||
|
|
@ -529,16 +567,16 @@ std::unique_ptr<nano::container_info_component> nano::collect_container_info (no
|
|||
composite->add_component (node.rep_crawler.collect_container_info ("rep_crawler"));
|
||||
composite->add_component (node.block_processor.collect_container_info ("block_processor"));
|
||||
composite->add_component (collect_container_info (node.online_reps, "online_reps"));
|
||||
composite->add_component (collect_container_info (node.history, "history"));
|
||||
composite->add_component (node.history.collect_container_info ("history"));
|
||||
composite->add_component (node.block_uniquer.collect_container_info ("block_uniquer"));
|
||||
composite->add_component (node.vote_uniquer.collect_container_info ("vote_uniquer"));
|
||||
composite->add_component (collect_container_info (node.confirmation_height_processor, "confirmation_height_processor"));
|
||||
composite->add_component (node.confirming_set.collect_container_info ("confirming_set"));
|
||||
composite->add_component (collect_container_info (node.distributed_work, "distributed_work"));
|
||||
composite->add_component (collect_container_info (node.aggregator, "request_aggregator"));
|
||||
composite->add_component (node.scheduler.collect_container_info ("election_scheduler"));
|
||||
composite->add_component (node.vote_cache.collect_container_info ("vote_cache"));
|
||||
composite->add_component (collect_container_info (node.generator, "vote_generator"));
|
||||
composite->add_component (collect_container_info (node.final_generator, "vote_generator_final"));
|
||||
composite->add_component (node.generator.collect_container_info ("vote_generator"));
|
||||
composite->add_component (node.final_generator.collect_container_info ("vote_generator_final"));
|
||||
composite->add_component (node.ascendboot.collect_container_info ("bootstrap_ascending"));
|
||||
composite->add_component (node.unchecked.collect_container_info ("unchecked"));
|
||||
composite->add_component (node.local_block_broadcaster.collect_container_info ("local_block_broadcaster"));
|
||||
|
|
@ -558,7 +596,7 @@ void nano::node::process_active (std::shared_ptr<nano::block> const & incoming)
|
|||
|
||||
nano::block_status nano::node::process (std::shared_ptr<nano::block> block)
|
||||
{
|
||||
auto const transaction = store.tx_begin_write ({ tables::accounts, tables::blocks, tables::frontiers, tables::pending });
|
||||
auto const transaction = store.tx_begin_write ({ tables::accounts, tables::blocks, tables::pending, tables::rep_weights });
|
||||
return process (transaction, block);
|
||||
}
|
||||
|
||||
|
|
@ -643,6 +681,7 @@ void nano::node::start ()
|
|||
active.start ();
|
||||
generator.start ();
|
||||
final_generator.start ();
|
||||
confirming_set.start ();
|
||||
scheduler.start ();
|
||||
backlog.start ();
|
||||
bootstrap_server.start ();
|
||||
|
|
@ -683,8 +722,7 @@ void nano::node::stop ()
|
|||
active.stop ();
|
||||
generator.stop ();
|
||||
final_generator.stop ();
|
||||
confirmation_height_processor.stop ();
|
||||
network.stop ();
|
||||
confirming_set.stop ();
|
||||
telemetry.stop ();
|
||||
websocket.stop ();
|
||||
bootstrap_server.stop ();
|
||||
|
|
@ -696,6 +734,8 @@ void nano::node::stop ()
|
|||
epoch_upgrader.stop ();
|
||||
workers.stop ();
|
||||
local_block_broadcaster.stop ();
|
||||
network.stop (); // Stop network last to avoid killing in-use sockets
|
||||
|
||||
// work pool is not stopped on purpose due to testing setup
|
||||
}
|
||||
|
||||
|
|
@ -738,7 +778,8 @@ std::pair<nano::uint128_t, nano::uint128_t> nano::node::balance_pending (nano::a
|
|||
|
||||
nano::uint128_t nano::node::weight (nano::account const & account_a)
|
||||
{
|
||||
return ledger.weight (account_a);
|
||||
auto txn{ ledger.store.tx_begin_read () };
|
||||
return ledger.weight_exact (txn, account_a);
|
||||
}
|
||||
|
||||
nano::uint128_t nano::node::minimum_principal_weight ()
|
||||
|
|
@ -786,7 +827,7 @@ void nano::node::ongoing_bootstrap ()
|
|||
}
|
||||
// Differential bootstrap with max age (75% of all legacy attempts)
|
||||
uint32_t frontiers_age (std::numeric_limits<uint32_t>::max ());
|
||||
auto bootstrap_weight_reached (ledger.cache.block_count >= ledger.bootstrap_weight_max_blocks);
|
||||
auto bootstrap_weight_reached (ledger.block_count () >= ledger.bootstrap_weight_max_blocks);
|
||||
auto previous_bootstrap_count (stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate, nano::stat::dir::out) + stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate_legacy_age, nano::stat::dir::out));
|
||||
/*
|
||||
- Maximum value for 25% of attempts or if block count is below preconfigured value (initial bootstrap not finished)
|
||||
|
|
@ -984,7 +1025,7 @@ void nano::node::ledger_pruning (uint64_t const batch_size_a, bool bootstrap_wei
|
|||
|
||||
void nano::node::ongoing_ledger_pruning ()
|
||||
{
|
||||
auto bootstrap_weight_reached (ledger.cache.block_count >= ledger.bootstrap_weight_max_blocks);
|
||||
auto bootstrap_weight_reached (ledger.block_count () >= ledger.bootstrap_weight_max_blocks);
|
||||
ledger_pruning (flags.block_processor_batch_size != 0 ? flags.block_processor_batch_size : 2 * 1024, bootstrap_weight_reached);
|
||||
auto const ledger_pruning_interval (bootstrap_weight_reached ? config.max_pruning_age : std::min (config.max_pruning_age, std::chrono::seconds (15 * 60)));
|
||||
auto this_l (shared ());
|
||||
|
|
@ -1116,15 +1157,22 @@ void nano::node::add_initial_peers ()
|
|||
return;
|
||||
}
|
||||
|
||||
auto transaction (store.tx_begin_read ());
|
||||
for (auto i (store.peer.begin (transaction)), n (store.peer.end ()); i != n; ++i)
|
||||
std::vector<nano::endpoint> initial_peers;
|
||||
{
|
||||
nano::endpoint endpoint (boost::asio::ip::address_v6 (i->first.address_bytes ()), i->first.port ());
|
||||
if (!network.reachout (endpoint, config.allow_local_peers))
|
||||
auto transaction = store.tx_begin_read ();
|
||||
for (auto i (store.peer.begin (transaction)), n (store.peer.end ()); i != n; ++i)
|
||||
{
|
||||
network.tcp_channels.start_tcp (endpoint);
|
||||
nano::endpoint endpoint (boost::asio::ip::address_v6 (i->first.address_bytes ()), i->first.port ());
|
||||
initial_peers.push_back (endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info (nano::log::type::node, "Adding cached initial peers: {}", initial_peers.size ());
|
||||
|
||||
for (auto const & peer : initial_peers)
|
||||
{
|
||||
network.merge_peer (peer);
|
||||
}
|
||||
}
|
||||
|
||||
void nano::node::start_election (std::shared_ptr<nano::block> const & block)
|
||||
|
|
@ -1140,7 +1188,7 @@ bool nano::node::block_confirmed (nano::block_hash const & hash_a)
|
|||
|
||||
bool nano::node::block_confirmed_or_being_confirmed (nano::store::transaction const & transaction, nano::block_hash const & hash_a)
|
||||
{
|
||||
return confirmation_height_processor.is_processing_block (hash_a) || ledger.block_confirmed (transaction, hash_a);
|
||||
return confirming_set.exists (hash_a) || ledger.block_confirmed (transaction, hash_a);
|
||||
}
|
||||
|
||||
bool nano::node::block_confirmed_or_being_confirmed (nano::block_hash const & hash_a)
|
||||
|
|
@ -1204,47 +1252,6 @@ void nano::node::receive_confirmed (store::transaction const & block_transaction
|
|||
}
|
||||
}
|
||||
|
||||
void nano::node::process_confirmed_data (store::transaction const & transaction_a, std::shared_ptr<nano::block> const & block_a, nano::block_hash const & hash_a, nano::account & account_a, nano::uint128_t & amount_a, bool & is_state_send_a, bool & is_state_epoch_a, nano::account & pending_account_a)
|
||||
{
|
||||
// Faster account calculation
|
||||
account_a = block_a->account ();
|
||||
// Faster amount calculation
|
||||
auto previous (block_a->previous ());
|
||||
auto previous_balance = ledger.balance (transaction_a, previous);
|
||||
auto block_balance = block_a->balance ();
|
||||
if (hash_a != ledger.constants.genesis->account ())
|
||||
{
|
||||
if (previous_balance)
|
||||
{
|
||||
amount_a = block_balance > previous_balance.value () ? block_balance.number () - previous_balance.value () : previous_balance.value () - block_balance.number ();
|
||||
}
|
||||
else
|
||||
{
|
||||
amount_a = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
amount_a = nano::dev::constants.genesis_amount;
|
||||
}
|
||||
if (auto state = dynamic_cast<nano::state_block *> (block_a.get ()))
|
||||
{
|
||||
if (state->hashables.balance < previous_balance)
|
||||
{
|
||||
is_state_send_a = true;
|
||||
}
|
||||
if (amount_a == 0 && network_params.ledger.epochs.is_epoch_link (state->link_field ().value ()))
|
||||
{
|
||||
is_state_epoch_a = true;
|
||||
}
|
||||
pending_account_a = state->hashables.link.as_account ();
|
||||
}
|
||||
if (auto send = dynamic_cast<nano::send_block *> (block_a.get ()))
|
||||
{
|
||||
pending_account_a = send->hashables.destination;
|
||||
}
|
||||
}
|
||||
|
||||
void nano::node::process_confirmed (nano::election_status const & status_a, uint64_t iteration_a)
|
||||
{
|
||||
auto hash (status_a.winner->hash ());
|
||||
|
|
@ -1253,7 +1260,7 @@ void nano::node::process_confirmed (nano::election_status const & status_a, uint
|
|||
{
|
||||
logger.trace (nano::log::type::node, nano::log::detail::process_confirmed, nano::log::arg{ "block", block_l });
|
||||
|
||||
confirmation_height_processor.add (block_l);
|
||||
confirming_set.add (block_l->hash ());
|
||||
}
|
||||
else if (iteration_a < num_iters)
|
||||
{
|
||||
|
|
@ -1345,15 +1352,15 @@ nano::telemetry_data nano::node::local_telemetry () const
|
|||
{
|
||||
nano::telemetry_data telemetry_data;
|
||||
telemetry_data.node_id = node_id.pub;
|
||||
telemetry_data.block_count = ledger.cache.block_count;
|
||||
telemetry_data.cemented_count = ledger.cache.cemented_count;
|
||||
telemetry_data.block_count = ledger.block_count ();
|
||||
telemetry_data.cemented_count = ledger.cemented_count ();
|
||||
telemetry_data.bandwidth_cap = config.bandwidth_limit;
|
||||
telemetry_data.protocol_version = network_params.network.protocol_version;
|
||||
telemetry_data.uptime = std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now () - startup_time).count ();
|
||||
telemetry_data.unchecked_count = unchecked.count ();
|
||||
telemetry_data.genesis_block = network_params.ledger.genesis->hash ();
|
||||
telemetry_data.peer_count = nano::narrow_cast<decltype (telemetry_data.peer_count)> (network.size ());
|
||||
telemetry_data.account_count = ledger.cache.account_count;
|
||||
telemetry_data.account_count = ledger.account_count ();
|
||||
telemetry_data.major_version = nano::get_major_node_version ();
|
||||
telemetry_data.minor_version = nano::get_minor_node_version ();
|
||||
telemetry_data.patch_version = nano::get_patch_node_version ();
|
||||
|
|
@ -1406,7 +1413,7 @@ nano::node_wrapper::node_wrapper (std::filesystem::path const & path_a, std::fil
|
|||
auto & node_config = daemon_config.node;
|
||||
node_config.peering_port = 24000;
|
||||
|
||||
node = std::make_shared<nano::node> (*io_context, path_a, node_config, work, node_flags_a);
|
||||
node = std::make_shared<nano::node> (io_context, path_a, node_config, work, node_flags_a);
|
||||
}
|
||||
|
||||
nano::node_wrapper::~node_wrapper ()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
#include <nano/lib/stats.hpp>
|
||||
#include <nano/lib/thread_pool.hpp>
|
||||
#include <nano/lib/work.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/backlog_population.hpp>
|
||||
#include <nano/node/bandwidth_limiter.hpp>
|
||||
#include <nano/node/blockprocessor.hpp>
|
||||
|
|
@ -14,9 +13,7 @@
|
|||
#include <nano/node/bootstrap/bootstrap_attempt.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_server.hpp>
|
||||
#include <nano/node/bootstrap_ascending/service.hpp>
|
||||
#include <nano/node/confirmation_height_processor.hpp>
|
||||
#include <nano/node/distributed_work_factory.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/epoch_upgrader.hpp>
|
||||
#include <nano/node/local_block_broadcaster.hpp>
|
||||
#include <nano/node/network.hpp>
|
||||
|
|
@ -48,9 +45,8 @@
|
|||
|
||||
namespace nano
|
||||
{
|
||||
namespace rocksdb
|
||||
{
|
||||
} // Declare a namespace rocksdb inside nano so all references to the rocksdb library need to be globally scoped e.g. ::rocksdb::Slice
|
||||
class active_transactions;
|
||||
class confirming_set;
|
||||
class node;
|
||||
class work_pool;
|
||||
|
||||
|
|
@ -58,16 +54,26 @@ namespace scheduler
|
|||
{
|
||||
class component;
|
||||
}
|
||||
namespace transport
|
||||
{
|
||||
class tcp_listener;
|
||||
}
|
||||
namespace rocksdb
|
||||
{
|
||||
} // Declare a namespace rocksdb inside nano so all references to the rocksdb library need to be globally scoped e.g. ::rocksdb::Slice
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
// Configs
|
||||
backlog_population::config backlog_population_config (node_config const &);
|
||||
outbound_bandwidth_limiter::config outbound_bandwidth_limiter_config (node_config const &);
|
||||
|
||||
class node final : public std::enable_shared_from_this<nano::node>
|
||||
class node final : public std::enable_shared_from_this<node>
|
||||
{
|
||||
public:
|
||||
node (boost::asio::io_context &, uint16_t, std::filesystem::path const &, nano::work_pool &, nano::node_flags = nano::node_flags (), unsigned seq = 0);
|
||||
node (boost::asio::io_context &, std::filesystem::path const &, nano::node_config const &, nano::work_pool &, nano::node_flags = nano::node_flags (), unsigned seq = 0);
|
||||
node (std::shared_ptr<boost::asio::io_context>, uint16_t peering_port, std::filesystem::path const & application_path, nano::work_pool &, nano::node_flags = nano::node_flags (), unsigned seq = 0);
|
||||
node (std::shared_ptr<boost::asio::io_context>, std::filesystem::path const & application_path, nano::node_config const &, nano::work_pool &, nano::node_flags = nano::node_flags (), unsigned seq = 0);
|
||||
~node ();
|
||||
|
||||
public:
|
||||
|
|
@ -83,7 +89,6 @@ public:
|
|||
std::shared_ptr<nano::node> shared ();
|
||||
int store_version ();
|
||||
void receive_confirmed (store::transaction const & block_transaction_a, nano::block_hash const & hash_a, nano::account const & destination_a);
|
||||
void process_confirmed_data (store::transaction const &, std::shared_ptr<nano::block> const &, nano::block_hash const &, nano::account &, nano::uint128_t &, bool &, bool &, nano::account &);
|
||||
void process_confirmed (nano::election_status const &, uint64_t = 0);
|
||||
void process_active (std::shared_ptr<nano::block> const &);
|
||||
std::optional<nano::block_status> process_local (std::shared_ptr<nano::block> const &);
|
||||
|
|
@ -134,6 +139,7 @@ public:
|
|||
public:
|
||||
const nano::keypair node_id;
|
||||
nano::write_database_queue write_database_queue;
|
||||
std::shared_ptr<boost::asio::io_context> io_ctx_shared;
|
||||
boost::asio::io_context & io_ctx;
|
||||
boost::latch node_initialized_latch;
|
||||
nano::node_config config;
|
||||
|
|
@ -161,20 +167,25 @@ public:
|
|||
std::filesystem::path application_path;
|
||||
nano::node_observers observers;
|
||||
nano::port_mapping port_mapping;
|
||||
nano::block_processor block_processor;
|
||||
std::unique_ptr<nano::confirming_set> confirming_set_impl;
|
||||
nano::confirming_set & confirming_set;
|
||||
std::unique_ptr<nano::active_transactions> active_impl;
|
||||
nano::active_transactions & active;
|
||||
nano::online_reps online_reps;
|
||||
nano::rep_crawler rep_crawler;
|
||||
nano::rep_tiers rep_tiers;
|
||||
nano::vote_processor vote_processor;
|
||||
unsigned warmed_up;
|
||||
nano::block_processor block_processor;
|
||||
nano::local_vote_history history;
|
||||
std::unique_ptr<nano::local_vote_history> history_impl;
|
||||
nano::local_vote_history & history;
|
||||
nano::block_uniquer block_uniquer;
|
||||
nano::vote_uniquer vote_uniquer;
|
||||
nano::confirmation_height_processor confirmation_height_processor;
|
||||
nano::vote_cache vote_cache;
|
||||
nano::vote_generator generator;
|
||||
nano::vote_generator final_generator;
|
||||
nano::active_transactions active;
|
||||
std::unique_ptr<nano::vote_generator> generator_impl;
|
||||
nano::vote_generator & generator;
|
||||
std::unique_ptr<nano::vote_generator> final_generator_impl;
|
||||
nano::vote_generator & final_generator;
|
||||
|
||||
private: // Placed here to maintain initialization order
|
||||
std::unique_ptr<nano::scheduler::component> scheduler_impl;
|
||||
|
|
|
|||
|
|
@ -2,17 +2,26 @@
|
|||
|
||||
#include <nano/lib/numbers.hpp>
|
||||
#include <nano/lib/utility.hpp>
|
||||
#include <nano/node/active_transactions.hpp>
|
||||
#include <nano/node/transport/transport.hpp>
|
||||
#include <nano/node/vote_with_weight_info.hpp>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class election_status;
|
||||
class telemetry;
|
||||
}
|
||||
namespace nano::transport
|
||||
{
|
||||
class channel;
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class node_observers final
|
||||
{
|
||||
public:
|
||||
using blocks_t = nano::observer_set<nano::election_status const &, std::vector<nano::vote_with_weight_info> const &, nano::account const &, nano::uint128_t const &, bool, bool>;
|
||||
blocks_t blocks;
|
||||
blocks_t blocks; // Notification upon election completion or cancellation
|
||||
nano::observer_set<bool> wallet;
|
||||
nano::observer_set<std::shared_ptr<nano::vote>, std::shared_ptr<nano::transport::channel>, nano::vote_code> vote;
|
||||
nano::observer_set<nano::block_hash const &> active_started;
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const
|
|||
toml.put ("bootstrap_fraction_numerator", bootstrap_fraction_numerator, "Change bootstrap threshold (online stake / 256 * bootstrap_fraction_numerator).\ntype:uint32");
|
||||
toml.put ("receive_minimum", receive_minimum.to_string_dec (), "Minimum receive amount. Only affects node wallets. A large amount is recommended to avoid automatic work generation for tiny transactions.\ntype:string,amount,raw");
|
||||
toml.put ("online_weight_minimum", online_weight_minimum.to_string_dec (), "When calculating online weight, the node is forced to assume at least this much voting weight is online, thus setting a floor for voting weight to confirm transactions at online_weight_minimum * \"quorum delta\".\ntype:string,amount,raw");
|
||||
toml.put ("representative_vote_weight_minimum", representative_vote_weight_minimum.to_string_dec (), "Minimum vote weight that a representative must have for its vote to be counted.\nAll representatives above this weight will be kept in memory!\ntype:string,amount,raw");
|
||||
toml.put ("password_fanout", password_fanout, "Password fanout factor.\ntype:uint64");
|
||||
toml.put ("io_threads", io_threads, "Number of threads dedicated to I/O operations. Defaults to the number of CPU threads, and at least 4.\ntype:uint64");
|
||||
toml.put ("network_threads", network_threads, "Number of threads dedicated to processing network messages. Defaults to the number of CPU threads, and at least 4.\ntype:uint64");
|
||||
|
|
@ -126,11 +127,12 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const
|
|||
toml.put ("bootstrap_bandwidth_limit", bootstrap_bandwidth_limit, "Outbound bootstrap traffic limit in bytes/sec after which messages will be dropped.\nNote: changing to unlimited bandwidth (0) is not recommended for limited connections.\ntype:uint64");
|
||||
toml.put ("bootstrap_bandwidth_burst_ratio", bootstrap_bandwidth_burst_ratio, "Burst ratio for outbound bootstrap traffic.\ntype:double");
|
||||
|
||||
toml.put ("conf_height_processor_batch_min_time", conf_height_processor_batch_min_time.count (), "Minimum write batching time when there are blocks pending confirmation height.\ntype:milliseconds");
|
||||
toml.put ("confirming_set_batch_time", confirming_set_batch_time.count (), "Maximum time the confirming set will hold the database write transaction.\ntype:milliseconds");
|
||||
toml.put ("backup_before_upgrade", backup_before_upgrade, "Backup the ledger database before performing upgrades.\nWarning: uses more disk storage and increases startup time when upgrading.\ntype:bool");
|
||||
toml.put ("max_work_generate_multiplier", max_work_generate_multiplier, "Maximum allowed difficulty multiplier for work generation.\ntype:double,[1..]");
|
||||
toml.put ("frontiers_confirmation", serialize_frontiers_confirmation (frontiers_confirmation), "Mode controlling frontier confirmation rate.\ntype:string,{auto,always,disabled}");
|
||||
toml.put ("max_queued_requests", max_queued_requests, "Limit for number of queued confirmation requests for one channel, after which new requests are dropped until the queue drops below this value.\ntype:uint32");
|
||||
toml.put ("request_aggregator_threads", request_aggregator_threads, "Number of threads to dedicate to request aggregator. Defaults to using all cpu threads, up to a maximum of 4");
|
||||
toml.put ("max_unchecked_blocks", max_unchecked_blocks, "Maximum number of unchecked blocks to store in memory. Defaults to 65536. \ntype:uint64,[0..]");
|
||||
toml.put ("rep_crawler_weight_minimum", rep_crawler_weight_minimum.to_string_dec (), "Rep crawler minimum weight, if this is less than minimum principal weight then this is taken as the minimum weight a rep must have to be tracked. If you want to track all reps set this to 0. If you do not want this to influence anything then set it to max value. This is only useful for debugging or for people who really know what they are doing.\ntype:string,amount,raw");
|
||||
toml.put ("backlog_scan_batch_size", backlog_scan_batch_size, "Number of accounts per second to process when doing backlog population scan. Increasing this value will help unconfirmed frontiers get into election prioritization queue faster, however it will also increase resource usage. \ntype:uint");
|
||||
|
|
@ -350,6 +352,16 @@ nano::error nano::node_config::deserialize_toml (nano::tomlconfig & toml)
|
|||
toml.get_error ().set ("online_weight_minimum contains an invalid decimal amount");
|
||||
}
|
||||
|
||||
auto representative_vote_weight_minimum_l{ representative_vote_weight_minimum.to_string_dec () };
|
||||
if (toml.has_key ("representative_vote_weight_minimum"))
|
||||
{
|
||||
representative_vote_weight_minimum_l = toml.get<std::string> ("representative_vote_weight_minimum");
|
||||
}
|
||||
if (representative_vote_weight_minimum.decode_dec (representative_vote_weight_minimum_l))
|
||||
{
|
||||
toml.get_error ().set ("representative_vote_weight_minimum contains an invalid decimal amount");
|
||||
}
|
||||
|
||||
auto vote_minimum_l (vote_minimum.to_string_dec ());
|
||||
if (toml.has_key ("vote_minimum"))
|
||||
{
|
||||
|
|
@ -431,13 +443,14 @@ nano::error nano::node_config::deserialize_toml (nano::tomlconfig & toml)
|
|||
|
||||
toml.get<bool> ("backup_before_upgrade", backup_before_upgrade);
|
||||
|
||||
auto conf_height_processor_batch_min_time_l (conf_height_processor_batch_min_time.count ());
|
||||
toml.get ("conf_height_processor_batch_min_time", conf_height_processor_batch_min_time_l);
|
||||
conf_height_processor_batch_min_time = std::chrono::milliseconds (conf_height_processor_batch_min_time_l);
|
||||
auto confirming_set_batch_time_l (confirming_set_batch_time.count ());
|
||||
toml.get ("confirming_set_batch_time", confirming_set_batch_time_l);
|
||||
confirming_set_batch_time = std::chrono::milliseconds (confirming_set_batch_time_l);
|
||||
|
||||
toml.get<double> ("max_work_generate_multiplier", max_work_generate_multiplier);
|
||||
|
||||
toml.get<uint32_t> ("max_queued_requests", max_queued_requests);
|
||||
toml.get<uint32_t> ("request_aggregator_threads", request_aggregator_threads);
|
||||
|
||||
toml.get<unsigned> ("max_unchecked_blocks", max_unchecked_blocks);
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,11 @@ public:
|
|||
std::chrono::milliseconds vote_generator_delay{ std::chrono::milliseconds (100) };
|
||||
unsigned vote_generator_threshold{ 3 };
|
||||
nano::amount online_weight_minimum{ 60000 * nano::Gxrb_ratio };
|
||||
/*
|
||||
* The minimum vote weight that a representative must have for its vote to be counted.
|
||||
* All representatives above this weight will be kept in memory!
|
||||
*/
|
||||
nano::amount representative_vote_weight_minimum{ 10 * nano::Mxrb_ratio };
|
||||
unsigned password_fanout{ 1024 };
|
||||
unsigned io_threads{ std::max (4u, nano::hardware_concurrency ()) };
|
||||
unsigned network_threads{ std::max (4u, nano::hardware_concurrency ()) };
|
||||
|
|
@ -115,10 +120,11 @@ public:
|
|||
/** Bootstrap traffic does not need bursts */
|
||||
double bootstrap_bandwidth_burst_ratio{ 1. };
|
||||
nano::bootstrap_ascending_config bootstrap_ascending;
|
||||
std::chrono::milliseconds conf_height_processor_batch_min_time{ 50 };
|
||||
std::chrono::milliseconds confirming_set_batch_time{ 250 };
|
||||
bool backup_before_upgrade{ false };
|
||||
double max_work_generate_multiplier{ 64. };
|
||||
uint32_t max_queued_requests{ 512 };
|
||||
unsigned request_aggregator_threads{ std::min (nano::hardware_concurrency (), 4u) }; // Max 4 threads if available
|
||||
unsigned max_unchecked_blocks{ 65536 };
|
||||
std::chrono::seconds max_pruning_age{ !network_params.network.is_beta_network () ? std::chrono::seconds (24 * 60 * 60) : std::chrono::seconds (5 * 60) }; // 1 day; 5 minutes for beta network
|
||||
uint64_t max_pruning_depth{ 0 };
|
||||
|
|
@ -171,7 +177,6 @@ public:
|
|||
bool fast_bootstrap{ false };
|
||||
bool read_only{ false };
|
||||
bool disable_connection_cleanup{ false };
|
||||
nano::confirmation_height_mode confirmation_height_processor_mode{ nano::confirmation_height_mode::automatic };
|
||||
nano::generate_cache_flags generate_cache;
|
||||
bool inactive_node{ false };
|
||||
std::size_t block_processor_batch_size{ 0 };
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue