Merge branch 'develop' into fair-queuing-3-wip

# Conflicts:
#	nano/lib/stats_enums.hpp
#	nano/node/CMakeLists.txt
This commit is contained in:
Piotr Wójcik 2024-04-03 22:34:58 +02:00
commit 852347e723
205 changed files with 6445 additions and 8159 deletions

View file

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

View file

@ -97,7 +97,6 @@ set_property(
block_arrival
block_processor
block_uniquer
confirmation_height_processor
dropped_elections,
election_winner_details
gap_cache

View file

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

View file

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

View file

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

View file

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

View file

@ -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 ())

View file

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

View file

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

View file

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

View 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 ());
}

View file

@ -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 ()

View file

@ -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 ()));
}

View file

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

View 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 ()), "");
}
}

View file

@ -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 ());
}

View file

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

View file

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

View file

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

View file

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

View file

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

View 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 ());
}

View file

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

View file

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

View file

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

View file

@ -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 ();
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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");
}
}

View file

@ -23,7 +23,6 @@ enum class mutexes
block_processor,
block_uniquer,
blockstore_cache,
confirmation_height_processor,
election_winner_details,
gap_cache,
network_filter,

View file

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

View file

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

View file

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

View file

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

View file

@ -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 ();
}
}

View file

@ -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
View 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 () };
};
}

View file

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

View file

@ -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 &);
}

View file

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

View file

@ -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");
}

View file

@ -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
*/

View file

@ -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 ()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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)
{

View file

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

View file

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

View file

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

View file

@ -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 },

View file

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

View file

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

View file

@ -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 ()

View file

@ -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)
{

View file

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

View file

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

View file

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

View file

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

View file

@ -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 ();
}

View file

@ -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 &);
}

View file

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

View file

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

View 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;
}

View 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;
};
}

View file

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

View file

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

View 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,
};
}

View 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 };
};
}

View file

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

View file

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

View file

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

View file

@ -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 ();
}

View file

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

View file

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

View file

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

View file

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

View 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;
}

View 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;
};
}

View file

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

View file

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

View file

@ -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 ())

View file

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

View file

@ -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 ()

View file

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

View file

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

View file

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

View file

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