diff --git a/.ci/.env b/.ci/.env new file mode 100644 index 0000000..37598ca --- /dev/null +++ b/.ci/.env @@ -0,0 +1,41 @@ +GCC_VERSION=14 +LINUX_PACKAGES="make \ + autoconf \ + automake \ + build-essential \ + cargo \ + curl \ + git \ + golang-go \ + libtool \ + nasm \ + ninja-build \ + pkg-config \ + python3.12 \ + python3.12-dev \ + python3.12-venv \ + python3-pip \ + gcc-$GCC_VERSION \ + g++-$GCC_VERSION \ + unzip \ + zip \ + nano" + +MACOS_PACKAGES="make \ + autoconf \ + automake \ + rust \ + curl \ + git \ + go \ + libtool \ + nasm \ + ninja \ + pkg-config \ + python@3.12 \ + unzip \ + zip" + +CMAKE_VERSION=3.31.1 +DEBIAN_FRONTEND=noninteractive +#VCPKG_FORCE_SYSTEM_BINARIES=1 diff --git a/.ci/scripts/detect_os.sh b/.ci/scripts/detect_os.sh new file mode 100755 index 0000000..ac6e1dd --- /dev/null +++ b/.ci/scripts/detect_os.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -euo pipefail +# set -x + +detect_os() { + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + if command -v apt >/dev/null 2>&1; then + echo "linux_deb" + else + echo "linux_other" + fi + elif [[ "$OSTYPE" == "darwin"* ]]; then + echo "macos" + else + echo "unknown" + fi +} + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + echo "This script is intended to be sourced or used as a library." + echo "Exported functions: detect_os" + exit 1 +fi diff --git a/.ci/scripts/init.sh b/.ci/scripts/init.sh new file mode 100755 index 0000000..48a3ab5 --- /dev/null +++ b/.ci/scripts/init.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -euo pipefail +# set -x +trap 'echo "=== Error on line $LINENO"; exit 1' ERR + +SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" +OS_SELECT=$(source ${SCRIPT_DIR}/detect_os.sh && detect_os) + +set -o allexport && . ${SCRIPT_DIR}/../.env && set +o allexport + +main() { + case "$OS_SELECT" in + linux_deb) + echo "=== Detected Linux system with apt" + apt update && apt install -y $LINUX_PACKAGES + update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-$GCC_VERSION 90 + update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-$GCC_VERSION 90 + ;; + linux_other) + echo "=== Detected Linux system without apt" + echo "=== Support for other package managers is not added" + ;; + macos) + echo "=== Detected macOS system" + if command -v brew >/dev/null 2>&1; then + echo "=== Homebrew found. Installing packages..." + brew update && brew install $MACOS_PACKAGES + else + echo "=== Homebrew is not installed. Install it before proceeding: https://brew.sh" + fi + ;; + *) + echo "=== Unknown system" + ;; + esac +} + +main + +exit 0 diff --git a/.ci/scripts/init_py.sh b/.ci/scripts/init_py.sh new file mode 100755 index 0000000..7894fbc --- /dev/null +++ b/.ci/scripts/init_py.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -euo pipefail +# set -x + +init_py() { + echo "$VENV" + python3 -m venv "$VENV" + source $VENV/bin/activate + pip3 install cmake==${CMAKE_VERSION} + pip3 install --no-cache-dir asn1tools +} + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + echo "This script is intended to be sourced or used as a library." + echo "Exported functions: init_py" + exit 1 +fi diff --git a/.ci/scripts/init_vcpkg.sh b/.ci/scripts/init_vcpkg.sh new file mode 100755 index 0000000..6cbafbe --- /dev/null +++ b/.ci/scripts/init_vcpkg.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -euo pipefail +# set -x + +init_vcpkg() { + if [[ ! -d $VCPKG || -z "$(ls -A $VCPKG 2>/dev/null)" ]]; then + echo "Directory $VCPKG does not exist or is empty. Cloning vcpkg..." + git clone https://github.com/microsoft/vcpkg.git $VCPKG + fi + + if [[ ! -e $VCPKG/vcpkg ]]; then + echo "vcpkg executable not found. Bootstrapping vcpkg..." + $VCPKG/bootstrap-vcpkg.sh -disableMetrics + fi + + echo "vcpkg is initialized at $VCPKG." +} + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + echo "This script is intended to be sourced or used as a library." + echo "Exported functions: init_vcpkg" + exit 1 +fi diff --git a/.github/workflows/build_and_test.yaml b/.github/workflows/build_and_test.yaml new file mode 100644 index 0000000..5ebcc2f --- /dev/null +++ b/.github/workflows/build_and_test.yaml @@ -0,0 +1,85 @@ +name: Run Tests + +on: + push: + branches: + - master + pull_request: + workflow_dispatch: + inputs: + use_cache: + description: 'Use cache for build' + required: true + default: 'true' + type: 'choice' + options: + - 'true' + - 'false' + +env: + USE_CACHE: ${{ github.event.inputs.use_cache || 'true' }} + CACHE_VERSION: v02 + CACHE_PATH: | + ~/.hunter + ~/.cache/pip + ~/.cache/vcpkg + .vcpkg + .venv + .build + + +jobs: + build-and-test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-24.04, macos-15] + fail-fast: false + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + fetch-depth: 0 + + - name: "Restore cache dependencies" + id: cache-restore + if: ${{ env.USE_CACHE == 'true' }} + uses: actions/cache/restore@v4 + with: + path: ${{ env.CACHE_PATH }} + key: lean-${{ runner.os }}-${{ github.job }}-${{ env.CACHE_VERSION }}-${{ github.run_id }} + restore-keys: | + lean-${{ runner.os }}-${{ github.job }}-${{ env.CACHE_VERSION }} + lean-${{ runner.os }}-${{ github.job }} + lean-${{ runner.os }} + + - name: "Basic init" + run: | + if [[ $RUNNER_OS == "Linux" ]]; then + sudo ./.ci/scripts/init.sh + else + ./.ci/scripts/init.sh + fi + + - name: "Init all dependencies" + run: | + make init_py + make init_vcpkg + + - name: "Configure" + run: make configure + + - name: "Build" + run: make build + + - name: "Test" + run: make test + + - name: "Always Save Cache" + id: cache-save + if: always() && ( steps.cache-restore.outputs.cache-hit != 'true' ) + uses: actions/cache/save@v4 + with: + path: ${{ env.CACHE_PATH }} + key: ${{ steps.cache-restore.outputs.cache-primary-key }} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bd6526..56688c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.25) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) project(leanp2p) set(CMAKE_CXX_STANDARD 23) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c9a6828 --- /dev/null +++ b/Makefile @@ -0,0 +1,53 @@ +SHELL := /bin/bash +PROJECT := $(shell pwd) +CI_DIR := $(PROJECT)/.ci + +VENV ?= $(PROJECT)/.venv +BUILD ?= $(PROJECT)/.build +VCPKG ?= $(PROJECT)/.vcpkg +PATH = $(VENV)/bin:$(shell echo $$PATH) + +ifneq (,$(wildcard $(CI_DIR)/.env)) + include $(CI_DIR)/.env + export +endif + +OS_TYPE := $(shell bash -c 'source $(CI_DIR)/scripts/detect_os.sh && detect_os') + + +all: init_all configure build test + +init_all: init init_py init_vcpkg + +os: + @echo "=== Detected OS: $(OS_TYPE)" + +init: + @echo "=== Initializing..." + $(CI_DIR)/scripts/init.sh + +init_py: + @echo "=== Initializing Python..." + source $(CI_DIR)/scripts/init_py.sh && init_py + +init_vcpkg: + @echo "=== Initializing Vcpkg..." + source $(CI_DIR)/scripts/init_vcpkg.sh && init_vcpkg + +configure: + @echo "=== Configuring..." + VCPKG_ROOT=$(VCPKG) cmake --preset=default -DPython3_EXECUTABLE="$(VENV)/bin/python3" -B $(BUILD) $(PROJECT) + +build: + @echo "=== Building..." + cmake --build $(BUILD) + +test: + @echo "=== Testing..." + ctest --test-dir $(BUILD) + +clean_all: + @echo "=== Cleaning..." + rm -rf $(VENV) $(BUILD) $(VCPKG) + +.PHONY: all init_all os init init_py init_vcpkg configure build test clean_all diff --git a/include/libp2p/coro/channel.hpp b/include/libp2p/coro/channel.hpp index ba30047..d6502dc 100644 --- a/include/libp2p/coro/channel.hpp +++ b/include/libp2p/coro/channel.hpp @@ -15,8 +15,8 @@ namespace libp2p { class CoroOutcomeChannel { public: CoroOutcome receive() { - co_return (co_await channel_.async_receive(boost::asio::use_awaitable)) - .value(); + auto result = co_await channel_.async_receive(boost::asio::use_awaitable); + co_return result.value(); } void send(outcome::result result) { diff --git a/include/libp2p/transport/tcp/tcp_util.hpp b/include/libp2p/transport/tcp/tcp_util.hpp index 027ff64..6fa6c9f 100644 --- a/include/libp2p/transport/tcp/tcp_util.hpp +++ b/include/libp2p/transport/tcp/tcp_util.hpp @@ -67,6 +67,7 @@ namespace libp2p::transport::detail { return dns; } default: + break; } return std::errc::protocol_not_supported; } diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..69a672e --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,105 @@ +# Python Version Check Script + +This script checks for Python version 3.12 or higher and offers installation if requirements are not met. + +## Supported Operating Systems + +- **macOS** - uses Homebrew +- **Ubuntu/Debian** - uses apt with deadsnakes PPA +- **Arch Linux** - uses pacman + +## Usage + +1. Make the script executable: + ```bash + chmod +x scripts/check_python.sh + ``` + +2. Run the script: + ```bash + ./scripts/check_python.sh + ``` + +## What the Script Does + +1. **Python Version Check**: Searches for available Python commands and checks their versions +2. **OS Detection**: Automatically detects the operating system +3. **Installation Offer**: If a suitable version is not found, offers to install +4. **Installation**: Uses the appropriate package manager for installation +5. **Result Verification**: Ensures the installation was successful + +## Example Output + +### Successful Check +``` +[INFO] Checking Python version requirements (>= 3.12) +Found python3.12 version: 3.12.1 +[SUCCESS] Python requirement satisfied with python3.12 +[SUCCESS] Python version check passed! +``` + +### Installation on Ubuntu +``` +[INFO] Checking Python version requirements (>= 3.12) +Found python3 version: 3.10.6 +[WARNING] Python 3.12+ not found or version is too old +[INFO] Detected OS: ubuntu +[WARNING] Do you want to install/update Python? (y/N) +y +[INFO] Installing Python on Ubuntu... +[INFO] Updating package list... +[INFO] Adding deadsnakes PPA... +[INFO] Installing Python 3.12... +[INFO] Verifying installation... +Found python3.12 version: 3.12.7 +[SUCCESS] Installation successful! Python requirement satisfied with python3.12 +``` + +## Requirements + +### macOS +- Homebrew must be installed +- If Homebrew is not installed, the script will provide installation instructions + +### Ubuntu/Debian +- Sudo privileges for package installation +- Internet access to add PPA + +### Arch Linux +- Sudo privileges to use pacman +- Internet access for package updates + +## Security + +The script: +- Always asks for permission before installation +- Uses official package repositories +- Does not modify system files without permission +- Can be safely interrupted at any time (Ctrl+C) + +## Troubleshooting + +### Script cannot find Python after installation +Restart your terminal or run: +```bash +source ~/.bashrc # for bash +source ~/.zshrc # for zsh +``` + +### Permission errors +Make sure you have sudo privileges and your user can install packages. + +### Homebrew not found (macOS) +Install Homebrew: +```bash +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +``` + +## Script Features + +- **Multi-platform support**: Works on macOS, Ubuntu/Debian, and Arch Linux +- **Smart detection**: Checks multiple Python command variants (python3.12, python3.13, python3, python) +- **Safe installation**: Always prompts before making changes +- **Colored output**: Easy-to-read status messages +- **Error handling**: Graceful handling of edge cases and errors +- **Verification**: Post-installation verification to ensure success \ No newline at end of file diff --git a/scripts/check_python.sh b/scripts/check_python.sh new file mode 100755 index 0000000..35f91e7 --- /dev/null +++ b/scripts/check_python.sh @@ -0,0 +1,209 @@ +#!/bin/bash + +# Script to check and install Python 3.12 or higher +# Supports: macOS, Ubuntu, Arch Linux + +set -e + +REQUIRED_MAJOR=3 +REQUIRED_MINOR=12 + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +print_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +print_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Function to check Python version +check_python_version() { + local python_cmd=$1 + + if ! command -v "$python_cmd" &> /dev/null; then + return 1 + fi + + local version_output + version_output=$($python_cmd --version 2>&1) + + # Extract version numbers + local version + version=$(echo "$version_output" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) + + if [[ -z "$version" ]]; then + return 1 + fi + + local major minor patch + IFS='.' read -r major minor patch <<< "$version" + + echo "Found $python_cmd version: $version" + + if [[ $major -gt $REQUIRED_MAJOR ]] || + [[ $major -eq $REQUIRED_MAJOR && $minor -ge $REQUIRED_MINOR ]]; then + return 0 + else + return 1 + fi +} + +# Function to detect OS +detect_os() { + if [[ "$OSTYPE" == "darwin"* ]]; then + echo "macos" + elif [[ -f /etc/arch-release ]]; then + echo "arch" + elif [[ -f /etc/debian_version ]] || [[ -f /etc/ubuntu-release ]]; then + echo "ubuntu" + else + echo "unknown" + fi +} + +# Function to install Python on macOS +install_python_macos() { + print_info "Installing Python on macOS..." + + if ! command -v brew &> /dev/null; then + print_error "Homebrew is not installed. Please install Homebrew first:" + echo " /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"" + exit 1 + fi + + print_info "Updating Homebrew..." + brew update + + print_info "Installing Python 3.12..." + brew install python@3.12 + + # Create symlinks if needed + if [[ ! -L /usr/local/bin/python3.12 ]]; then + brew link python@3.12 + fi +} + +# Function to install Python on Ubuntu +install_python_ubuntu() { + print_info "Installing Python on Ubuntu..." + + print_info "Updating package list..." + sudo apt update + + # Add deadsnakes PPA for newer Python versions + print_info "Adding deadsnakes PPA..." + sudo apt install -y software-properties-common + sudo add-apt-repository -y ppa:deadsnakes/ppa + sudo apt update + + print_info "Installing Python 3.12..." + sudo apt install -y python3.12 python3.12-venv python3.12-pip python3.12-dev + + # Set up alternatives + sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.12 1 +} + +# Function to install Python on Arch Linux +install_python_arch() { + print_info "Installing Python on Arch Linux..." + + print_info "Updating package database..." + sudo pacman -Sy + + print_info "Installing Python..." + sudo pacman -S --noconfirm python python-pip +} + +# Main function +main() { + print_info "Checking Python version requirements (>= ${REQUIRED_MAJOR}.${REQUIRED_MINOR})" + + # Check various Python commands + local python_found=false + local python_commands=("python3.12" "python3.13" "python3.14" "python3" "python") + + for cmd in "${python_commands[@]}"; do + if check_python_version "$cmd"; then + print_success "Python requirement satisfied with $cmd" + python_found=true + break + fi + done + + if [[ "$python_found" == "true" ]]; then + print_success "Python version check passed!" + exit 0 + fi + + print_warning "Python ${REQUIRED_MAJOR}.${REQUIRED_MINOR}+ not found or version is too old" + + # Detect OS + local os + os=$(detect_os) + print_info "Detected OS: $os" + + # Ask user for permission to install + echo + print_warning "Do you want to install/update Python? (y/N)" + read -r response + + if [[ ! "$response" =~ ^[Yy]$ ]]; then + print_info "Installation cancelled by user" + exit 0 + fi + + # Install based on OS + case $os in + "macos") + install_python_macos + ;; + "ubuntu") + install_python_ubuntu + ;; + "arch") + install_python_arch + ;; + *) + print_error "Unsupported operating system: $os" + print_info "Please install Python ${REQUIRED_MAJOR}.${REQUIRED_MINOR}+ manually" + exit 1 + ;; + esac + + # Verify installation + print_info "Verifying installation..." + sleep 2 + + python_found=false + for cmd in "${python_commands[@]}"; do + if check_python_version "$cmd"; then + print_success "Installation successful! Python requirement satisfied with $cmd" + python_found=true + break + fi + done + + if [[ "$python_found" == "false" ]]; then + print_error "Installation completed but Python ${REQUIRED_MAJOR}.${REQUIRED_MINOR}+ still not found" + print_info "You may need to restart your terminal or add Python to your PATH" + exit 1 + fi +} + +# Run main function +main "$@" \ No newline at end of file diff --git a/scripts/get_version.sh b/scripts/get_version.sh new file mode 100755 index 0000000..b7d7e35 --- /dev/null +++ b/scripts/get_version.sh @@ -0,0 +1,96 @@ +#!/bin/sh -eu +# +# Copyright Quadrivium LLC +# All Rights Reserved +# SPDX-License-Identifier: Apache-2.0 +# +# +# Version String Generator for Git Repositories +# +# This script generates a version string based on the current Git state. +# It retrieves the latest tag, calculates the number of commits since the last tag, +# includes the current branch (if different from master), and appends the commit hash. +# If the working directory has uncommitted changes, it marks the version as "dirty". +# +# Usage: +# ./get_version.sh [--sanitized] +# +# Options: +# --sanitized Replaces non-alphanumeric characters in the version string +# with hyphens for compatibility with package managers. +# +# Output: +# Prints a version string in the format: +# ---- +# If the repository is dirty, "-dirty" is appended. +# +# Example: +# v1.2.3-5-feature-branch-2-a1b2c3d-dirty +# +# Requirements: +# - Git +# - sed (for --sanitized mode) +# +# Notes: +# - If the repository has no tags, an initial tag "_init" is created. +# - If the repository is not a Git repo, outputs "Unknown(no git)". +# - If Git is not installed, it exits with an error. +# + +sanitize_version() { + echo "$1" | sed -E 's/[^a-zA-Z0-9.+~:-]/-/g' +} + +realpath() { + case "$1" in + /*) echo "$1" ;; + *) echo "$(pwd)/$1" ;; + esac +} + +cd "$(dirname "$(realpath "$0")")" + +SANITIZED=false +[ "$#" -gt 0 ] && [ "$1" = "--sanitized" ] && SANITIZED=true + +if [ -x "$(command -v git)" ] && [ -d "$(git rev-parse --git-dir 2>/dev/null)" ]; then + HEAD=$(git rev-parse --short HEAD) + + # Determine the main branch (fallback to default names if necessary) + MAIN_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@') + if [ -z "$MAIN_BRANCH" ]; then + MAIN_BRANCH=$(git branch --format='%(refname:short)' | grep -E '^(main|master)$' | head -n1) + fi + if [ -z "$MAIN_BRANCH" ]; then + MAIN_BRANCH="master" # Fallback to "master" if no main branch detected + fi + + COMMON=$(git merge-base HEAD "$MAIN_BRANCH" 2>/dev/null || echo "$HEAD") + + if ! git tag | grep -q .; then + ROOT_COMMIT=$(git rev-list --max-parents=0 HEAD || echo "") + [ -n "$ROOT_COMMIT" ] && ! git tag | grep -q "_init" && git tag _init "$ROOT_COMMIT" + fi + + DESCR=$(git describe --tags --long "$COMMON" 2>/dev/null || echo "") + [ -z "$DESCR" ] && DESCR="$HEAD-0-g$HEAD" + + TAG_IN_MASTER=$(echo "$DESCR" | sed -E "s/v?(.*)-([0-9]+)-g[a-f0-9]+/\1/") + TAG_TO_FORK_DISTANCE=$(echo "$DESCR" | sed -E "s/v?(.*)-([0-9]+)-g[a-f0-9]+/\2/") + + BRANCH=$(git branch --show-current 2>/dev/null || echo "$HEAD") + FORK_TO_HEAD_DISTANCE=$(git rev-list --count "$COMMON..HEAD" 2>/dev/null || echo "0") + + RESULT=$TAG_IN_MASTER + [ "$TAG_TO_FORK_DISTANCE" != "0" ] && RESULT="$RESULT-$TAG_TO_FORK_DISTANCE" + [ "$BRANCH" != "$MAIN_BRANCH" ] && RESULT="$RESULT-$BRANCH-$FORK_TO_HEAD_DISTANCE-$HEAD" + + git diff --quiet || DIRTY="-dirty" + RESULT="$RESULT${DIRTY:-}" +else + RESULT="Unknown(no git)" +fi + +[ "$SANITIZED" = true ] && RESULT=$(sanitize_version "$RESULT") + +printf "%s" "$RESULT" diff --git a/src/multi/content_identifier_codec.cpp b/src/multi/content_identifier_codec.cpp index 8680436..89915a7 100644 --- a/src/multi/content_identifier_codec.cpp +++ b/src/multi/content_identifier_codec.cpp @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -55,13 +56,11 @@ namespace libp2p::multi { outcome::result> ContentIdentifierCodec::encode( const ContentIdentifier &cid) { - std::vector bytes; + qtils::ByteVec bytes; if (cid.version == ContentIdentifier::Version::V1) { - UVarint version(static_cast(cid.version)); - bytes.append_range(version.toBytes()); - UVarint type(static_cast(cid.content_type)); - bytes.append_range(type.toBytes()); - bytes.append_range(cid.content_address.toBuffer()); + bytes.put(EncodeVarint{static_cast(cid.version)}); + bytes.put(EncodeVarint{static_cast(cid.content_type)}); + bytes.put(cid.content_address.toBuffer()); } else if (cid.version == ContentIdentifier::Version::V0) { if (cid.content_type != MulticodecType::Code::DAG_PB) { return EncodeError::INVALID_CONTENT_TYPE; diff --git a/src/protocol/gossip/gossip.cpp b/src/protocol/gossip/gossip.cpp index 454bff3..cfcf977 100644 --- a/src/protocol/gossip/gossip.cpp +++ b/src/protocol/gossip/gossip.cpp @@ -126,7 +126,7 @@ namespace libp2p::protocol::gossip { if (i >= slots) { break; } - result.append_range(slot); + result.insert(result.end(), slot.begin(), slot.end()); ++i; } return result; @@ -898,7 +898,9 @@ namespace libp2p::protocol::gossip { } else if (topic->mesh_peers_.size() > config_.mesh_n_high_for_topic(topic_hash)) { std::vector shuffled; - shuffled.append_range(topic->mesh_peers_); + shuffled.insert(shuffled.end(), + topic->mesh_peers_.begin(), + topic->mesh_peers_.end()); choose_peers_.shuffle(shuffled); std::ranges::sort(shuffled, [&](const PeerPtr &l, const PeerPtr &r) { return score_.score(r->peer_id_) < score_.score(l->peer_id_);