"copy-pasting only 500 lines of code"

This commit is contained in:
preland 2024-03-24 13:34:13 -05:00
parent 83e920a5b7
commit f67b0578a9
66 changed files with 15787 additions and 56 deletions

5
.gitignore vendored
View File

@ -70,6 +70,11 @@ cmake_install.cmake
install_manifest.txt install_manifest.txt
cmake-build-debug/ cmake-build-debug/
### Clangd ###
.cache/
.clangd
compile_commands.json
### Linux ### ### Linux ###
*~ *~

View File

@ -2,31 +2,33 @@
# #
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, are # Redistribution and use in source and binary forms, with or without
# permitted provided that the following conditions are met: # modification, are permitted provided that the following conditions are met:
# #
# 1. Redistributions of source code must retain the above copyright notice, this list of # 1. Redistributions of source code must retain the above copyright notice, this
# conditions and the following disclaimer. # list of conditions and the following disclaimer.
# #
# 2. Redistributions in binary form must reproduce the above copyright notice, this list # 1. Redistributions in binary form must reproduce the above copyright notice,
# of conditions and the following disclaimer in the documentation and/or other # this list of conditions and the following disclaimer in the documentation
# materials provided with the distribution. # and/or other materials provided with the distribution.
# #
# 3. Neither the name of the copyright holder nor the names of its contributors may be # 1. Neither the name of the copyright holder nor the names of its contributors
# used to endorse or promote products derived from this software without specific # may be used to endorse or promote products derived from this software
# prior written permission. # without specific prior written permission.
# #
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# #
# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers # Parts of this file are originally copyright (c) 2012-2013 The Cryptonote
# developers
if(WIN32 OR STATIC) if(WIN32 OR STATIC)
add_definitions(-DSTATICLIB) add_definitions(-DSTATICLIB)
@ -35,9 +37,7 @@ if (WIN32 OR STATIC)
endif() endif()
function(monero_private_headers group) function(monero_private_headers group)
source_group("${group}\\Private" source_group("${group}\\Private" FILES ${ARGN})
FILES
${ARGN})
endfunction() endfunction()
function(monero_install_headers subdir) function(monero_install_headers subdir)
@ -49,31 +49,27 @@ endfunction ()
function(enable_stack_trace target) function(enable_stack_trace target)
if(STACK_TRACE) if(STACK_TRACE)
set_property(TARGET ${target} set_property(
APPEND PROPERTY COMPILE_DEFINITIONS "STACK_TRACE") TARGET ${target}
APPEND
PROPERTY COMPILE_DEFINITIONS "STACK_TRACE")
if(STATIC) if(STATIC)
set_property(TARGET "${target}" set_property(
APPEND PROPERTY LINK_FLAGS "-Wl,--wrap=__cxa_throw") TARGET "${target}"
APPEND
PROPERTY LINK_FLAGS "-Wl,--wrap=__cxa_throw")
endif() endif()
endif() endif()
endfunction() endfunction()
function(monero_add_executable name) function(monero_add_executable name)
source_group("${name}" source_group("${name}" FILES ${ARGN})
FILES
${ARGN})
add_executable("${name}" add_executable("${name}" ${ARGN})
${ARGN}) target_link_libraries("${name}" PRIVATE ${EXTRA_LIBRARIES})
target_link_libraries("${name}" set_property(TARGET "${name}" PROPERTY FOLDER "prog")
PRIVATE set_property(TARGET "${name}" PROPERTY RUNTIME_OUTPUT_DIRECTORY
${EXTRA_LIBRARIES}) "${CMAKE_BINARY_DIR}/bin")
set_property(TARGET "${name}"
PROPERTY
FOLDER "prog")
set_property(TARGET "${name}"
PROPERTY
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
enable_stack_trace("${name}") enable_stack_trace("${name}")
monero_set_target_no_relink("${name}") monero_set_target_no_relink("${name}")
@ -81,7 +77,8 @@ function (monero_add_executable name)
endfunction() endfunction()
include(Version) include(Version)
monero_add_library(version SOURCES ${CMAKE_BINARY_DIR}/version.cpp DEPENDS genversion) monero_add_library(version SOURCES ${CMAKE_BINARY_DIR}/version.cpp DEPENDS
genversion)
add_subdirectory(common) add_subdirectory(common)
add_subdirectory(crypto) add_subdirectory(crypto)
@ -92,6 +89,7 @@ add_subdirectory(cryptonote_core)
add_subdirectory(lmdb) add_subdirectory(lmdb)
add_subdirectory(multisig) add_subdirectory(multisig)
add_subdirectory(net) add_subdirectory(net)
add_subdirectory(i2p)
add_subdirectory(hardforks) add_subdirectory(hardforks)
add_subdirectory(blockchain_db) add_subdirectory(blockchain_db)
add_subdirectory(mnemonics) add_subdirectory(mnemonics)

282
src/i2p/.logging.h Normal file
View File

@ -0,0 +1,282 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_LOGGING_H
#define BITCOIN_LOGGING_H
#include "threadsafety.h"
#include "tinyformat.h"
#include "util/fs.h"
#include "util/string.h"
#include <atomic>
#include <cstdint>
#include <functional>
#include <list>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
static const bool DEFAULT_LOGTIMEMICROS = false;
static const bool DEFAULT_LOGIPS = false;
static const bool DEFAULT_LOGTIMESTAMPS = true;
static const bool DEFAULT_LOGTHREADNAMES = false;
static const bool DEFAULT_LOGSOURCELOCATIONS = false;
static constexpr bool DEFAULT_LOGLEVELALWAYS = false;
extern const char *const DEFAULT_DEBUGLOGFILE;
extern bool fLogIPs;
struct LogCategory {
std::string category;
bool active;
};
namespace BCLog {
enum LogFlags : uint32_t {
NONE = 0,
NET = (1 << 0),
TOR = (1 << 1),
MEMPOOL = (1 << 2),
HTTP = (1 << 3),
BENCH = (1 << 4),
ZMQ = (1 << 5),
WALLETDB = (1 << 6),
RPC = (1 << 7),
ESTIMATEFEE = (1 << 8),
ADDRMAN = (1 << 9),
SELECTCOINS = (1 << 10),
REINDEX = (1 << 11),
CMPCTBLOCK = (1 << 12),
RAND = (1 << 13),
PRUNE = (1 << 14),
PROXY = (1 << 15),
MEMPOOLREJ = (1 << 16),
LIBEVENT = (1 << 17),
COINDB = (1 << 18),
QT = (1 << 19),
LEVELDB = (1 << 20),
VALIDATION = (1 << 21),
I2P = (1 << 22),
IPC = (1 << 23),
#ifdef DEBUG_LOCKCONTENTION
LOCK = (1 << 24),
#endif
UTIL = (1 << 25),
BLOCKSTORAGE = (1 << 26),
TXRECONCILIATION = (1 << 27),
SCAN = (1 << 28),
TXPACKAGES = (1 << 29),
ALL = ~(uint32_t)0,
};
enum class Level {
Trace = 0, // High-volume or detailed logging for development/debugging
Debug, // Reasonably noisy logging, but still usable in production
Info, // Default
Warning,
Error,
};
constexpr auto DEFAULT_LOG_LEVEL{Level::Debug};
class Logger {
private:
mutable StdMutex
m_cs; // Can not use Mutex from sync.h because in debug mode it would
// cause a deadlock when a potential deadlock was detected
FILE *m_fileout GUARDED_BY(m_cs) = nullptr;
std::list<std::string> m_msgs_before_open GUARDED_BY(m_cs);
bool m_buffering GUARDED_BY(m_cs) =
true; //!< Buffer messages before logging can be started.
/**
* m_started_new_line is a state variable that will suppress printing of
* the timestamp when multiple calls are made that don't end in a
* newline.
*/
std::atomic_bool m_started_new_line{true};
//! Category-specific log level. Overrides `m_log_level`.
std::unordered_map<LogFlags, Level> m_category_log_levels GUARDED_BY(m_cs);
//! If there is no category-specific log level, all logs with a severity
//! level lower than `m_log_level` will be ignored.
std::atomic<Level> m_log_level{DEFAULT_LOG_LEVEL};
/** Log categories bitfield. */
std::atomic<uint32_t> m_categories{0};
std::string LogTimestampStr(const std::string &str);
/** Slots that connect to the print signal */
std::list<std::function<void(const std::string &)>>
m_print_callbacks GUARDED_BY(m_cs){};
public:
bool m_print_to_console = false;
bool m_print_to_file = false;
bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS;
bool m_log_time_micros = DEFAULT_LOGTIMEMICROS;
bool m_log_threadnames = DEFAULT_LOGTHREADNAMES;
bool m_log_sourcelocations = DEFAULT_LOGSOURCELOCATIONS;
bool m_always_print_category_level = DEFAULT_LOGLEVELALWAYS;
fs::path m_file_path;
std::atomic<bool> m_reopen_file{false};
std::string GetLogPrefix(LogFlags category, Level level) const;
/** Send a string to the log output */
void LogPrintStr(const std::string &str, const std::string &logging_function,
const std::string &source_file, int source_line,
BCLog::LogFlags category, BCLog::Level level);
/** Returns whether logs will be written to any output */
bool Enabled() const {
StdLockGuard scoped_lock(m_cs);
return m_buffering || m_print_to_console || m_print_to_file ||
!m_print_callbacks.empty();
}
/** Connect a slot to the print signal and return the connection */
std::list<std::function<void(const std::string &)>>::iterator
PushBackCallback(std::function<void(const std::string &)> fun) {
StdLockGuard scoped_lock(m_cs);
m_print_callbacks.push_back(std::move(fun));
return --m_print_callbacks.end();
}
/** Delete a connection */
void DeleteCallback(
std::list<std::function<void(const std::string &)>>::iterator it) {
StdLockGuard scoped_lock(m_cs);
m_print_callbacks.erase(it);
}
/** Start logging (and flush all buffered messages) */
bool StartLogging();
/** Only for testing */
void DisconnectTestLogger();
void ShrinkDebugFile();
std::unordered_map<LogFlags, Level> CategoryLevels() const {
StdLockGuard scoped_lock(m_cs);
return m_category_log_levels;
}
void SetCategoryLogLevel(const std::unordered_map<LogFlags, Level> &levels) {
StdLockGuard scoped_lock(m_cs);
m_category_log_levels = levels;
}
bool SetCategoryLogLevel(const std::string &category_str,
const std::string &level_str);
Level LogLevel() const { return m_log_level.load(); }
void SetLogLevel(Level level) { m_log_level = level; }
bool SetLogLevel(const std::string &level);
uint32_t GetCategoryMask() const { return m_categories.load(); }
void EnableCategory(LogFlags flag);
bool EnableCategory(const std::string &str);
void DisableCategory(LogFlags flag);
bool DisableCategory(const std::string &str);
bool WillLogCategory(LogFlags category) const;
bool WillLogCategoryLevel(LogFlags category, Level level) const;
/** Returns a vector of the log categories in alphabetical order. */
std::vector<LogCategory> LogCategoriesList() const;
/** Returns a string with the log categories in alphabetical order. */
std::string LogCategoriesString() const {
return Join(LogCategoriesList(), ", ",
[&](const LogCategory &i) { return i.category; });
};
//! Returns a string with all user-selectable log levels.
std::string LogLevelsString() const;
//! Returns the string representation of a log level.
static std::string LogLevelToStr(BCLog::Level level);
bool DefaultShrinkDebugFile() const;
};
} // namespace BCLog
BCLog::Logger &LogInstance();
/** Return true if log accepts specified category, at the specified level. */
static inline bool LogAcceptCategory(BCLog::LogFlags category,
BCLog::Level level) {
return LogInstance().WillLogCategoryLevel(category, level);
}
/** Return true if str parses as a log category and set the flag */
bool GetLogCategory(BCLog::LogFlags &flag, const std::string &str);
// Be conservative when using functions that
// unconditionally log to debug.log! It should not be the case that an inbound
// peer can fill up a user's disk with debug.log entries.
template <typename... Args>
static inline void
LogPrintf_(const std::string &logging_function, const std::string &source_file,
const int source_line, const BCLog::LogFlags flag,
const BCLog::Level level, const char *fmt, const Args &...args) {
if (LogInstance().Enabled()) {
std::string log_msg;
try {
log_msg = tfm::format(fmt, args...);
} catch (tinyformat::format_error &fmterr) {
/* Original format string will have newline so don't add one here */
log_msg = "Error \"" + std::string(fmterr.what()) +
"\" while formatting log message: " + fmt;
}
LogInstance().LogPrintStr(log_msg, logging_function, source_file,
source_line, flag, level);
}
}
#define LogPrintLevel_(category, level, ...) \
LogPrintf_(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
// Log unconditionally.
#define LogInfo(...) \
LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, __VA_ARGS__)
#define LogWarning(...) \
LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Warning, __VA_ARGS__)
#define LogError(...) \
LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Error, __VA_ARGS__)
// Deprecated unconditional logging.
#define LogPrintf(...) LogInfo(__VA_ARGS__)
#define LogPrintfCategory(category, ...) \
LogPrintLevel_(category, BCLog::Level::Info, __VA_ARGS__)
// Use a macro instead of a function for conditional logging to prevent
// evaluating arguments when logging for the category is not enabled.
// Log conditionally, prefixing the output with the passed category name and
// severity level.
#define LogPrintLevel(category, level, ...) \
do { \
if (LogAcceptCategory((category), (level))) { \
LogPrintLevel_(category, level, __VA_ARGS__); \
} \
} while (0)
// Log conditionally, prefixing the output with the passed category name.
#define LogDebug(category, ...) \
LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__)
#define LogTrace(category, ...) \
LogPrintLevel(category, BCLog::Level::Trace, __VA_ARGS__)
// Deprecated conditional logging
#define LogPrint(category, ...) LogDebug(category, __VA_ARGS__)
#endif // BITCOIN_LOGGING_H

45
src/i2p/CMakeLists.txt Normal file
View File

@ -0,0 +1,45 @@
# conditions and the following disclaimer.
#
# 1. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 1. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(CMAKE_CXX_STANDARD 20)
set(i2p_sources i2p.cpp)
monero_find_all_headers(i2p_headers "${CMAKE_CURRENT_SOURCE_DIR}")
monero_add_library(i2p ${i2p_sources} ${i2p_headers})
# target_include_directories(i2p PUBLIC
# ${CMAKE_CURRENT_SOURCE_DIR}/contrib/epee/include )
target_link_libraries(
i2p
common
epee
${I2P_LIB}
${EPEE_READLINE}
${Boost_CHRONO_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_REGEX_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${ZMQ_LIB}
${GNU_READLINE_LIBRARY}
${EXTRA_LIBRARIES}
${Blocks})

27
src/i2p/attributes.h Normal file
View File

@ -0,0 +1,27 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_ATTRIBUTES_H
#define BITCOIN_ATTRIBUTES_H
#if defined(__clang__)
# if __has_attribute(lifetimebound)
# define LIFETIMEBOUND [[clang::lifetimebound]]
# else
# define LIFETIMEBOUND
# endif
#else
# define LIFETIMEBOUND
#endif
#if defined(__GNUC__)
# define ALWAYS_INLINE inline __attribute__((always_inline))
#elif defined(_MSC_VER)
# define ALWAYS_INLINE __forceinline
#else
# error No known always_inline attribute for this platform.
#endif
#endif // BITCOIN_ATTRIBUTES_H

31
src/i2p/chainparams.h Normal file
View File

@ -0,0 +1,31 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CHAINPARAMS_H
#define BITCOIN_CHAINPARAMS_H
#include "kernel/chainparams.h" // IWYU pragma: export
#include <memory>
class ArgsManager;
/**
* Creates and returns a std::unique_ptr<CChainParams> of the chosen chain.
*/
std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager& args, const ChainType chain);
/**
* Return the currently selected parameters. This won't change after app
* startup, except for unit tests.
*/
const CChainParams &Params();
/**
* Sets the params returned by Params() to those for the given chain type.
*/
void SelectParams(const ChainType chain);
#endif // BITCOIN_CHAINPARAMS_H

510
src/i2p/common/args.h Normal file
View File

@ -0,0 +1,510 @@
// Copyright (c) 2023 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_COMMON_ARGS_H
#define BITCOIN_COMMON_ARGS_H
#include "../common/settings.h"
#include "../compat/compat.h"
#include "../sync.h"
#include "../util/chaintype.h"
#include "../util/fs.h"
#include <iosfwd>
#include <list>
#include <map>
#include <optional>
#include <set>
#include <stdint.h>
#include <string>
#include <variant>
#include <vector>
class ArgsManager;
extern const char *const BITCOIN_CONF_FILENAME;
extern const char *const BITCOIN_SETTINGS_FILENAME;
// Return true if -datadir option points to a valid directory or is not
// specified.
bool CheckDataDirOption(const ArgsManager &args);
/**
* Most paths passed as configuration arguments are treated as relative to
* the datadir if they are not absolute.
*
* @param args Parsed arguments and settings.
* @param path The path to be conditionally prefixed with datadir.
* @param net_specific Use network specific datadir variant
* @return The normalized path.
*/
fs::path AbsPathForConfigVal(const ArgsManager &args, const fs::path &path,
bool net_specific = true);
inline bool IsSwitchChar(char c) {
#ifdef WIN32
return c == '-' || c == '/';
#else
return c == '-';
#endif
}
enum class OptionsCategory {
OPTIONS,
CONNECTION,
WALLET,
WALLET_DEBUG_TEST,
ZMQ,
DEBUG_TEST,
CHAINPARAMS,
NODE_RELAY,
BLOCK_CREATION,
RPC,
GUI,
COMMANDS,
REGISTER_COMMANDS,
HIDDEN // Always the last option to avoid printing these in the help
};
struct KeyInfo {
std::string name;
std::string section;
bool negated{false};
};
KeyInfo InterpretKey(std::string key);
std::optional<common::SettingsValue> InterpretValue(const KeyInfo &key,
const std::string *value,
unsigned int flags,
std::string &error);
struct SectionInfo {
std::string m_name;
std::string m_file;
int m_line;
};
std::string SettingToString(const common::SettingsValue &, const std::string &);
std::optional<std::string> SettingToString(const common::SettingsValue &);
int64_t SettingToInt(const common::SettingsValue &, int64_t);
std::optional<int64_t> SettingToInt(const common::SettingsValue &);
bool SettingToBool(const common::SettingsValue &, bool);
std::optional<bool> SettingToBool(const common::SettingsValue &);
class ArgsManager {
public:
/**
* Flags controlling how config and command line arguments are validated and
* interpreted.
*/
enum Flags : uint32_t {
ALLOW_ANY = 0x01, //!< disable validation
// ALLOW_BOOL = 0x02, //!< unimplemented, draft implementation in #16545
// ALLOW_INT = 0x04, //!< unimplemented, draft implementation in #16545
// ALLOW_STRING = 0x08, //!< unimplemented, draft implementation in #16545
// ALLOW_LIST = 0x10, //!< unimplemented, draft implementation in #16545
DISALLOW_NEGATION = 0x20, //!< disallow -nofoo syntax
DISALLOW_ELISION =
0x40, //!< disallow -foo syntax that doesn't assign any value
DEBUG_ONLY = 0x100,
/* Some options would cause cross-contamination if values for
* mainnet were used while running on regtest/testnet (or vice-versa).
* Setting them as NETWORK_ONLY ensures that sharing a config file
* between mainnet and regtest/testnet won't cause problems due to these
* parameters by accident. */
NETWORK_ONLY = 0x200,
// This argument's value is sensitive (such as a password).
SENSITIVE = 0x400,
COMMAND = 0x800,
};
protected:
struct Arg {
std::string m_help_param;
std::string m_help_text;
unsigned int m_flags;
};
mutable RecursiveMutex cs_args;
common::Settings m_settings GUARDED_BY(cs_args);
std::vector<std::string> m_command GUARDED_BY(cs_args);
std::string m_network GUARDED_BY(cs_args);
std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
std::map<OptionsCategory, std::map<std::string, Arg>>
m_available_args GUARDED_BY(cs_args);
bool m_accept_any_command GUARDED_BY(cs_args){true};
std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
std::optional<fs::path> m_config_path GUARDED_BY(cs_args);
mutable fs::path m_cached_blocks_path GUARDED_BY(cs_args);
mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args);
mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args);
[[nodiscard]] bool ReadConfigStream(std::istream &stream,
const std::string &filepath,
std::string &error,
bool ignore_invalid_keys = false);
/**
* Returns true if settings values from the default section should be used,
* depending on the current network and whether the setting is
* network-specific.
*/
bool UseDefaultSection(const std::string &arg) const
EXCLUSIVE_LOCKS_REQUIRED(cs_args);
public:
/**
* Get setting value.
*
* Result will be null if setting was unset, true if "-setting" argument was
* passed false if "-nosetting" argument was passed, and a string if a
* "-setting=value" argument was passed.
*/
common::SettingsValue GetSetting(const std::string &arg) const;
/**
* Get list of setting values.
*/
std::vector<common::SettingsValue>
GetSettingsList(const std::string &arg) const;
ArgsManager();
~ArgsManager();
/**
* Select the network in use
*/
void SelectConfigNetwork(const std::string &network);
[[nodiscard]] bool ParseParameters(int argc, const char *const argv[],
std::string &error);
/**
* Return config file path (read-only)
*/
fs::path GetConfigFilePath() const;
void SetConfigFilePath(fs::path);
[[nodiscard]] bool ReadConfigFiles(std::string &error,
bool ignore_invalid_keys = false);
/**
* Log warnings for options in m_section_only_args when
* they are specified in the default section but not overridden
* on the command line or in a network-specific section in the
* config file.
*/
std::set<std::string> GetUnsuitableSectionOnlyArgs() const;
/**
* Log warnings for unrecognized section names in the config file.
*/
std::list<SectionInfo> GetUnrecognizedSections() const;
struct Command {
/** The command (if one has been registered with AddCommand), or empty */
std::string command;
/**
* If command is non-empty: Any args that followed it
* If command is empty: The unregistered command and any args that followed
* it
*/
std::vector<std::string> args;
};
/**
* Get the command and command args (returns std::nullopt if no command
* provided)
*/
std::optional<const Command> GetCommand() const;
/**
* Get blocks directory path
*
* @return Blocks path which is network specific
*/
fs::path GetBlocksDirPath() const;
/**
* Get data directory path
*
* @return Absolute path on success, otherwise an empty path when a
* non-directory path would be returned
*/
fs::path GetDataDirBase() const { return GetDataDir(false); }
/**
* Get data directory path with appended network identifier
*
* @return Absolute path on success, otherwise an empty path when a
* non-directory path would be returned
*/
fs::path GetDataDirNet() const { return GetDataDir(true); }
/**
* Clear cached directory paths
*/
void ClearPathCache();
/**
* Return a vector of strings of the given argument
*
* @param strArg Argument to get (e.g. "-foo")
* @return command-line arguments
*/
std::vector<std::string> GetArgs(const std::string &strArg) const;
/**
* Return true if the given argument has been manually set
*
* @param strArg Argument to get (e.g. "-foo")
* @return true if the argument has been set
*/
bool IsArgSet(const std::string &strArg) const;
/**
* Return true if the argument was originally passed as a negated option,
* i.e. -nofoo.
*
* @param strArg Argument to get (e.g. "-foo")
* @return true if the argument was passed negated
*/
bool IsArgNegated(const std::string &strArg) const;
/**
* Return string argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
* @param strDefault (e.g. "1")
* @return command-line argument or default value
*/
std::string GetArg(const std::string &strArg,
const std::string &strDefault) const;
std::optional<std::string> GetArg(const std::string &strArg) const;
/**
* Return path argument or default value
*
* @param arg Argument to get a path from (e.g., "-datadir", "-blocksdir" or
* "-walletdir")
* @param default_value Optional default value to return instead of the empty
* path.
* @return normalized path if argument is set, with redundant "." and ".."
* path components and trailing separators removed (see patharg unit test
* for examples or implementation for details). If argument is empty or not
* set, default_value is returned unchanged.
*/
fs::path GetPathArg(std::string arg,
const fs::path &default_value = {}) const;
/**
* Return integer argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
* @param nDefault (e.g. 1)
* @return command-line argument (0 if invalid number) or default value
*/
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const;
std::optional<int64_t> GetIntArg(const std::string &strArg) const;
/**
* Return boolean argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
* @param fDefault (true or false)
* @return command-line argument or default value
*/
bool GetBoolArg(const std::string &strArg, bool fDefault) const;
std::optional<bool> GetBoolArg(const std::string &strArg) const;
/**
* Set an argument if it doesn't already have a value
*
* @param strArg Argument to set (e.g. "-foo")
* @param strValue Value (e.g. "1")
* @return true if argument gets set, false if it already had a value
*/
bool SoftSetArg(const std::string &strArg, const std::string &strValue);
/**
* Set a boolean argument if it doesn't already have a value
*
* @param strArg Argument to set (e.g. "-foo")
* @param fValue Value (e.g. false)
* @return true if argument gets set, false if it already had a value
*/
bool SoftSetBoolArg(const std::string &strArg, bool fValue);
// Forces an arg setting. Called by SoftSetArg() if the arg hasn't already
// been set. Also called directly in testing.
void ForceSetArg(const std::string &strArg, const std::string &strValue);
/**
* Returns the appropriate chain type from the program arguments.
* @return ChainType::MAIN by default; raises runtime error if an invalid
* combination, or unknown chain is given.
*/
ChainType GetChainType() const;
/**
* Returns the appropriate chain type string from the program arguments.
* @return ChainType::MAIN string by default; raises runtime error if an
* invalid combination is given.
*/
std::string GetChainTypeString() const;
/**
* Add argument
*/
void AddArg(const std::string &name, const std::string &help,
unsigned int flags, const OptionsCategory &cat);
/**
* Add subcommand
*/
void AddCommand(const std::string &cmd, const std::string &help);
/**
* Add many hidden arguments
*/
void AddHiddenArgs(const std::vector<std::string> &args);
/**
* Clear available arguments
*/
void ClearArgs() {
LOCK(cs_args);
m_available_args.clear();
m_network_only_args.clear();
}
/**
* Get the help string
*/
std::string GetHelpMessage() const;
/**
* Return Flags for known arg.
* Return nullopt for unknown arg.
*/
std::optional<unsigned int> GetArgFlags(const std::string &name) const;
/**
* Get settings file path, or return false if read-write settings were
* disabled with -nosettings.
*/
bool GetSettingsPath(fs::path *filepath = nullptr, bool temp = false,
bool backup = false) const;
/**
* Read settings file. Push errors to vector, or log them if null.
*/
bool ReadSettingsFile(std::vector<std::string> *errors = nullptr);
/**
* Write settings file or backup settings file. Push errors to vector, or
* log them if null.
*/
bool WriteSettingsFile(std::vector<std::string> *errors = nullptr,
bool backup = false) const;
/**
* Get current setting from config file or read/write settings file,
* ignoring nonpersistent command line or forced settings values.
*/
common::SettingsValue GetPersistentSetting(const std::string &name) const;
/**
* Access settings with lock held.
*/
template <typename Fn> void LockSettings(Fn &&fn) {
LOCK(cs_args);
fn(m_settings);
}
/**
* Log the config file options and the command line arguments,
* useful for troubleshooting.
*/
void LogArgs() const;
private:
/**
* Get data directory path
*
* @param net_specific Append network identifier to the returned path
* @return Absolute path on success, otherwise an empty path when a
* non-directory path would be returned
*/
fs::path GetDataDir(bool net_specific) const;
/**
* Return -regtest/-signet/-testnet/-chain= setting as a ChainType enum if a
* recognized chain type was set, or as a string if an unrecognized chain
* name was set. Raise an exception if an invalid combination of flags was
* provided.
*/
std::variant<ChainType, std::string> GetChainArg() const;
// Helper function for LogArgs().
void
logArgsPrefix(const std::string &prefix, const std::string &section,
const std::map<std::string, std::vector<common::SettingsValue>>
&args) const;
};
extern ArgsManager gArgs;
/**
* @return true if help has been requested via a command-line arg
*/
bool HelpRequested(const ArgsManager &args);
/** Add help options to the args manager */
void SetupHelpOptions(ArgsManager &args);
extern const std::vector<std::string> TEST_OPTIONS_DOC;
/** Checks if a particular test option is present in -test command-line arg
* options */
bool HasTestOption(const ArgsManager &args, const std::string &test_option);
/**
* Format a string to be used as group of options in help messages
*
* @param message Group name (e.g. "RPC server options:")
* @return the formatted string
*/
std::string HelpMessageGroup(const std::string &message);
/**
* Format a string to be used as option description in help messages
*
* @param option Option message (e.g. "-rpcuser=<user>")
* @param message Option description (e.g. "Username for JSON-RPC connections")
* @return the formatted string
*/
std::string HelpMessageOpt(const std::string &option,
const std::string &message);
namespace common {
#ifdef WIN32
class WinCmdLineArgs {
public:
WinCmdLineArgs();
~WinCmdLineArgs();
std::pair<int, char **> get();
private:
int argc;
char **argv;
std::vector<std::string> args;
};
#endif
} // namespace common
#endif // BITCOIN_COMMON_ARGS_H

117
src/i2p/common/settings.h Normal file
View File

@ -0,0 +1,117 @@
// Copyright (c) 2019-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_COMMON_SETTINGS_H
#define BITCOIN_COMMON_SETTINGS_H
#include "../util/fs.h"
#include <cstddef>
#include <map>
#include <string>
#include <vector>
class UniValue;
namespace common {
//! Settings value type (string/integer/boolean/null variant).
//!
//! @note UniValue is used here for convenience and because it can be easily
//! serialized in a readable format. But any other variant type that can
//! be assigned strings, int64_t, and bool values and has get_str(),
//! getInt<int64_t>(), get_bool(), isNum(), isBool(), isFalse(), isTrue()
//! and isNull() methods can be substituted if there's a need to move away
//! from UniValue. (An implementation with boost::variant was posted at
//! https://github.com/bitcoin/bitcoin/pull/15934/files#r337691812)
using SettingsValue = UniValue;
//! Stored settings. This struct combines settings from the command line, a
//! read-only configuration file, and a read-write runtime settings file.
struct Settings {
//! Map of setting name to forced setting value.
std::map<std::string, SettingsValue> forced_settings;
//! Map of setting name to list of command line values.
std::map<std::string, std::vector<SettingsValue>> command_line_options;
//! Map of setting name to read-write file setting value.
std::map<std::string, SettingsValue> rw_settings;
//! Map of config section name and setting name to list of config file values.
std::map<std::string, std::map<std::string, std::vector<SettingsValue>>>
ro_config;
};
//! Read settings file.
bool ReadSettings(const fs::path &path,
std::map<std::string, SettingsValue> &values,
std::vector<std::string> &errors);
//! Write settings file.
bool WriteSettings(const fs::path &path,
const std::map<std::string, SettingsValue> &values,
std::vector<std::string> &errors);
//! Get settings value from combined sources: forced settings, command line
//! arguments, runtime read-write settings, and the read-only config file.
//!
//! @param ignore_default_section_config - ignore values in the default section
//! of the config file (part before any
//! [section] keywords)
//! @param ignore_nonpersistent - ignore non-persistent settings values (forced
//! settings values and values specified on the
//! command line). Only return settings in the
//! read-only config and read-write settings
//! files.
//! @param get_chain_type - enable special backwards compatible behavior
//! for GetChainType
SettingsValue GetSetting(const Settings &settings, const std::string &section,
const std::string &name,
bool ignore_default_section_config,
bool ignore_nonpersistent, bool get_chain_type);
//! Get combined setting value similar to GetSetting(), except if setting was
//! specified multiple times, return a list of all the values specified.
std::vector<SettingsValue> GetSettingsList(const Settings &settings,
const std::string &section,
const std::string &name,
bool ignore_default_section_config);
//! Return true if a setting is set in the default config file section, and not
//! overridden by a higher priority command-line or network section value.
//!
//! This is used to provide user warnings about values that might be getting
//! ignored unintentionally.
bool OnlyHasDefaultSectionSetting(const Settings &settings,
const std::string &section,
const std::string &name);
//! Accessor for list of settings that skips negated values when iterated over.
//! The last boolean `false` value in the list and all earlier values are
//! considered negated.
struct SettingsSpan {
explicit SettingsSpan() = default;
explicit SettingsSpan(const SettingsValue &value) noexcept
: SettingsSpan(&value, 1) {}
explicit SettingsSpan(const SettingsValue *data, size_t size) noexcept
: data(data), size(size) {}
explicit SettingsSpan(const std::vector<SettingsValue> &vec) noexcept;
const SettingsValue *begin() const; //!< Pointer to first non-negated value.
const SettingsValue *end() const; //!< Pointer to end of values.
bool empty() const; //!< True if there are any non-negated values.
bool last_negated() const; //!< True if the last value is negated.
size_t negated() const; //!< Number of negated values.
const SettingsValue *data = nullptr;
size_t size = 0;
};
//! Map lookup helper.
template <typename Map, typename Key>
auto FindKey(Map &&map, Key &&key) -> decltype(&map.at(key)) {
auto it = map.find(key);
return it == map.end() ? nullptr : &it->second;
}
} // namespace common
#endif // BITCOIN_COMMON_SETTINGS_H

View File

@ -0,0 +1,44 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// Compile-time verification of assumptions we make.
#ifndef BITCOIN_COMPAT_ASSUMPTIONS_H
#define BITCOIN_COMPAT_ASSUMPTIONS_H
#include <cstddef>
#include <limits>
// Assumption: We assume the floating-point types to fulfill the requirements of
// IEC 559 (IEEE 754) standard.
// Example(s): Floating-point division by zero in ConnectBlock, CreateTransaction
// and EstimateMedianVal.
static_assert(std::numeric_limits<float>::is_iec559, "IEEE 754 float assumed");
static_assert(std::numeric_limits<double>::is_iec559, "IEEE 754 double assumed");
// Assumption: We assume eight bits per byte (obviously, but remember: don't
// trust -- verify!).
// Example(s): Everywhere :-)
static_assert(std::numeric_limits<unsigned char>::digits == 8, "8-bit byte assumed");
// Assumption: We assume integer widths.
// Example(s): GetSizeOfCompactSize and WriteCompactSize in the serialization
// code.
static_assert(sizeof(short) == 2, "16-bit short assumed");
static_assert(sizeof(int) == 4, "32-bit int assumed");
static_assert(sizeof(unsigned) == 4, "32-bit unsigned assumed");
// Assumption: We assume size_t to be 32-bit or 64-bit.
// Example(s): size_t assumed to be at least 32-bit in ecdsa_signature_parse_der_lax(...).
// size_t assumed to be 32-bit or 64-bit in MallocUsage(...).
static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "size_t assumed to be 32-bit or 64-bit");
static_assert(sizeof(size_t) == sizeof(void*), "Sizes of size_t and void* assumed to be equal");
// Some important things we are NOT assuming (non-exhaustive list):
// * We are NOT assuming a specific value for std::endian::native.
// * We are NOT assuming a specific value for std::locale("").name().
// * We are NOT assuming a specific value for std::numeric_limits<char>::is_signed.
#endif // BITCOIN_COMPAT_ASSUMPTIONS_H

79
src/i2p/compat/byteswap.h Normal file
View File

@ -0,0 +1,79 @@
// Copyright (c) 2014-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_COMPAT_BYTESWAP_H
#define BITCOIN_COMPAT_BYTESWAP_H
#include <cstdint>
#ifdef _MSC_VER
#include <cstdlib>
#endif
// All internal_bswap_* functions can be replaced with std::byteswap once we
// require c++23. Both libstdc++ and libc++ implement std::byteswap via these
// builtins.
#ifndef DISABLE_BUILTIN_BSWAPS
# if defined __has_builtin
# if __has_builtin(__builtin_bswap16)
# define bitcoin_builtin_bswap16(x) __builtin_bswap16(x)
# endif
# if __has_builtin(__builtin_bswap32)
# define bitcoin_builtin_bswap32(x) __builtin_bswap32(x)
# endif
# if __has_builtin(__builtin_bswap64)
# define bitcoin_builtin_bswap64(x) __builtin_bswap64(x)
# endif
# elif defined(_MSC_VER)
# define bitcoin_builtin_bswap16(x) _byteswap_ushort(x)
# define bitcoin_builtin_bswap32(x) _byteswap_ulong(x)
# define bitcoin_builtin_bswap64(x) _byteswap_uint64(x)
# endif
#endif
// MSVC's _byteswap_* functions are not constexpr
#ifndef _MSC_VER
#define BSWAP_CONSTEXPR constexpr
#else
#define BSWAP_CONSTEXPR
#endif
inline BSWAP_CONSTEXPR uint16_t internal_bswap_16(uint16_t x)
{
#ifdef bitcoin_builtin_bswap16
return bitcoin_builtin_bswap16(x);
#else
return (x >> 8) | (x << 8);
#endif
}
inline BSWAP_CONSTEXPR uint32_t internal_bswap_32(uint32_t x)
{
#ifdef bitcoin_builtin_bswap32
return bitcoin_builtin_bswap32(x);
#else
return (((x & 0xff000000U) >> 24) | ((x & 0x00ff0000U) >> 8) |
((x & 0x0000ff00U) << 8) | ((x & 0x000000ffU) << 24));
#endif
}
inline BSWAP_CONSTEXPR uint64_t internal_bswap_64(uint64_t x)
{
#ifdef bitcoin_builtin_bswap64
return bitcoin_builtin_bswap64(x);
#else
return (((x & 0xff00000000000000ull) >> 56)
| ((x & 0x00ff000000000000ull) >> 40)
| ((x & 0x0000ff0000000000ull) >> 24)
| ((x & 0x000000ff00000000ull) >> 8)
| ((x & 0x00000000ff000000ull) << 8)
| ((x & 0x0000000000ff0000ull) << 24)
| ((x & 0x000000000000ff00ull) << 40)
| ((x & 0x00000000000000ffull) << 56));
#endif
}
#endif // BITCOIN_COMPAT_BYTESWAP_H

115
src/i2p/compat/compat.h Normal file
View File

@ -0,0 +1,115 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_COMPAT_COMPAT_H
#define BITCOIN_COMPAT_COMPAT_H
// Windows defines FD_SETSIZE to 64 (see _fd_types.h in mingw-w64),
// which is too small for our usage, but allows us to redefine it safely.
// We redefine it to be 1024, to match glibc, see typesizes.h.
#ifdef WIN32
#ifdef FD_SETSIZE
#undef FD_SETSIZE
#endif
#define FD_SETSIZE 1024
#include <winsock2.h>
#include <ws2tcpip.h>
#include <cstdint>
#else
#include <arpa/inet.h> // IWYU pragma: export
#include <fcntl.h> // IWYU pragma: export
#include <ifaddrs.h> // IWYU pragma: export
#include <net/if.h> // IWYU pragma: export
#include <netdb.h> // IWYU pragma: export
#include <netinet/in.h> // IWYU pragma: export
#include <netinet/tcp.h> // IWYU pragma: export
#include <sys/mman.h> // IWYU pragma: export
#include <sys/select.h> // IWYU pragma: export
#include <sys/socket.h> // IWYU pragma: export
#include <sys/types.h> // IWYU pragma: export
#include <unistd.h> // IWYU pragma: export
#endif
// Windows does not have `sa_family_t` - it defines `sockaddr::sa_family` as `u_short`.
// Thus define `sa_family_t` on Windows too so that the rest of the code can use `sa_family_t`.
// See https://learn.microsoft.com/en-us/windows/win32/api/winsock/ns-winsock-sockaddr#syntax
#ifdef WIN32
typedef u_short sa_family_t;
#endif
// We map Linux / BSD error functions and codes, to the equivalent
// Windows definitions, and use the WSA* names throughout our code.
// Note that glibc defines EWOULDBLOCK as EAGAIN (see errno.h).
#ifndef WIN32
typedef unsigned int SOCKET;
#include <cerrno>
#define WSAGetLastError() errno
#define WSAEINVAL EINVAL
#define WSAEWOULDBLOCK EWOULDBLOCK
#define WSAEAGAIN EAGAIN
#define WSAEMSGSIZE EMSGSIZE
#define WSAEINTR EINTR
#define WSAEINPROGRESS EINPROGRESS
#define WSAEADDRINUSE EADDRINUSE
#define INVALID_SOCKET (SOCKET)(~0)
#define SOCKET_ERROR -1
#else
// WSAEAGAIN doesn't exist on Windows
#ifdef EAGAIN
#define WSAEAGAIN EAGAIN
#else
#define WSAEAGAIN WSAEWOULDBLOCK
#endif
#endif
// Windows defines MAX_PATH as it's maximum path length.
// We define MAX_PATH for use on non-Windows systems.
#ifndef WIN32
#define MAX_PATH 1024
#endif
// ssize_t is POSIX, and not present when using MSVC.
#ifdef _MSC_VER
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#endif
// The type of the option value passed to getsockopt & setsockopt
// differs between Windows and non-Windows.
#ifndef WIN32
typedef void* sockopt_arg_type;
#else
typedef char* sockopt_arg_type;
#endif
#ifdef WIN32
// Export main() and ensure working ASLR when using mingw-w64.
// Exporting a symbol will prevent the linker from stripping
// the .reloc section from the binary, which is a requirement
// for ASLR. While release builds are not affected, anyone
// building with a binutils < 2.36 is subject to this ld bug.
#define MAIN_FUNCTION __declspec(dllexport) int main(int argc, char* argv[])
#else
#define MAIN_FUNCTION int main(int argc, char* argv[])
#endif
// Note these both should work with the current usage of poll, but best to be safe
// WIN32 poll is broken https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/
// __APPLE__ poll is broke https://github.com/bitcoin/bitcoin/pull/14336#issuecomment-437384408
#if defined(__linux__)
#define USE_POLL
#endif
// MSG_NOSIGNAL is not available on some platforms, if it doesn't exist define it as 0
#if !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0
#endif
// MSG_DONTWAIT is not available on some platforms, if it doesn't exist define it as 0
#if !defined(MSG_DONTWAIT)
#define MSG_DONTWAIT 0
#endif
#endif // BITCOIN_COMPAT_COMPAT_H

74
src/i2p/compat/endian.h Normal file
View File

@ -0,0 +1,74 @@
// Copyright (c) 2014-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_COMPAT_ENDIAN_H
#define BITCOIN_COMPAT_ENDIAN_H
#include "byteswap.h"
#include <bit>
#include <cstdint>
inline BSWAP_CONSTEXPR uint16_t htobe16_internal(uint16_t host_16bits)
{
if constexpr (std::endian::native == std::endian::little) return internal_bswap_16(host_16bits);
else return host_16bits;
}
inline BSWAP_CONSTEXPR uint16_t htole16_internal(uint16_t host_16bits)
{
if constexpr (std::endian::native == std::endian::big) return internal_bswap_16(host_16bits);
else return host_16bits;
}
inline BSWAP_CONSTEXPR uint16_t be16toh_internal(uint16_t big_endian_16bits)
{
if constexpr (std::endian::native == std::endian::little) return internal_bswap_16(big_endian_16bits);
else return big_endian_16bits;
}
inline BSWAP_CONSTEXPR uint16_t le16toh_internal(uint16_t little_endian_16bits)
{
if constexpr (std::endian::native == std::endian::big) return internal_bswap_16(little_endian_16bits);
else return little_endian_16bits;
}
inline BSWAP_CONSTEXPR uint32_t htobe32_internal(uint32_t host_32bits)
{
if constexpr (std::endian::native == std::endian::little) return internal_bswap_32(host_32bits);
else return host_32bits;
}
inline BSWAP_CONSTEXPR uint32_t htole32_internal(uint32_t host_32bits)
{
if constexpr (std::endian::native == std::endian::big) return internal_bswap_32(host_32bits);
else return host_32bits;
}
inline BSWAP_CONSTEXPR uint32_t be32toh_internal(uint32_t big_endian_32bits)
{
if constexpr (std::endian::native == std::endian::little) return internal_bswap_32(big_endian_32bits);
else return big_endian_32bits;
}
inline BSWAP_CONSTEXPR uint32_t le32toh_internal(uint32_t little_endian_32bits)
{
if constexpr (std::endian::native == std::endian::big) return internal_bswap_32(little_endian_32bits);
else return little_endian_32bits;
}
inline BSWAP_CONSTEXPR uint64_t htobe64_internal(uint64_t host_64bits)
{
if constexpr (std::endian::native == std::endian::little) return internal_bswap_64(host_64bits);
else return host_64bits;
}
inline BSWAP_CONSTEXPR uint64_t htole64_internal(uint64_t host_64bits)
{
if constexpr (std::endian::native == std::endian::big) return internal_bswap_64(host_64bits);
else return host_64bits;
}
inline BSWAP_CONSTEXPR uint64_t be64toh_internal(uint64_t big_endian_64bits)
{
if constexpr (std::endian::native == std::endian::little) return internal_bswap_64(big_endian_64bits);
else return big_endian_64bits;
}
inline BSWAP_CONSTEXPR uint64_t le64toh_internal(uint64_t little_endian_64bits)
{
if constexpr (std::endian::native == std::endian::big) return internal_bswap_64(little_endian_64bits);
else return little_endian_64bits;
}
#endif // BITCOIN_COMPAT_ENDIAN_H

View File

@ -0,0 +1,29 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CONSENSUS_AMOUNT_H
#define BITCOIN_CONSENSUS_AMOUNT_H
#include <cstdint>
/** Amount in satoshis (Can be negative) */
typedef int64_t CAmount;
/** The amount of satoshis in one BTC. */
static constexpr CAmount COIN = 100000000;
/** No amount larger than this (in satoshi) is valid.
*
* Note that this constant is *not* the total money supply, which in Bitcoin
* currently happens to be less than 21,000,000 BTC for various reasons, but
* rather a sanity check. As this sanity check is used by consensus-critical
* validation code, the exact value of the MAX_MONEY constant is consensus
* critical; in unusual circumstances like a(nother) overflow bug that allowed
* for the creation of coins out of thin air modification could lead to a fork.
* */
static constexpr CAmount MAX_MONEY = 21000000 * COIN;
inline bool MoneyRange(const CAmount& nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
#endif // BITCOIN_CONSENSUS_AMOUNT_H

151
src/i2p/consensus/params.h Normal file
View File

@ -0,0 +1,151 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CONSENSUS_PARAMS_H
#define BITCOIN_CONSENSUS_PARAMS_H
#include "../uint256.h"
#include <chrono>
#include <limits>
#include <map>
#include <vector>
namespace Consensus {
/**
* A buried deployment is one where the height of the activation has been hardcoded into
* the client implementation long after the consensus change has activated. See BIP 90.
*/
enum BuriedDeployment : int16_t {
// buried deployments get negative values to avoid overlap with DeploymentPos
DEPLOYMENT_HEIGHTINCB = std::numeric_limits<int16_t>::min(),
DEPLOYMENT_CLTV,
DEPLOYMENT_DERSIG,
DEPLOYMENT_CSV,
DEPLOYMENT_SEGWIT,
};
constexpr bool ValidDeployment(BuriedDeployment dep) { return dep <= DEPLOYMENT_SEGWIT; }
enum DeploymentPos : uint16_t {
DEPLOYMENT_TESTDUMMY,
DEPLOYMENT_TAPROOT, // Deployment of Schnorr/Taproot (BIPs 340-342)
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp
MAX_VERSION_BITS_DEPLOYMENTS
};
constexpr bool ValidDeployment(DeploymentPos dep) { return dep < MAX_VERSION_BITS_DEPLOYMENTS; }
/**
* Struct for each individual consensus rule change using BIP9.
*/
struct BIP9Deployment {
/** Bit position to select the particular bit in nVersion. */
int bit{28};
/** Start MedianTime for version bits miner confirmation. Can be a date in the past */
int64_t nStartTime{NEVER_ACTIVE};
/** Timeout/expiry MedianTime for the deployment attempt. */
int64_t nTimeout{NEVER_ACTIVE};
/** If lock in occurs, delay activation until at least this block
* height. Note that activation will only occur on a retarget
* boundary.
*/
int min_activation_height{0};
/** Constant for nTimeout very far in the future. */
static constexpr int64_t NO_TIMEOUT = std::numeric_limits<int64_t>::max();
/** Special value for nStartTime indicating that the deployment is always active.
* This is useful for testing, as it means tests don't need to deal with the activation
* process (which takes at least 3 BIP9 intervals). Only tests that specifically test the
* behaviour during activation cannot use this. */
static constexpr int64_t ALWAYS_ACTIVE = -1;
/** Special value for nStartTime indicating that the deployment is never active.
* This is useful for integrating the code changes for a new feature
* prior to deploying it on some or all networks. */
static constexpr int64_t NEVER_ACTIVE = -2;
};
/**
* Parameters that influence chain consensus.
*/
struct Params {
uint256 hashGenesisBlock;
int nSubsidyHalvingInterval;
/**
* Hashes of blocks that
* - are known to be consensus valid, and
* - buried in the chain, and
* - fail if the default script verify flags are applied.
*/
std::map<uint256, uint32_t> script_flag_exceptions;
/** Block height and hash at which BIP34 becomes active */
int BIP34Height;
uint256 BIP34Hash;
/** Block height at which BIP65 becomes active */
int BIP65Height;
/** Block height at which BIP66 becomes active */
int BIP66Height;
/** Block height at which CSV (BIP68, BIP112 and BIP113) becomes active */
int CSVHeight;
/** Block height at which Segwit (BIP141, BIP143 and BIP147) becomes active.
* Note that segwit v0 script rules are enforced on all blocks except the
* BIP 16 exception blocks. */
int SegwitHeight;
/** Don't warn about unknown BIP 9 activations below this height.
* This prevents us from warning about the CSV and segwit activations. */
int MinBIP9WarningHeight;
/**
* Minimum blocks including miner confirmation of the total of 2016 blocks in a retargeting period,
* (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments.
* Examples: 1916 for 95%, 1512 for testchains.
*/
uint32_t nRuleChangeActivationThreshold;
uint32_t nMinerConfirmationWindow;
BIP9Deployment vDeployments[MAX_VERSION_BITS_DEPLOYMENTS];
/** Proof of work parameters */
uint256 powLimit;
bool fPowAllowMinDifficultyBlocks;
bool fPowNoRetargeting;
int64_t nPowTargetSpacing;
int64_t nPowTargetTimespan;
std::chrono::seconds PowTargetSpacing() const
{
return std::chrono::seconds{nPowTargetSpacing};
}
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
/** The best chain should have at least this much work */
uint256 nMinimumChainWork;
/** By default assume that the signatures in ancestors of this block are valid */
uint256 defaultAssumeValid;
/**
* If true, witness commitments contain a payload equal to a Bitcoin Script solution
* to the signet challenge. See BIP325.
*/
bool signet_blocks{false};
std::vector<uint8_t> signet_challenge;
int DeploymentHeight(BuriedDeployment dep) const
{
switch (dep) {
case DEPLOYMENT_HEIGHTINCB:
return BIP34Height;
case DEPLOYMENT_CLTV:
return BIP65Height;
case DEPLOYMENT_DERSIG:
return BIP66Height;
case DEPLOYMENT_CSV:
return CSVHeight;
case DEPLOYMENT_SEGWIT:
return SegwitHeight;
} // no default case, so the compiler can warn about missing cases
return std::numeric_limits<int>::max();
}
};
} // namespace Consensus
#endif // BITCOIN_CONSENSUS_PARAMS_H

159
src/i2p/crypto/chacha20.h Normal file
View File

@ -0,0 +1,159 @@
// Copyright (c) 2017-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CRYPTO_CHACHA20_H
#define BITCOIN_CRYPTO_CHACHA20_H
#include "../span.h"
#include <array>
#include <cstddef>
#include <cstdlib>
#include <stdint.h>
#include <utility>
// classes for ChaCha20 256-bit stream cipher developed by Daniel J. Bernstein
// https://cr.yp.to/chacha/chacha-20080128.pdf.
//
// The 128-bit input is here implemented as a 96-bit nonce and a 32-bit block
// counter, as in RFC8439 Section 2.3. When the 32-bit block counter overflows
// the first 32-bit part of the nonce is automatically incremented, making it
// conceptually compatible with variants that use a 64/64 split instead.
/** ChaCha20 cipher that only operates on multiples of 64 bytes. */
class ChaCha20Aligned
{
private:
uint32_t input[12];
public:
/** Expected key length in constructor and SetKey. */
static constexpr unsigned KEYLEN{32};
/** Block size (inputs/outputs to Keystream / Crypt should be multiples of this). */
static constexpr unsigned BLOCKLEN{64};
/** For safety, disallow initialization without key. */
ChaCha20Aligned() noexcept = delete;
/** Initialize a cipher with specified 32-byte key. */
ChaCha20Aligned(Span<const std::byte> key) noexcept;
/** Destructor to clean up private memory. */
~ChaCha20Aligned();
/** Set 32-byte key, and seek to nonce 0 and block position 0. */
void SetKey(Span<const std::byte> key) noexcept;
/** Type for 96-bit nonces used by the Set function below.
*
* The first field corresponds to the LE32-encoded first 4 bytes of the nonce, also referred
* to as the '32-bit fixed-common part' in Example 2.8.2 of RFC8439.
*
* The second field corresponds to the LE64-encoded last 8 bytes of the nonce.
*
*/
using Nonce96 = std::pair<uint32_t, uint64_t>;
/** Set the 96-bit nonce and 32-bit block counter.
*
* Block_counter selects a position to seek to (to byte BLOCKLEN*block_counter). After 256 GiB,
* the block counter overflows, and nonce.first is incremented.
*/
void Seek(Nonce96 nonce, uint32_t block_counter) noexcept;
/** outputs the keystream into out, whose length must be a multiple of BLOCKLEN. */
void Keystream(Span<std::byte> out) noexcept;
/** en/deciphers the message <input> and write the result into <output>
*
* The size of input and output must be equal, and be a multiple of BLOCKLEN.
*/
void Crypt(Span<const std::byte> input, Span<std::byte> output) noexcept;
};
/** Unrestricted ChaCha20 cipher. */
class ChaCha20
{
private:
ChaCha20Aligned m_aligned;
std::array<std::byte, ChaCha20Aligned::BLOCKLEN> m_buffer;
unsigned m_bufleft{0};
public:
/** Expected key length in constructor and SetKey. */
static constexpr unsigned KEYLEN = ChaCha20Aligned::KEYLEN;
/** For safety, disallow initialization without key. */
ChaCha20() noexcept = delete;
/** Initialize a cipher with specified 32-byte key. */
ChaCha20(Span<const std::byte> key) noexcept : m_aligned(key) {}
/** Destructor to clean up private memory. */
~ChaCha20();
/** Set 32-byte key, and seek to nonce 0 and block position 0. */
void SetKey(Span<const std::byte> key) noexcept;
/** 96-bit nonce type. */
using Nonce96 = ChaCha20Aligned::Nonce96;
/** Set the 96-bit nonce and 32-bit block counter. See ChaCha20Aligned::Seek. */
void Seek(Nonce96 nonce, uint32_t block_counter) noexcept
{
m_aligned.Seek(nonce, block_counter);
m_bufleft = 0;
}
/** en/deciphers the message <in_bytes> and write the result into <out_bytes>
*
* The size of in_bytes and out_bytes must be equal.
*/
void Crypt(Span<const std::byte> in_bytes, Span<std::byte> out_bytes) noexcept;
/** outputs the keystream to out. */
void Keystream(Span<std::byte> out) noexcept;
};
/** Forward-secure ChaCha20
*
* This implements a stream cipher that automatically transitions to a new stream with a new key
* and new nonce after a predefined number of encryptions or decryptions.
*
* See BIP324 for details.
*/
class FSChaCha20
{
private:
/** Internal stream cipher. */
ChaCha20 m_chacha20;
/** The number of encryptions/decryptions before a rekey happens. */
const uint32_t m_rekey_interval;
/** The number of encryptions/decryptions since the last rekey. */
uint32_t m_chunk_counter{0};
/** The number of rekey operations that have happened. */
uint64_t m_rekey_counter{0};
public:
/** Length of keys expected by the constructor. */
static constexpr unsigned KEYLEN = 32;
// No copy or move to protect the secret.
FSChaCha20(const FSChaCha20&) = delete;
FSChaCha20(FSChaCha20&&) = delete;
FSChaCha20& operator=(const FSChaCha20&) = delete;
FSChaCha20& operator=(FSChaCha20&&) = delete;
/** Construct an FSChaCha20 cipher that rekeys every rekey_interval Crypt() calls. */
FSChaCha20(Span<const std::byte> key, uint32_t rekey_interval) noexcept;
/** Encrypt or decrypt a chunk. */
void Crypt(Span<const std::byte> input, Span<std::byte> output) noexcept;
};
#endif // BITCOIN_CRYPTO_CHACHA20_H

85
src/i2p/crypto/common.h Normal file
View File

@ -0,0 +1,85 @@
// Copyright (c) 2014-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CRYPTO_COMMON_H
#define BITCOIN_CRYPTO_COMMON_H
#include "../compat/endian.h"
#include <cstdint>
#include <cstring>
uint16_t static inline ReadLE16(const unsigned char* ptr)
{
uint16_t x;
memcpy(&x, ptr, 2);
return le16toh_internal(x);
}
uint32_t static inline ReadLE32(const unsigned char* ptr)
{
uint32_t x;
memcpy(&x, ptr, 4);
return le32toh_internal(x);
}
uint64_t static inline ReadLE64(const unsigned char* ptr)
{
uint64_t x;
memcpy(&x, ptr, 8);
return le64toh_internal(x);
}
void static inline WriteLE16(unsigned char* ptr, uint16_t x)
{
uint16_t v = htole16_internal(x);
memcpy(ptr, &v, 2);
}
void static inline WriteLE32(unsigned char* ptr, uint32_t x)
{
uint32_t v = htole32_internal(x);
memcpy(ptr, &v, 4);
}
void static inline WriteLE64(unsigned char* ptr, uint64_t x)
{
uint64_t v = htole64_internal(x);
memcpy(ptr, &v, 8);
}
uint16_t static inline ReadBE16(const unsigned char* ptr)
{
uint16_t x;
memcpy(&x, ptr, 2);
return be16toh_internal(x);
}
uint32_t static inline ReadBE32(const unsigned char* ptr)
{
uint32_t x;
memcpy(&x, ptr, 4);
return be32toh_internal(x);
}
uint64_t static inline ReadBE64(const unsigned char* ptr)
{
uint64_t x;
memcpy(&x, ptr, 8);
return be64toh_internal(x);
}
void static inline WriteBE32(unsigned char* ptr, uint32_t x)
{
uint32_t v = htobe32_internal(x);
memcpy(ptr, &v, 4);
}
void static inline WriteBE64(unsigned char* ptr, uint64_t x)
{
uint64_t v = htobe64_internal(x);
memcpy(ptr, &v, 8);
}
#endif // BITCOIN_CRYPTO_COMMON_H

View File

@ -0,0 +1,32 @@
// Copyright (c) 2014-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "hmac_sha512.h"
#include <string.h>
CHMAC_SHA512::CHMAC_SHA512(const unsigned char *key, size_t keylen) {
unsigned char rkey[128];
if (keylen <= 128) {
memcpy(rkey, key, keylen);
memset(rkey + keylen, 0, 128 - keylen);
} else {
CSHA512().Write(key, keylen).Finalize(rkey);
memset(rkey + 64, 0, 64);
}
for (int n = 0; n < 128; n++)
rkey[n] ^= 0x5c;
outer.Write(rkey, 128);
for (int n = 0; n < 128; n++)
rkey[n] ^= 0x5c ^ 0x36;
inner.Write(rkey, 128);
}
void CHMAC_SHA512::Finalize(unsigned char hash[OUTPUT_SIZE]) {
unsigned char temp[64];
inner.Finalize(temp);
outer.Write(temp, 64).Finalize(hash);
}

View File

@ -0,0 +1,30 @@
// Copyright (c) 2014-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CRYPTO_HMAC_SHA512_H
#define BITCOIN_CRYPTO_HMAC_SHA512_H
#include "sha512.h"
#include <cstdlib>
#include <stdint.h>
/** A hasher class for HMAC-SHA-512. */
class CHMAC_SHA512 {
private:
CSHA512 outer;
CSHA512 inner;
public:
static const size_t OUTPUT_SIZE = 64;
CHMAC_SHA512(const unsigned char *key, size_t keylen);
CHMAC_SHA512 &Write(const unsigned char *data, size_t len) {
inner.Write(data, len);
return *this;
}
void Finalize(unsigned char hash[OUTPUT_SIZE]);
};
#endif // BITCOIN_CRYPTO_HMAC_SHA512_H

View File

@ -0,0 +1,320 @@
// Copyright (c) 2014-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "ripemd160.h"
#include "common.h"
#include <string.h>
// Internal implementation code.
namespace {
/// Internal RIPEMD-160 implementation.
namespace ripemd160 {
uint32_t inline f1(uint32_t x, uint32_t y, uint32_t z) { return x ^ y ^ z; }
uint32_t inline f2(uint32_t x, uint32_t y, uint32_t z) {
return (x & y) | (~x & z);
}
uint32_t inline f3(uint32_t x, uint32_t y, uint32_t z) { return (x | ~y) ^ z; }
uint32_t inline f4(uint32_t x, uint32_t y, uint32_t z) {
return (x & z) | (y & ~z);
}
uint32_t inline f5(uint32_t x, uint32_t y, uint32_t z) { return x ^ (y | ~z); }
/** Initialize RIPEMD-160 state. */
void inline Initialize(uint32_t *s) {
s[0] = 0x67452301ul;
s[1] = 0xEFCDAB89ul;
s[2] = 0x98BADCFEul;
s[3] = 0x10325476ul;
s[4] = 0xC3D2E1F0ul;
}
uint32_t inline rol(uint32_t x, int i) { return (x << i) | (x >> (32 - i)); }
void inline Round(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e,
uint32_t f, uint32_t x, uint32_t k, int r) {
a = rol(a + f + x + k, r) + e;
c = rol(c, 10);
}
void inline R11(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e,
uint32_t x, int r) {
Round(a, b, c, d, e, f1(b, c, d), x, 0, r);
}
void inline R21(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e,
uint32_t x, int r) {
Round(a, b, c, d, e, f2(b, c, d), x, 0x5A827999ul, r);
}
void inline R31(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e,
uint32_t x, int r) {
Round(a, b, c, d, e, f3(b, c, d), x, 0x6ED9EBA1ul, r);
}
void inline R41(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e,
uint32_t x, int r) {
Round(a, b, c, d, e, f4(b, c, d), x, 0x8F1BBCDCul, r);
}
void inline R51(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e,
uint32_t x, int r) {
Round(a, b, c, d, e, f5(b, c, d), x, 0xA953FD4Eul, r);
}
void inline R12(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e,
uint32_t x, int r) {
Round(a, b, c, d, e, f5(b, c, d), x, 0x50A28BE6ul, r);
}
void inline R22(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e,
uint32_t x, int r) {
Round(a, b, c, d, e, f4(b, c, d), x, 0x5C4DD124ul, r);
}
void inline R32(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e,
uint32_t x, int r) {
Round(a, b, c, d, e, f3(b, c, d), x, 0x6D703EF3ul, r);
}
void inline R42(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e,
uint32_t x, int r) {
Round(a, b, c, d, e, f2(b, c, d), x, 0x7A6D76E9ul, r);
}
void inline R52(uint32_t &a, uint32_t b, uint32_t &c, uint32_t d, uint32_t e,
uint32_t x, int r) {
Round(a, b, c, d, e, f1(b, c, d), x, 0, r);
}
/** Perform a RIPEMD-160 transformation, processing a 64-byte chunk. */
void Transform(uint32_t *s, const unsigned char *chunk) {
uint32_t a1 = s[0], b1 = s[1], c1 = s[2], d1 = s[3], e1 = s[4];
uint32_t a2 = a1, b2 = b1, c2 = c1, d2 = d1, e2 = e1;
uint32_t w0 = ReadLE32(chunk + 0), w1 = ReadLE32(chunk + 4),
w2 = ReadLE32(chunk + 8), w3 = ReadLE32(chunk + 12);
uint32_t w4 = ReadLE32(chunk + 16), w5 = ReadLE32(chunk + 20),
w6 = ReadLE32(chunk + 24), w7 = ReadLE32(chunk + 28);
uint32_t w8 = ReadLE32(chunk + 32), w9 = ReadLE32(chunk + 36),
w10 = ReadLE32(chunk + 40), w11 = ReadLE32(chunk + 44);
uint32_t w12 = ReadLE32(chunk + 48), w13 = ReadLE32(chunk + 52),
w14 = ReadLE32(chunk + 56), w15 = ReadLE32(chunk + 60);
R11(a1, b1, c1, d1, e1, w0, 11);
R12(a2, b2, c2, d2, e2, w5, 8);
R11(e1, a1, b1, c1, d1, w1, 14);
R12(e2, a2, b2, c2, d2, w14, 9);
R11(d1, e1, a1, b1, c1, w2, 15);
R12(d2, e2, a2, b2, c2, w7, 9);
R11(c1, d1, e1, a1, b1, w3, 12);
R12(c2, d2, e2, a2, b2, w0, 11);
R11(b1, c1, d1, e1, a1, w4, 5);
R12(b2, c2, d2, e2, a2, w9, 13);
R11(a1, b1, c1, d1, e1, w5, 8);
R12(a2, b2, c2, d2, e2, w2, 15);
R11(e1, a1, b1, c1, d1, w6, 7);
R12(e2, a2, b2, c2, d2, w11, 15);
R11(d1, e1, a1, b1, c1, w7, 9);
R12(d2, e2, a2, b2, c2, w4, 5);
R11(c1, d1, e1, a1, b1, w8, 11);
R12(c2, d2, e2, a2, b2, w13, 7);
R11(b1, c1, d1, e1, a1, w9, 13);
R12(b2, c2, d2, e2, a2, w6, 7);
R11(a1, b1, c1, d1, e1, w10, 14);
R12(a2, b2, c2, d2, e2, w15, 8);
R11(e1, a1, b1, c1, d1, w11, 15);
R12(e2, a2, b2, c2, d2, w8, 11);
R11(d1, e1, a1, b1, c1, w12, 6);
R12(d2, e2, a2, b2, c2, w1, 14);
R11(c1, d1, e1, a1, b1, w13, 7);
R12(c2, d2, e2, a2, b2, w10, 14);
R11(b1, c1, d1, e1, a1, w14, 9);
R12(b2, c2, d2, e2, a2, w3, 12);
R11(a1, b1, c1, d1, e1, w15, 8);
R12(a2, b2, c2, d2, e2, w12, 6);
R21(e1, a1, b1, c1, d1, w7, 7);
R22(e2, a2, b2, c2, d2, w6, 9);
R21(d1, e1, a1, b1, c1, w4, 6);
R22(d2, e2, a2, b2, c2, w11, 13);
R21(c1, d1, e1, a1, b1, w13, 8);
R22(c2, d2, e2, a2, b2, w3, 15);
R21(b1, c1, d1, e1, a1, w1, 13);
R22(b2, c2, d2, e2, a2, w7, 7);
R21(a1, b1, c1, d1, e1, w10, 11);
R22(a2, b2, c2, d2, e2, w0, 12);
R21(e1, a1, b1, c1, d1, w6, 9);
R22(e2, a2, b2, c2, d2, w13, 8);
R21(d1, e1, a1, b1, c1, w15, 7);
R22(d2, e2, a2, b2, c2, w5, 9);
R21(c1, d1, e1, a1, b1, w3, 15);
R22(c2, d2, e2, a2, b2, w10, 11);
R21(b1, c1, d1, e1, a1, w12, 7);
R22(b2, c2, d2, e2, a2, w14, 7);
R21(a1, b1, c1, d1, e1, w0, 12);
R22(a2, b2, c2, d2, e2, w15, 7);
R21(e1, a1, b1, c1, d1, w9, 15);
R22(e2, a2, b2, c2, d2, w8, 12);
R21(d1, e1, a1, b1, c1, w5, 9);
R22(d2, e2, a2, b2, c2, w12, 7);
R21(c1, d1, e1, a1, b1, w2, 11);
R22(c2, d2, e2, a2, b2, w4, 6);
R21(b1, c1, d1, e1, a1, w14, 7);
R22(b2, c2, d2, e2, a2, w9, 15);
R21(a1, b1, c1, d1, e1, w11, 13);
R22(a2, b2, c2, d2, e2, w1, 13);
R21(e1, a1, b1, c1, d1, w8, 12);
R22(e2, a2, b2, c2, d2, w2, 11);
R31(d1, e1, a1, b1, c1, w3, 11);
R32(d2, e2, a2, b2, c2, w15, 9);
R31(c1, d1, e1, a1, b1, w10, 13);
R32(c2, d2, e2, a2, b2, w5, 7);
R31(b1, c1, d1, e1, a1, w14, 6);
R32(b2, c2, d2, e2, a2, w1, 15);
R31(a1, b1, c1, d1, e1, w4, 7);
R32(a2, b2, c2, d2, e2, w3, 11);
R31(e1, a1, b1, c1, d1, w9, 14);
R32(e2, a2, b2, c2, d2, w7, 8);
R31(d1, e1, a1, b1, c1, w15, 9);
R32(d2, e2, a2, b2, c2, w14, 6);
R31(c1, d1, e1, a1, b1, w8, 13);
R32(c2, d2, e2, a2, b2, w6, 6);
R31(b1, c1, d1, e1, a1, w1, 15);
R32(b2, c2, d2, e2, a2, w9, 14);
R31(a1, b1, c1, d1, e1, w2, 14);
R32(a2, b2, c2, d2, e2, w11, 12);
R31(e1, a1, b1, c1, d1, w7, 8);
R32(e2, a2, b2, c2, d2, w8, 13);
R31(d1, e1, a1, b1, c1, w0, 13);
R32(d2, e2, a2, b2, c2, w12, 5);
R31(c1, d1, e1, a1, b1, w6, 6);
R32(c2, d2, e2, a2, b2, w2, 14);
R31(b1, c1, d1, e1, a1, w13, 5);
R32(b2, c2, d2, e2, a2, w10, 13);
R31(a1, b1, c1, d1, e1, w11, 12);
R32(a2, b2, c2, d2, e2, w0, 13);
R31(e1, a1, b1, c1, d1, w5, 7);
R32(e2, a2, b2, c2, d2, w4, 7);
R31(d1, e1, a1, b1, c1, w12, 5);
R32(d2, e2, a2, b2, c2, w13, 5);
R41(c1, d1, e1, a1, b1, w1, 11);
R42(c2, d2, e2, a2, b2, w8, 15);
R41(b1, c1, d1, e1, a1, w9, 12);
R42(b2, c2, d2, e2, a2, w6, 5);
R41(a1, b1, c1, d1, e1, w11, 14);
R42(a2, b2, c2, d2, e2, w4, 8);
R41(e1, a1, b1, c1, d1, w10, 15);
R42(e2, a2, b2, c2, d2, w1, 11);
R41(d1, e1, a1, b1, c1, w0, 14);
R42(d2, e2, a2, b2, c2, w3, 14);
R41(c1, d1, e1, a1, b1, w8, 15);
R42(c2, d2, e2, a2, b2, w11, 14);
R41(b1, c1, d1, e1, a1, w12, 9);
R42(b2, c2, d2, e2, a2, w15, 6);
R41(a1, b1, c1, d1, e1, w4, 8);
R42(a2, b2, c2, d2, e2, w0, 14);
R41(e1, a1, b1, c1, d1, w13, 9);
R42(e2, a2, b2, c2, d2, w5, 6);
R41(d1, e1, a1, b1, c1, w3, 14);
R42(d2, e2, a2, b2, c2, w12, 9);
R41(c1, d1, e1, a1, b1, w7, 5);
R42(c2, d2, e2, a2, b2, w2, 12);
R41(b1, c1, d1, e1, a1, w15, 6);
R42(b2, c2, d2, e2, a2, w13, 9);
R41(a1, b1, c1, d1, e1, w14, 8);
R42(a2, b2, c2, d2, e2, w9, 12);
R41(e1, a1, b1, c1, d1, w5, 6);
R42(e2, a2, b2, c2, d2, w7, 5);
R41(d1, e1, a1, b1, c1, w6, 5);
R42(d2, e2, a2, b2, c2, w10, 15);
R41(c1, d1, e1, a1, b1, w2, 12);
R42(c2, d2, e2, a2, b2, w14, 8);
R51(b1, c1, d1, e1, a1, w4, 9);
R52(b2, c2, d2, e2, a2, w12, 8);
R51(a1, b1, c1, d1, e1, w0, 15);
R52(a2, b2, c2, d2, e2, w15, 5);
R51(e1, a1, b1, c1, d1, w5, 5);
R52(e2, a2, b2, c2, d2, w10, 12);
R51(d1, e1, a1, b1, c1, w9, 11);
R52(d2, e2, a2, b2, c2, w4, 9);
R51(c1, d1, e1, a1, b1, w7, 6);
R52(c2, d2, e2, a2, b2, w1, 12);
R51(b1, c1, d1, e1, a1, w12, 8);
R52(b2, c2, d2, e2, a2, w5, 5);
R51(a1, b1, c1, d1, e1, w2, 13);
R52(a2, b2, c2, d2, e2, w8, 14);
R51(e1, a1, b1, c1, d1, w10, 12);
R52(e2, a2, b2, c2, d2, w7, 6);
R51(d1, e1, a1, b1, c1, w14, 5);
R52(d2, e2, a2, b2, c2, w6, 8);
R51(c1, d1, e1, a1, b1, w1, 12);
R52(c2, d2, e2, a2, b2, w2, 13);
R51(b1, c1, d1, e1, a1, w3, 13);
R52(b2, c2, d2, e2, a2, w13, 6);
R51(a1, b1, c1, d1, e1, w8, 14);
R52(a2, b2, c2, d2, e2, w14, 5);
R51(e1, a1, b1, c1, d1, w11, 11);
R52(e2, a2, b2, c2, d2, w0, 15);
R51(d1, e1, a1, b1, c1, w6, 8);
R52(d2, e2, a2, b2, c2, w3, 13);
R51(c1, d1, e1, a1, b1, w15, 5);
R52(c2, d2, e2, a2, b2, w9, 11);
R51(b1, c1, d1, e1, a1, w13, 6);
R52(b2, c2, d2, e2, a2, w11, 11);
uint32_t t = s[0];
s[0] = s[1] + c1 + d2;
s[1] = s[2] + d1 + e2;
s[2] = s[3] + e1 + a2;
s[3] = s[4] + a1 + b2;
s[4] = t + b1 + c2;
}
} // namespace ripemd160
} // namespace
////// RIPEMD160
CRIPEMD160::CRIPEMD160() { ripemd160::Initialize(s); }
CRIPEMD160 &CRIPEMD160::Write(const unsigned char *data, size_t len) {
const unsigned char *end = data + len;
size_t bufsize = bytes % 64;
if (bufsize && bufsize + len >= 64) {
// Fill the buffer, and process it.
memcpy(buf + bufsize, data, 64 - bufsize);
bytes += 64 - bufsize;
data += 64 - bufsize;
ripemd160::Transform(s, buf);
bufsize = 0;
}
while (end - data >= 64) {
// Process full chunks directly from the source.
ripemd160::Transform(s, data);
bytes += 64;
data += 64;
}
if (end > data) {
// Fill the buffer with what remains.
memcpy(buf + bufsize, data, end - data);
bytes += end - data;
}
return *this;
}
void CRIPEMD160::Finalize(unsigned char hash[OUTPUT_SIZE]) {
static const unsigned char pad[64] = {0x80};
unsigned char sizedesc[8];
WriteLE64(sizedesc, bytes << 3);
Write(pad, 1 + ((119 - (bytes % 64)) % 64));
Write(sizedesc, 8);
WriteLE32(hash, s[0]);
WriteLE32(hash + 4, s[1]);
WriteLE32(hash + 8, s[2]);
WriteLE32(hash + 12, s[3]);
WriteLE32(hash + 16, s[4]);
}
CRIPEMD160 &CRIPEMD160::Reset() {
bytes = 0;
ripemd160::Initialize(s);
return *this;
}

View File

@ -0,0 +1,27 @@
// Copyright (c) 2014-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CRYPTO_RIPEMD160_H
#define BITCOIN_CRYPTO_RIPEMD160_H
#include <cstdlib>
#include <stdint.h>
/** A hasher class for RIPEMD-160. */
class CRIPEMD160 {
private:
uint32_t s[5];
unsigned char buf[64];
uint64_t bytes{0};
public:
static const size_t OUTPUT_SIZE = 20;
CRIPEMD160();
CRIPEMD160 &Write(const unsigned char *data, size_t len);
void Finalize(unsigned char hash[OUTPUT_SIZE]);
CRIPEMD160 &Reset();
};
#endif // BITCOIN_CRYPTO_RIPEMD160_H

55
src/i2p/crypto/sha256.h Normal file
View File

@ -0,0 +1,55 @@
// Copyright (c) 2014-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CRYPTO_SHA256_H
#define BITCOIN_CRYPTO_SHA256_H
#include <cstdlib>
#include <stdint.h>
#include <string>
/** A hasher class for SHA-256. */
class CSHA256 {
private:
uint32_t s[8];
unsigned char buf[64];
uint64_t bytes{0};
public:
static const size_t OUTPUT_SIZE = 32;
CSHA256();
CSHA256 &Write(const unsigned char *data, size_t len);
void Finalize(unsigned char hash[OUTPUT_SIZE]);
CSHA256 &Reset();
};
namespace sha256_implementation {
enum UseImplementation : uint8_t {
STANDARD = 0,
USE_SSE4 = 1 << 0,
USE_AVX2 = 1 << 1,
USE_SHANI = 1 << 2,
USE_SSE4_AND_AVX2 = USE_SSE4 | USE_AVX2,
USE_SSE4_AND_SHANI = USE_SSE4 | USE_SHANI,
USE_ALL = USE_SSE4 | USE_AVX2 | USE_SHANI,
};
}
/** Autodetect the best available SHA256 implementation.
* Returns the name of the implementation.
*/
std::string
SHA256AutoDetect(sha256_implementation::UseImplementation use_implementation =
sha256_implementation::USE_ALL);
/** Compute multiple double-SHA256's of 64-byte blobs.
* output: pointer to a blocks*32 byte output buffer
* input: pointer to a blocks*64 byte input buffer
* blocks: the number of hashes to compute.
*/
void SHA256D64(unsigned char *output, const unsigned char *input,
size_t blocks);
#endif // BITCOIN_CRYPTO_SHA256_H

245
src/i2p/crypto/sha3.cpp Normal file
View File

@ -0,0 +1,245 @@
// Copyright (c) 2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// Based on https://github.com/mjosaarinen/tiny_sha3/blob/master/sha3.c
// by Markku-Juhani O. Saarinen <mjos@iki.fi>
#include "../crypto/sha3.h"
#include "../crypto/common.h"
#include "../span.h"
#include <algorithm>
#include <array> // For std::begin and std::end.
#include <bit>
#include <stdint.h>
void KeccakF(uint64_t (&st)[25]) {
static constexpr uint64_t RNDC[24] = {
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
0x8000000000008080, 0x0000000080000001, 0x8000000080008008};
static constexpr int ROUNDS = 24;
for (int round = 0; round < ROUNDS; ++round) {
uint64_t bc0, bc1, bc2, bc3, bc4, t;
// Theta
bc0 = st[0] ^ st[5] ^ st[10] ^ st[15] ^ st[20];
bc1 = st[1] ^ st[6] ^ st[11] ^ st[16] ^ st[21];
bc2 = st[2] ^ st[7] ^ st[12] ^ st[17] ^ st[22];
bc3 = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23];
bc4 = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24];
t = bc4 ^ std::rotl(bc1, 1);
st[0] ^= t;
st[5] ^= t;
st[10] ^= t;
st[15] ^= t;
st[20] ^= t;
t = bc0 ^ std::rotl(bc2, 1);
st[1] ^= t;
st[6] ^= t;
st[11] ^= t;
st[16] ^= t;
st[21] ^= t;
t = bc1 ^ std::rotl(bc3, 1);
st[2] ^= t;
st[7] ^= t;
st[12] ^= t;
st[17] ^= t;
st[22] ^= t;
t = bc2 ^ std::rotl(bc4, 1);
st[3] ^= t;
st[8] ^= t;
st[13] ^= t;
st[18] ^= t;
st[23] ^= t;
t = bc3 ^ std::rotl(bc0, 1);
st[4] ^= t;
st[9] ^= t;
st[14] ^= t;
st[19] ^= t;
st[24] ^= t;
// Rho Pi
t = st[1];
bc0 = st[10];
st[10] = std::rotl(t, 1);
t = bc0;
bc0 = st[7];
st[7] = std::rotl(t, 3);
t = bc0;
bc0 = st[11];
st[11] = std::rotl(t, 6);
t = bc0;
bc0 = st[17];
st[17] = std::rotl(t, 10);
t = bc0;
bc0 = st[18];
st[18] = std::rotl(t, 15);
t = bc0;
bc0 = st[3];
st[3] = std::rotl(t, 21);
t = bc0;
bc0 = st[5];
st[5] = std::rotl(t, 28);
t = bc0;
bc0 = st[16];
st[16] = std::rotl(t, 36);
t = bc0;
bc0 = st[8];
st[8] = std::rotl(t, 45);
t = bc0;
bc0 = st[21];
st[21] = std::rotl(t, 55);
t = bc0;
bc0 = st[24];
st[24] = std::rotl(t, 2);
t = bc0;
bc0 = st[4];
st[4] = std::rotl(t, 14);
t = bc0;
bc0 = st[15];
st[15] = std::rotl(t, 27);
t = bc0;
bc0 = st[23];
st[23] = std::rotl(t, 41);
t = bc0;
bc0 = st[19];
st[19] = std::rotl(t, 56);
t = bc0;
bc0 = st[13];
st[13] = std::rotl(t, 8);
t = bc0;
bc0 = st[12];
st[12] = std::rotl(t, 25);
t = bc0;
bc0 = st[2];
st[2] = std::rotl(t, 43);
t = bc0;
bc0 = st[20];
st[20] = std::rotl(t, 62);
t = bc0;
bc0 = st[14];
st[14] = std::rotl(t, 18);
t = bc0;
bc0 = st[22];
st[22] = std::rotl(t, 39);
t = bc0;
bc0 = st[9];
st[9] = std::rotl(t, 61);
t = bc0;
bc0 = st[6];
st[6] = std::rotl(t, 20);
t = bc0;
st[1] = std::rotl(t, 44);
// Chi Iota
bc0 = st[0];
bc1 = st[1];
bc2 = st[2];
bc3 = st[3];
bc4 = st[4];
st[0] = bc0 ^ (~bc1 & bc2) ^ RNDC[round];
st[1] = bc1 ^ (~bc2 & bc3);
st[2] = bc2 ^ (~bc3 & bc4);
st[3] = bc3 ^ (~bc4 & bc0);
st[4] = bc4 ^ (~bc0 & bc1);
bc0 = st[5];
bc1 = st[6];
bc2 = st[7];
bc3 = st[8];
bc4 = st[9];
st[5] = bc0 ^ (~bc1 & bc2);
st[6] = bc1 ^ (~bc2 & bc3);
st[7] = bc2 ^ (~bc3 & bc4);
st[8] = bc3 ^ (~bc4 & bc0);
st[9] = bc4 ^ (~bc0 & bc1);
bc0 = st[10];
bc1 = st[11];
bc2 = st[12];
bc3 = st[13];
bc4 = st[14];
st[10] = bc0 ^ (~bc1 & bc2);
st[11] = bc1 ^ (~bc2 & bc3);
st[12] = bc2 ^ (~bc3 & bc4);
st[13] = bc3 ^ (~bc4 & bc0);
st[14] = bc4 ^ (~bc0 & bc1);
bc0 = st[15];
bc1 = st[16];
bc2 = st[17];
bc3 = st[18];
bc4 = st[19];
st[15] = bc0 ^ (~bc1 & bc2);
st[16] = bc1 ^ (~bc2 & bc3);
st[17] = bc2 ^ (~bc3 & bc4);
st[18] = bc3 ^ (~bc4 & bc0);
st[19] = bc4 ^ (~bc0 & bc1);
bc0 = st[20];
bc1 = st[21];
bc2 = st[22];
bc3 = st[23];
bc4 = st[24];
st[20] = bc0 ^ (~bc1 & bc2);
st[21] = bc1 ^ (~bc2 & bc3);
st[22] = bc2 ^ (~bc3 & bc4);
st[23] = bc3 ^ (~bc4 & bc0);
st[24] = bc4 ^ (~bc0 & bc1);
}
}
SHA3_256 &SHA3_256::Write(Span<const unsigned char> data) {
if (m_bufsize && m_bufsize + data.size() >= sizeof(m_buffer)) {
// Fill the buffer and process it.
std::copy(data.begin(), data.begin() + sizeof(m_buffer) - m_bufsize,
m_buffer + m_bufsize);
data = data.subspan(sizeof(m_buffer) - m_bufsize);
m_state[m_pos++] ^= ReadLE64(m_buffer);
m_bufsize = 0;
if (m_pos == RATE_BUFFERS) {
KeccakF(m_state);
m_pos = 0;
}
}
while (data.size() >= sizeof(m_buffer)) {
// Process chunks directly from the buffer.
m_state[m_pos++] ^= ReadLE64(data.data());
data = data.subspan(8);
if (m_pos == RATE_BUFFERS) {
KeccakF(m_state);
m_pos = 0;
}
}
if (data.size()) {
// Keep the remainder in the buffer.
std::copy(data.begin(), data.end(), m_buffer + m_bufsize);
m_bufsize += data.size();
}
return *this;
}
SHA3_256 &SHA3_256::Finalize(Span<unsigned char> output) {
assert(output.size() == OUTPUT_SIZE);
std::fill(m_buffer + m_bufsize, m_buffer + sizeof(m_buffer), 0);
m_buffer[m_bufsize] ^= 0x06;
m_state[m_pos] ^= ReadLE64(m_buffer);
m_state[RATE_BUFFERS - 1] ^= 0x8000000000000000;
KeccakF(m_state);
for (unsigned i = 0; i < 4; ++i) {
WriteLE64(output.data() + 8 * i, m_state[i]);
}
return *this;
}
SHA3_256 &SHA3_256::Reset() {
m_bufsize = 0;
m_pos = 0;
std::fill(std::begin(m_state), std::end(m_state), 0);
return *this;
}

41
src/i2p/crypto/sha3.h Normal file
View File

@ -0,0 +1,41 @@
// Copyright (c) 2020-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CRYPTO_SHA3_H
#define BITCOIN_CRYPTO_SHA3_H
#include "../span.h"
#include <cstdlib>
#include <stdint.h>
//! The Keccak-f[1600] transform.
void KeccakF(uint64_t (&st)[25]);
class SHA3_256 {
private:
uint64_t m_state[25] = {0};
unsigned char m_buffer[8];
unsigned m_bufsize = 0;
unsigned m_pos = 0;
//! Sponge rate in bits.
static constexpr unsigned RATE_BITS = 1088;
//! Sponge rate expressed as a multiple of the buffer size.
static constexpr unsigned RATE_BUFFERS = RATE_BITS / (8 * sizeof(m_buffer));
static_assert(RATE_BITS % (8 * sizeof(m_buffer)) == 0,
"Rate must be a multiple of 8 bytes");
public:
static constexpr size_t OUTPUT_SIZE = 32;
SHA3_256() {}
SHA3_256 &Write(Span<const unsigned char> data);
SHA3_256 &Finalize(Span<unsigned char> output);
SHA3_256 &Reset();
};
#endif // BITCOIN_CRYPTO_SHA3_H

289
src/i2p/crypto/sha512.cpp Normal file
View File

@ -0,0 +1,289 @@
// Copyright (c) 2014-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "sha512.h"
#include "common.h"
#include <string.h>
// Internal implementation code.
namespace {
/// Internal SHA-512 implementation.
namespace sha512 {
uint64_t inline Ch(uint64_t x, uint64_t y, uint64_t z) {
return z ^ (x & (y ^ z));
}
uint64_t inline Maj(uint64_t x, uint64_t y, uint64_t z) {
return (x & y) | (z & (x | y));
}
uint64_t inline Sigma0(uint64_t x) {
return (x >> 28 | x << 36) ^ (x >> 34 | x << 30) ^ (x >> 39 | x << 25);
}
uint64_t inline Sigma1(uint64_t x) {
return (x >> 14 | x << 50) ^ (x >> 18 | x << 46) ^ (x >> 41 | x << 23);
}
uint64_t inline sigma0(uint64_t x) {
return (x >> 1 | x << 63) ^ (x >> 8 | x << 56) ^ (x >> 7);
}
uint64_t inline sigma1(uint64_t x) {
return (x >> 19 | x << 45) ^ (x >> 61 | x << 3) ^ (x >> 6);
}
/** One round of SHA-512. */
void inline Round(uint64_t a, uint64_t b, uint64_t c, uint64_t &d, uint64_t e,
uint64_t f, uint64_t g, uint64_t &h, uint64_t k, uint64_t w) {
uint64_t t1 = h + Sigma1(e) + Ch(e, f, g) + k + w;
uint64_t t2 = Sigma0(a) + Maj(a, b, c);
d += t1;
h = t1 + t2;
}
/** Initialize SHA-512 state. */
void inline Initialize(uint64_t *s) {
s[0] = 0x6a09e667f3bcc908ull;
s[1] = 0xbb67ae8584caa73bull;
s[2] = 0x3c6ef372fe94f82bull;
s[3] = 0xa54ff53a5f1d36f1ull;
s[4] = 0x510e527fade682d1ull;
s[5] = 0x9b05688c2b3e6c1full;
s[6] = 0x1f83d9abfb41bd6bull;
s[7] = 0x5be0cd19137e2179ull;
}
/** Perform one SHA-512 transformation, processing a 128-byte chunk. */
void Transform(uint64_t *s, const unsigned char *chunk) {
uint64_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6],
h = s[7];
uint64_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15;
Round(a, b, c, d, e, f, g, h, 0x428a2f98d728ae22ull,
w0 = ReadBE64(chunk + 0));
Round(h, a, b, c, d, e, f, g, 0x7137449123ef65cdull,
w1 = ReadBE64(chunk + 8));
Round(g, h, a, b, c, d, e, f, 0xb5c0fbcfec4d3b2full,
w2 = ReadBE64(chunk + 16));
Round(f, g, h, a, b, c, d, e, 0xe9b5dba58189dbbcull,
w3 = ReadBE64(chunk + 24));
Round(e, f, g, h, a, b, c, d, 0x3956c25bf348b538ull,
w4 = ReadBE64(chunk + 32));
Round(d, e, f, g, h, a, b, c, 0x59f111f1b605d019ull,
w5 = ReadBE64(chunk + 40));
Round(c, d, e, f, g, h, a, b, 0x923f82a4af194f9bull,
w6 = ReadBE64(chunk + 48));
Round(b, c, d, e, f, g, h, a, 0xab1c5ed5da6d8118ull,
w7 = ReadBE64(chunk + 56));
Round(a, b, c, d, e, f, g, h, 0xd807aa98a3030242ull,
w8 = ReadBE64(chunk + 64));
Round(h, a, b, c, d, e, f, g, 0x12835b0145706fbeull,
w9 = ReadBE64(chunk + 72));
Round(g, h, a, b, c, d, e, f, 0x243185be4ee4b28cull,
w10 = ReadBE64(chunk + 80));
Round(f, g, h, a, b, c, d, e, 0x550c7dc3d5ffb4e2ull,
w11 = ReadBE64(chunk + 88));
Round(e, f, g, h, a, b, c, d, 0x72be5d74f27b896full,
w12 = ReadBE64(chunk + 96));
Round(d, e, f, g, h, a, b, c, 0x80deb1fe3b1696b1ull,
w13 = ReadBE64(chunk + 104));
Round(c, d, e, f, g, h, a, b, 0x9bdc06a725c71235ull,
w14 = ReadBE64(chunk + 112));
Round(b, c, d, e, f, g, h, a, 0xc19bf174cf692694ull,
w15 = ReadBE64(chunk + 120));
Round(a, b, c, d, e, f, g, h, 0xe49b69c19ef14ad2ull,
w0 += sigma1(w14) + w9 + sigma0(w1));
Round(h, a, b, c, d, e, f, g, 0xefbe4786384f25e3ull,
w1 += sigma1(w15) + w10 + sigma0(w2));
Round(g, h, a, b, c, d, e, f, 0x0fc19dc68b8cd5b5ull,
w2 += sigma1(w0) + w11 + sigma0(w3));
Round(f, g, h, a, b, c, d, e, 0x240ca1cc77ac9c65ull,
w3 += sigma1(w1) + w12 + sigma0(w4));
Round(e, f, g, h, a, b, c, d, 0x2de92c6f592b0275ull,
w4 += sigma1(w2) + w13 + sigma0(w5));
Round(d, e, f, g, h, a, b, c, 0x4a7484aa6ea6e483ull,
w5 += sigma1(w3) + w14 + sigma0(w6));
Round(c, d, e, f, g, h, a, b, 0x5cb0a9dcbd41fbd4ull,
w6 += sigma1(w4) + w15 + sigma0(w7));
Round(b, c, d, e, f, g, h, a, 0x76f988da831153b5ull,
w7 += sigma1(w5) + w0 + sigma0(w8));
Round(a, b, c, d, e, f, g, h, 0x983e5152ee66dfabull,
w8 += sigma1(w6) + w1 + sigma0(w9));
Round(h, a, b, c, d, e, f, g, 0xa831c66d2db43210ull,
w9 += sigma1(w7) + w2 + sigma0(w10));
Round(g, h, a, b, c, d, e, f, 0xb00327c898fb213full,
w10 += sigma1(w8) + w3 + sigma0(w11));
Round(f, g, h, a, b, c, d, e, 0xbf597fc7beef0ee4ull,
w11 += sigma1(w9) + w4 + sigma0(w12));
Round(e, f, g, h, a, b, c, d, 0xc6e00bf33da88fc2ull,
w12 += sigma1(w10) + w5 + sigma0(w13));
Round(d, e, f, g, h, a, b, c, 0xd5a79147930aa725ull,
w13 += sigma1(w11) + w6 + sigma0(w14));
Round(c, d, e, f, g, h, a, b, 0x06ca6351e003826full,
w14 += sigma1(w12) + w7 + sigma0(w15));
Round(b, c, d, e, f, g, h, a, 0x142929670a0e6e70ull,
w15 += sigma1(w13) + w8 + sigma0(w0));
Round(a, b, c, d, e, f, g, h, 0x27b70a8546d22ffcull,
w0 += sigma1(w14) + w9 + sigma0(w1));
Round(h, a, b, c, d, e, f, g, 0x2e1b21385c26c926ull,
w1 += sigma1(w15) + w10 + sigma0(w2));
Round(g, h, a, b, c, d, e, f, 0x4d2c6dfc5ac42aedull,
w2 += sigma1(w0) + w11 + sigma0(w3));
Round(f, g, h, a, b, c, d, e, 0x53380d139d95b3dfull,
w3 += sigma1(w1) + w12 + sigma0(w4));
Round(e, f, g, h, a, b, c, d, 0x650a73548baf63deull,
w4 += sigma1(w2) + w13 + sigma0(w5));
Round(d, e, f, g, h, a, b, c, 0x766a0abb3c77b2a8ull,
w5 += sigma1(w3) + w14 + sigma0(w6));
Round(c, d, e, f, g, h, a, b, 0x81c2c92e47edaee6ull,
w6 += sigma1(w4) + w15 + sigma0(w7));
Round(b, c, d, e, f, g, h, a, 0x92722c851482353bull,
w7 += sigma1(w5) + w0 + sigma0(w8));
Round(a, b, c, d, e, f, g, h, 0xa2bfe8a14cf10364ull,
w8 += sigma1(w6) + w1 + sigma0(w9));
Round(h, a, b, c, d, e, f, g, 0xa81a664bbc423001ull,
w9 += sigma1(w7) + w2 + sigma0(w10));
Round(g, h, a, b, c, d, e, f, 0xc24b8b70d0f89791ull,
w10 += sigma1(w8) + w3 + sigma0(w11));
Round(f, g, h, a, b, c, d, e, 0xc76c51a30654be30ull,
w11 += sigma1(w9) + w4 + sigma0(w12));
Round(e, f, g, h, a, b, c, d, 0xd192e819d6ef5218ull,
w12 += sigma1(w10) + w5 + sigma0(w13));
Round(d, e, f, g, h, a, b, c, 0xd69906245565a910ull,
w13 += sigma1(w11) + w6 + sigma0(w14));
Round(c, d, e, f, g, h, a, b, 0xf40e35855771202aull,
w14 += sigma1(w12) + w7 + sigma0(w15));
Round(b, c, d, e, f, g, h, a, 0x106aa07032bbd1b8ull,
w15 += sigma1(w13) + w8 + sigma0(w0));
Round(a, b, c, d, e, f, g, h, 0x19a4c116b8d2d0c8ull,
w0 += sigma1(w14) + w9 + sigma0(w1));
Round(h, a, b, c, d, e, f, g, 0x1e376c085141ab53ull,
w1 += sigma1(w15) + w10 + sigma0(w2));
Round(g, h, a, b, c, d, e, f, 0x2748774cdf8eeb99ull,
w2 += sigma1(w0) + w11 + sigma0(w3));
Round(f, g, h, a, b, c, d, e, 0x34b0bcb5e19b48a8ull,
w3 += sigma1(w1) + w12 + sigma0(w4));
Round(e, f, g, h, a, b, c, d, 0x391c0cb3c5c95a63ull,
w4 += sigma1(w2) + w13 + sigma0(w5));
Round(d, e, f, g, h, a, b, c, 0x4ed8aa4ae3418acbull,
w5 += sigma1(w3) + w14 + sigma0(w6));
Round(c, d, e, f, g, h, a, b, 0x5b9cca4f7763e373ull,
w6 += sigma1(w4) + w15 + sigma0(w7));
Round(b, c, d, e, f, g, h, a, 0x682e6ff3d6b2b8a3ull,
w7 += sigma1(w5) + w0 + sigma0(w8));
Round(a, b, c, d, e, f, g, h, 0x748f82ee5defb2fcull,
w8 += sigma1(w6) + w1 + sigma0(w9));
Round(h, a, b, c, d, e, f, g, 0x78a5636f43172f60ull,
w9 += sigma1(w7) + w2 + sigma0(w10));
Round(g, h, a, b, c, d, e, f, 0x84c87814a1f0ab72ull,
w10 += sigma1(w8) + w3 + sigma0(w11));
Round(f, g, h, a, b, c, d, e, 0x8cc702081a6439ecull,
w11 += sigma1(w9) + w4 + sigma0(w12));
Round(e, f, g, h, a, b, c, d, 0x90befffa23631e28ull,
w12 += sigma1(w10) + w5 + sigma0(w13));
Round(d, e, f, g, h, a, b, c, 0xa4506cebde82bde9ull,
w13 += sigma1(w11) + w6 + sigma0(w14));
Round(c, d, e, f, g, h, a, b, 0xbef9a3f7b2c67915ull,
w14 += sigma1(w12) + w7 + sigma0(w15));
Round(b, c, d, e, f, g, h, a, 0xc67178f2e372532bull,
w15 += sigma1(w13) + w8 + sigma0(w0));
Round(a, b, c, d, e, f, g, h, 0xca273eceea26619cull,
w0 += sigma1(w14) + w9 + sigma0(w1));
Round(h, a, b, c, d, e, f, g, 0xd186b8c721c0c207ull,
w1 += sigma1(w15) + w10 + sigma0(w2));
Round(g, h, a, b, c, d, e, f, 0xeada7dd6cde0eb1eull,
w2 += sigma1(w0) + w11 + sigma0(w3));
Round(f, g, h, a, b, c, d, e, 0xf57d4f7fee6ed178ull,
w3 += sigma1(w1) + w12 + sigma0(w4));
Round(e, f, g, h, a, b, c, d, 0x06f067aa72176fbaull,
w4 += sigma1(w2) + w13 + sigma0(w5));
Round(d, e, f, g, h, a, b, c, 0x0a637dc5a2c898a6ull,
w5 += sigma1(w3) + w14 + sigma0(w6));
Round(c, d, e, f, g, h, a, b, 0x113f9804bef90daeull,
w6 += sigma1(w4) + w15 + sigma0(w7));
Round(b, c, d, e, f, g, h, a, 0x1b710b35131c471bull,
w7 += sigma1(w5) + w0 + sigma0(w8));
Round(a, b, c, d, e, f, g, h, 0x28db77f523047d84ull,
w8 += sigma1(w6) + w1 + sigma0(w9));
Round(h, a, b, c, d, e, f, g, 0x32caab7b40c72493ull,
w9 += sigma1(w7) + w2 + sigma0(w10));
Round(g, h, a, b, c, d, e, f, 0x3c9ebe0a15c9bebcull,
w10 += sigma1(w8) + w3 + sigma0(w11));
Round(f, g, h, a, b, c, d, e, 0x431d67c49c100d4cull,
w11 += sigma1(w9) + w4 + sigma0(w12));
Round(e, f, g, h, a, b, c, d, 0x4cc5d4becb3e42b6ull,
w12 += sigma1(w10) + w5 + sigma0(w13));
Round(d, e, f, g, h, a, b, c, 0x597f299cfc657e2aull,
w13 += sigma1(w11) + w6 + sigma0(w14));
Round(c, d, e, f, g, h, a, b, 0x5fcb6fab3ad6faecull,
w14 + sigma1(w12) + w7 + sigma0(w15));
Round(b, c, d, e, f, g, h, a, 0x6c44198c4a475817ull,
w15 + sigma1(w13) + w8 + sigma0(w0));
s[0] += a;
s[1] += b;
s[2] += c;
s[3] += d;
s[4] += e;
s[5] += f;
s[6] += g;
s[7] += h;
}
} // namespace sha512
} // namespace
////// SHA-512
CSHA512::CSHA512() { sha512::Initialize(s); }
CSHA512 &CSHA512::Write(const unsigned char *data, size_t len) {
const unsigned char *end = data + len;
size_t bufsize = bytes % 128;
if (bufsize && bufsize + len >= 128) {
// Fill the buffer, and process it.
memcpy(buf + bufsize, data, 128 - bufsize);
bytes += 128 - bufsize;
data += 128 - bufsize;
sha512::Transform(s, buf);
bufsize = 0;
}
while (end - data >= 128) {
// Process full chunks directly from the source.
sha512::Transform(s, data);
data += 128;
bytes += 128;
}
if (end > data) {
// Fill the buffer with what remains.
memcpy(buf + bufsize, data, end - data);
bytes += end - data;
}
return *this;
}
void CSHA512::Finalize(unsigned char hash[OUTPUT_SIZE]) {
static const unsigned char pad[128] = {0x80};
unsigned char sizedesc[16] = {0x00};
WriteBE64(sizedesc + 8, bytes << 3);
Write(pad, 1 + ((239 - (bytes % 128)) % 128));
Write(sizedesc, 16);
WriteBE64(hash, s[0]);
WriteBE64(hash + 8, s[1]);
WriteBE64(hash + 16, s[2]);
WriteBE64(hash + 24, s[3]);
WriteBE64(hash + 32, s[4]);
WriteBE64(hash + 40, s[5]);
WriteBE64(hash + 48, s[6]);
WriteBE64(hash + 56, s[7]);
}
CSHA512 &CSHA512::Reset() {
bytes = 0;
sha512::Initialize(s);
return *this;
}

28
src/i2p/crypto/sha512.h Normal file
View File

@ -0,0 +1,28 @@
// Copyright (c) 2014-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CRYPTO_SHA512_H
#define BITCOIN_CRYPTO_SHA512_H
#include <cstdlib>
#include <stdint.h>
/** A hasher class for SHA-512. */
class CSHA512 {
private:
uint64_t s[8];
unsigned char buf[128];
uint64_t bytes{0};
public:
static constexpr size_t OUTPUT_SIZE = 64;
CSHA512();
CSHA512 &Write(const unsigned char *data, size_t len);
void Finalize(unsigned char hash[OUTPUT_SIZE]);
CSHA512 &Reset();
uint64_t Size() const { return bytes; }
};
#endif // BITCOIN_CRYPTO_SHA512_H

48
src/i2p/crypto/siphash.h Normal file
View File

@ -0,0 +1,48 @@
// Copyright (c) 2016-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CRYPTO_SIPHASH_H
#define BITCOIN_CRYPTO_SIPHASH_H
#include <stdint.h>
#include "../span.h"
#include "../uint256.h"
/** SipHash-2-4 */
class CSipHasher
{
private:
uint64_t v[4];
uint64_t tmp;
uint8_t count; // Only the low 8 bits of the input size matter.
public:
/** Construct a SipHash calculator initialized with 128-bit key (k0, k1) */
CSipHasher(uint64_t k0, uint64_t k1);
/** Hash a 64-bit integer worth of data
* It is treated as if this was the little-endian interpretation of 8 bytes.
* This function can only be used when a multiple of 8 bytes have been written so far.
*/
CSipHasher& Write(uint64_t data);
/** Hash arbitrary bytes. */
CSipHasher& Write(Span<const unsigned char> data);
/** Compute the 64-bit SipHash-2-4 of the data written so far. The object remains untouched. */
uint64_t Finalize() const;
};
/** Optimized SipHash-2-4 implementation for uint256.
*
* It is identical to:
* SipHasher(k0, k1)
* .Write(val.GetUint64(0))
* .Write(val.GetUint64(1))
* .Write(val.GetUint64(2))
* .Write(val.GetUint64(3))
* .Finalize()
*/
uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val);
uint64_t SipHashUint256Extra(uint64_t k0, uint64_t k1, const uint256& val, uint32_t extra);
#endif // BITCOIN_CRYPTO_SIPHASH_H

98
src/i2p/hash.cpp Normal file
View File

@ -0,0 +1,98 @@
// Copyright (c) 2013-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "hash.h"
#include "crypto/common.h"
#include "crypto/hmac_sha512.h"
#include "span.h"
#include <bit>
#include <string>
unsigned int MurmurHash3(unsigned int nHashSeed,
Span<const unsigned char> vDataToHash) {
// The following is MurmurHash3 (x86_32), see
// https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp
uint32_t h1 = nHashSeed;
const uint32_t c1 = 0xcc9e2d51;
const uint32_t c2 = 0x1b873593;
const int nblocks = vDataToHash.size() / 4;
//----------
// body
const uint8_t *blocks = vDataToHash.data();
for (int i = 0; i < nblocks; ++i) {
uint32_t k1 = ReadLE32(blocks + i * 4);
k1 *= c1;
k1 = std::rotl(k1, 15);
k1 *= c2;
h1 ^= k1;
h1 = std::rotl(h1, 13);
h1 = h1 * 5 + 0xe6546b64;
}
//----------
// tail
const uint8_t *tail = vDataToHash.data() + nblocks * 4;
uint32_t k1 = 0;
switch (vDataToHash.size() & 3) {
case 3:
k1 ^= tail[2] << 16;
[[fallthrough]];
case 2:
k1 ^= tail[1] << 8;
[[fallthrough]];
case 1:
k1 ^= tail[0];
k1 *= c1;
k1 = std::rotl(k1, 15);
k1 *= c2;
h1 ^= k1;
}
//----------
// finalization
h1 ^= vDataToHash.size();
h1 ^= h1 >> 16;
h1 *= 0x85ebca6b;
h1 ^= h1 >> 13;
h1 *= 0xc2b2ae35;
h1 ^= h1 >> 16;
return h1;
}
void BIP32Hash(const ChainCode &chainCode, unsigned int nChild,
unsigned char header, const unsigned char data[32],
unsigned char output[64]) {
unsigned char num[4];
WriteBE32(num, nChild);
CHMAC_SHA512(chainCode.begin(), chainCode.size())
.Write(&header, 1)
.Write(data, 32)
.Write(num, 4)
.Finalize(output);
}
uint256 SHA256Uint256(const uint256 &input) {
uint256 result;
CSHA256().Write(input.begin(), 32).Finalize(result.begin());
return result;
}
HashWriter TaggedHash(const std::string &tag) {
HashWriter writer{};
uint256 taghash;
CSHA256()
.Write((const unsigned char *)tag.data(), tag.size())
.Finalize(taghash.begin());
writer << taghash << taghash;
return writer;
}

221
src/i2p/hash.h Normal file
View File

@ -0,0 +1,221 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_HASH_H
#define BITCOIN_HASH_H
#include "attributes.h"
#include "crypto/common.h"
#include "crypto/ripemd160.h"
#include "crypto/sha256.h"
#include "prevector.h"
#include "serialize.h"
#include "span.h"
#include "uint256.h"
#include <string>
#include <vector>
typedef uint256 ChainCode;
/** A hasher class for Bitcoin's 256-bit hash (double SHA-256). */
class CHash256 {
private:
CSHA256 sha;
public:
static const size_t OUTPUT_SIZE = CSHA256::OUTPUT_SIZE;
void Finalize(Span<unsigned char> output) {
assert(output.size() == OUTPUT_SIZE);
unsigned char buf[CSHA256::OUTPUT_SIZE];
sha.Finalize(buf);
sha.Reset().Write(buf, CSHA256::OUTPUT_SIZE).Finalize(output.data());
}
CHash256 &Write(Span<const unsigned char> input) {
sha.Write(input.data(), input.size());
return *this;
}
CHash256 &Reset() {
sha.Reset();
return *this;
}
};
/** A hasher class for Bitcoin's 160-bit hash (SHA-256 + RIPEMD-160). */
class CHash160 {
private:
CSHA256 sha;
public:
static const size_t OUTPUT_SIZE = CRIPEMD160::OUTPUT_SIZE;
void Finalize(Span<unsigned char> output) {
assert(output.size() == OUTPUT_SIZE);
unsigned char buf[CSHA256::OUTPUT_SIZE];
sha.Finalize(buf);
CRIPEMD160().Write(buf, CSHA256::OUTPUT_SIZE).Finalize(output.data());
}
CHash160 &Write(Span<const unsigned char> input) {
sha.Write(input.data(), input.size());
return *this;
}
CHash160 &Reset() {
sha.Reset();
return *this;
}
};
/** Compute the 256-bit hash of an object. */
template <typename T> inline uint256 Hash(const T &in1) {
uint256 result;
CHash256().Write(MakeUCharSpan(in1)).Finalize(result);
return result;
}
/** Compute the 256-bit hash of the concatenation of two objects. */
template <typename T1, typename T2>
inline uint256 Hash(const T1 &in1, const T2 &in2) {
uint256 result;
CHash256()
.Write(MakeUCharSpan(in1))
.Write(MakeUCharSpan(in2))
.Finalize(result);
return result;
}
/** Compute the 160-bit hash an object. */
template <typename T1> inline uint160 Hash160(const T1 &in1) {
uint160 result;
CHash160().Write(MakeUCharSpan(in1)).Finalize(result);
return result;
}
/** A writer stream (for serialization) that computes a 256-bit hash. */
class HashWriter {
private:
CSHA256 ctx;
public:
void write(Span<const std::byte> src) {
ctx.Write(UCharCast(src.data()), src.size());
}
/** Compute the double-SHA256 hash of all data written to this object.
*
* Invalidates this object.
*/
uint256 GetHash() {
uint256 result;
ctx.Finalize(result.begin());
ctx.Reset()
.Write(result.begin(), CSHA256::OUTPUT_SIZE)
.Finalize(result.begin());
return result;
}
/** Compute the SHA256 hash of all data written to this object.
*
* Invalidates this object.
*/
uint256 GetSHA256() {
uint256 result;
ctx.Finalize(result.begin());
return result;
}
/**
* Returns the first 64 bits from the resulting hash.
*/
inline uint64_t GetCheapHash() {
uint256 result = GetHash();
return ReadLE64(result.begin());
}
template <typename T> HashWriter &operator<<(const T &obj) {
::Serialize(*this, obj);
return *this;
}
};
/** Reads data from an underlying stream, while hashing the read data. */
template <typename Source> class HashVerifier : public HashWriter {
private:
Source &m_source;
public:
explicit HashVerifier(Source &source LIFETIMEBOUND) : m_source{source} {}
void read(Span<std::byte> dst) {
m_source.read(dst);
this->write(dst);
}
void ignore(size_t num_bytes) {
std::byte data[1024];
while (num_bytes > 0) {
size_t now = std::min<size_t>(num_bytes, 1024);
read({data, now});
num_bytes -= now;
}
}
template <typename T> HashVerifier<Source> &operator>>(T &&obj) {
::Unserialize(*this, obj);
return *this;
}
};
/** Writes data to an underlying source stream, while hashing the written data.
*/
template <typename Source> class HashedSourceWriter : public HashWriter {
private:
Source &m_source;
public:
explicit HashedSourceWriter(Source &source LIFETIMEBOUND)
: HashWriter{}, m_source{source} {}
void write(Span<const std::byte> src) {
m_source.write(src);
HashWriter::write(src);
}
template <typename T> HashedSourceWriter &operator<<(const T &obj) {
::Serialize(*this, obj);
return *this;
}
};
/** Single-SHA256 a 32-byte input (represented as uint256). */
[[nodiscard]] uint256 SHA256Uint256(const uint256 &input);
unsigned int MurmurHash3(unsigned int nHashSeed,
Span<const unsigned char> vDataToHash);
void BIP32Hash(const ChainCode &chainCode, unsigned int nChild,
unsigned char header, const unsigned char data[32],
unsigned char output[64]);
/** Return a HashWriter primed for tagged hashes (as specified in BIP 340).
*
* The returned object will have SHA256(tag) written to it twice (= 64 bytes).
* A tagged hash can be computed by feeding the message into this object, and
* then calling HashWriter::GetSHA256().
*/
HashWriter TaggedHash(const std::string &tag);
/** Compute the 160-bit RIPEMD-160 hash of an array. */
inline uint160 RIPEMD160(Span<const unsigned char> data) {
uint160 result;
CRIPEMD160().Write(data.data(), data.size()).Finalize(result.begin());
return result;
}
#endif // BITCOIN_HASH_H

529
src/i2p/i2p.cpp Normal file
View File

@ -0,0 +1,529 @@
// Copyright (c) 2020-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "i2p.h"
// #include "chainparams.h"
// #include "common/args.h"
// #include "compat/compat.h"
#include "compat/endian.h"
#include "crypto/sha256.h"
#include "easylogging++.h"
#include "misc_log_ex.h"
#include "netaddress.h"
#include "netbase.h"
#include "random.h"
#include "sync.h"
#include "tinyformat.h"
#include "util/fs.h"
#include "util/readwritefile.h"
#include "util/sock.h"
#include "util/spanparsing.h"
#include "util/strencodings.h"
#include "util/threadinterrupt.h"
#include <chrono>
#include <memory>
#include <stdexcept>
#include <string>
namespace i2p {
/**
* Swap Standard Base64 <-> I2P Base64.
* Standard Base64 uses `+` and `/` as last two characters of its alphabet.
* I2P Base64 uses `-` and `~` respectively.
* So it is easy to detect in which one is the input and convert to the other.
* @param[in] from Input to convert.
* @return converted `from`
*/
/* quik notes area
* required classes:
* DecodeBase64(util/strencodings.h)
* tfm::format(tinyformat.h)v
* CNetAddr(netaddress.h)
* CSHA256(crypto/sha256.h")v
* EncodeBase32(util/strencodings.h)
* Proxy(netbase.h)
* CThreadInterrupt(util/threadinterrupt.h)
* LOCK(sync.h)
* AssertLockNotHeld(sync.h)
* Sock(util/sock.h)
* MAX_WAIT_FOR_IO(util/sock.h)
* CService(netaddress.h)
* Split(util/spanparsing.h)
* Span(span.h)
* WriteBinaryFile(util/readwritefile.h)
* quoted(fs.h)
* PathToString(fs.h)
* be16toh_internal(compat/endian.h)
* ReadBinaryFile(util/readwritefile.h)
* EncodeBase64(util/strencodings.h)
* I2P_SAM31_PORT(netaddress.h)
* Capitalize(util/strencodings.h)
* ToStringAddrPort(netaddress.h)
* LogPrint(logging.h)x
*/
// preland: this is my "checked line". nothing below this is checked.
static std::string SwapBase64(const std::string &from) {
std::string to;
to.resize(from.size());
for (size_t i = 0; i < from.size(); ++i) {
switch (from[i]) {
case '-':
to[i] = '+';
break;
case '~':
to[i] = '/';
break;
case '+':
to[i] = '-';
break;
case '/':
to[i] = '~';
break;
default:
to[i] = from[i];
break;
}
}
return to;
}
/**
* Decode an I2P-style Base64 string.
* @param[in] i2p_b64 I2P-style Base64 string.
* @return decoded `i2p_b64`
* @throw std::runtime_error if decoding fails
*/
static Binary DecodeI2PBase64(const std::string &i2p_b64) {
const std::string &std_b64 = SwapBase64(i2p_b64);
auto decoded = DecodeBase64(std_b64);
if (!decoded) {
throw std::runtime_error(
tfm::format("Cannot decode Base64: \"%s\"", i2p_b64));
}
return std::move(*decoded);
}
/**
* Derive the .b32.i2p address of an I2P destination (binary).
* @param[in] dest I2P destination.
* @return the address that corresponds to `dest`
* @throw std::runtime_error if conversion fails
*/
static CNetAddr DestBinToAddr(const Binary &dest) {
CSHA256 hasher;
hasher.Write(dest.data(), dest.size());
unsigned char hash[CSHA256::OUTPUT_SIZE];
hasher.Finalize(hash);
CNetAddr addr;
const std::string addr_str = EncodeBase32(hash, false) + ".b32.i2p";
if (!addr.SetSpecial(addr_str)) {
throw std::runtime_error(
tfm::format("Cannot parse I2P address: \"%s\"", addr_str));
}
return addr;
}
/**
* Derive the .b32.i2p address of an I2P destination (I2P-style Base64).
* @param[in] dest I2P destination.
* @return the address that corresponds to `dest`
* @throw std::runtime_error if conversion fails
*/
static CNetAddr DestB64ToAddr(const std::string &dest) {
const Binary &decoded = DecodeI2PBase64(dest);
return DestBinToAddr(decoded);
}
namespace sam {
Session::Session(const fs::path &private_key_file, const Proxy &control_host,
CThreadInterrupt *interrupt)
: m_private_key_file{private_key_file}, m_control_host{control_host},
m_interrupt{interrupt}, m_transient{false} {}
Session::Session(const Proxy &control_host, CThreadInterrupt *interrupt)
: m_control_host{control_host}, m_interrupt{interrupt}, m_transient{true} {}
Session::~Session() {
LOCK(m_mutex);
Disconnect();
}
bool Session::Listen(Connection &conn) {
try {
LOCK(m_mutex);
CreateIfNotCreatedAlready();
conn.me = m_my_addr;
conn.sock = StreamAccept();
return true;
} catch (const std::runtime_error &e) {
Log("Error listening: %s", e.what());
CheckControlSock();
}
return false;
}
bool Session::Accept(Connection &conn) {
AssertLockNotHeld(m_mutex);
std::string errmsg;
bool disconnect{false};
while (!*m_interrupt) {
Sock::Event occurred;
if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
errmsg = "wait on socket failed";
break;
}
if (occurred == 0) {
// Timeout, no incoming connections or errors within MAX_WAIT_FOR_IO.
continue;
}
std::string peer_dest;
try {
peer_dest = conn.sock->RecvUntilTerminator('\n', MAX_WAIT_FOR_IO,
*m_interrupt, MAX_MSG_SIZE);
} catch (const std::runtime_error &e) {
errmsg = e.what();
break;
}
CNetAddr peer_addr;
try {
peer_addr = DestB64ToAddr(peer_dest);
} catch (const std::runtime_error &e) {
// The I2P router is expected to send the Base64 of the connecting peer,
// but it may happen that something like this is sent instead:
// STREAM STATUS RESULT=I2P_ERROR MESSAGE="Session was closed"
// In that case consider the session damaged and close it right away,
// even if the control socket is alive.
if (peer_dest.find("RESULT=I2P_ERROR") != std::string::npos) {
errmsg = tfm::format(
"unexpected reply that hints the session is unusable: %s",
peer_dest);
disconnect = true;
} else {
errmsg = e.what();
}
break;
}
conn.peer = CService(peer_addr, I2P_SAM31_PORT);
return true;
}
Log("Error accepting%s: %s", disconnect ? " (will close the session)" : "",
errmsg);
if (disconnect) {
LOCK(m_mutex);
Disconnect();
} else {
CheckControlSock();
}
return false;
}
bool Session::Connect(const CService &to, Connection &conn, bool &proxy_error) {
// Refuse connecting to arbitrary ports. We don't specify any destination
// port to the SAM proxy
// when connecting (SAM 3.1 does not use ports) and it forces/defaults it
// to I2P_SAM31_PORT.
if (to.GetPort() != I2P_SAM31_PORT) {
Log("Error connecting to % s, connection refused due to arbitrary port % "
"s ",
to.ToStringAddrPort(), to.GetPort());
proxy_error = false;
return false;
}
proxy_error = true;
std::string session_id;
std::unique_ptr<Sock> sock;
conn.peer = to;
try {
{
LOCK(m_mutex);
CreateIfNotCreatedAlready();
session_id = m_session_id;
conn.me = m_my_addr;
sock = Hello();
}
const Reply &lookup_reply = SendRequestAndGetReply(
*sock, tfm::format("NAMING LOOKUP NAME=%s", to.ToStringAddr()));
const std::string &dest = lookup_reply.Get("VALUE");
const Reply &connect_reply = SendRequestAndGetReply(
*sock,
tfm::format("STREAM CONNECT ID=%s DESTINATION=%s SILENT=false",
session_id, dest),
false);
const std::string &result = connect_reply.Get("RESULT");
if (result == "OK") {
conn.sock = std::move(sock);
return true;
}
if (result == "INVALID_ID") {
LOCK(m_mutex);
Disconnect();
throw std::runtime_error("Invalid session id");
}
if (result == "CANT_REACH_PEER" || result == "TIMEOUT") {
proxy_error = false;
}
throw std::runtime_error(tfm::format("\"%s\"", connect_reply.full));
} catch (const std::runtime_error &e) {
Log("Error connecting to %s: %s", to.ToStringAddrPort(), e.what());
CheckControlSock();
return false;
}
}
// Private methods
std::string Session::Reply::Get(const std::string &key) const {
const auto &pos = keys.find(key);
if (pos == keys.end() || !pos->second.has_value()) {
throw std::runtime_error(tfm::format(
"Missing %s= in the reply to \"%s\": \"%s\"", key, request, full));
}
return pos->second.value();
}
template <typename... Args>
void Session::Log(const std::string &fmt, const Args &...args) const {
// LogPrint(BCLog::I2P, "%s\n", tfm::format(fmt, args...));
MLOG_GREEN(el::Level::Info, tfm::format(fmt, args...));
}
Session::Reply Session::SendRequestAndGetReply(const Sock &sock,
const std::string &request,
bool check_result_ok) const {
sock.SendComplete(request + "\n", MAX_WAIT_FOR_IO, *m_interrupt);
Reply reply;
// Don't log the full "SESSION CREATE ..." because it contains our private
// key.
reply.request = request.substr(0, 14) == "SESSION CREATE"
? "SESSION CREATE ... "
: request;
// It could take a few minutes for the I2P router to reply as it is
// querying
// the I2P network
// (when doing name lookup, for example). Notice:
// `RecvUntilTerminator()` is
// checking
// `m_interrupt` more often, so we would not be stuck here for long if
// `m_interrupt` is
// signaled.
static constexpr auto recv_timeout = 3min;
reply.full =
sock.RecvUntilTerminator('\n', recv_timeout, *m_interrupt, MAX_MSG_SIZE);
for (const auto &kv : spanparsing::Split(reply.full, ' ')) {
const auto &pos = std::find(kv.begin(), kv.end(), '=');
if (pos != kv.end()) {
reply.keys.emplace(std::string{kv.begin(), pos},
std::string{pos + 1, kv.end()});
} else {
reply.keys.emplace(std::string{kv.begin(), kv.end()}, std::nullopt);
}
}
if (check_result_ok && reply.Get("RESULT") != "OK") {
throw std::runtime_error(
tfm::format("Unexpected reply to \"%s\": \"%s\"", request, reply.full));
}
return reply;
}
std::unique_ptr<Sock> Session::Hello() const {
auto sock = m_control_host.Connect();
if (!sock) {
throw std::runtime_error(
tfm::format("Cannot connect to %s", m_control_host.ToString()));
}
SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1");
return sock;
}
void Session::CheckControlSock() {
LOCK(m_mutex);
std::string errmsg;
if (m_control_sock && !m_control_sock->IsConnected(errmsg)) {
Log("Control socket error: %s", errmsg);
Disconnect();
}
}
void Session::DestGenerate(const Sock &sock) {
// https://geti2p.net/spec/common-structures#key-certificates
// "7" or "EdDSA_SHA512_Ed25519" - "Recent Router Identities and
// Destinations".
// Use "7" because i2pd <2.24.0 does not recognize the textual form.
// If SIGNATURE_TYPE is not specified, then the default one is DSA_SHA1.
const Reply &reply =
SendRequestAndGetReply(sock, "DEST GENERATE SIGNATURE_TYPE=7", false);
m_private_key = DecodeI2PBase64(reply.Get("PRIV"));
}
void Session::GenerateAndSavePrivateKey(const Sock &sock) {
DestGenerate(sock);
// umask is set to 0077 in common/system.cpp, which is ok.
if (!WriteBinaryFile(m_private_key_file, std::string(m_private_key.begin(),
m_private_key.end()))) {
throw std::runtime_error(
tfm::format("Cannot save I2P private key to %s",
fs::quoted(fs::PathToString(m_private_key_file))));
}
}
Binary Session::MyDestination() const {
// From https://geti2p.net/spec/common-structures#destination:
// "They are 387 bytes plus the certificate length specified at bytes
// 385 - 386, which may be
// non-zero"
static constexpr size_t DEST_LEN_BASE = 387;
static constexpr size_t CERT_LEN_POS = 385;
uint16_t cert_len;
if (m_private_key.size() < CERT_LEN_POS + sizeof(cert_len)) {
throw std::runtime_error(
tfm::format("The private key is too short (%d < %d)",
m_private_key.size(), CERT_LEN_POS + sizeof(cert_len)));
}
memcpy(&cert_len, &m_private_key.at(CERT_LEN_POS), sizeof(cert_len));
cert_len = be16toh_internal(cert_len);
const size_t dest_len = DEST_LEN_BASE + cert_len;
if (dest_len > m_private_key.size()) {
throw std::runtime_error(tfm::format(
"Certificate length (%d) designates that the private key should "
" be % d bytes, but it is only % d bytes ",
cert_len, dest_len, m_private_key.size()));
}
return Binary{m_private_key.begin(), m_private_key.begin() + dest_len};
}
void Session::CreateIfNotCreatedAlready() {
std::string errmsg;
if (m_control_sock && m_control_sock->IsConnected(errmsg)) {
return;
}
const auto session_type = m_transient ? "transient" : "persistent";
const auto session_id = GetRandHash().GetHex().substr(
0, 10); // full is overkill, too verbose in the logs
Log("Creating %s SAM session %s with %s", session_type, session_id,
m_control_host.ToString());
auto sock = Hello();
if (m_transient) {
// The destination (private key) is generated upon session creation and
// returned
// in the reply in DESTINATION=.
const Reply &reply = SendRequestAndGetReply(
*sock,
tfm::format(
"SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT "
"SIGNATURE_TYPE=7 "
"i2cp.leaseSetEncType=4,0 inbound.quantity=1 outbound.quantity=1",
session_id));
m_private_key = DecodeI2PBase64(reply.Get("DESTINATION"));
} else {
// Read our persistent destination (private key) from disk or generate
// one and save it to disk. Then use it when creating the session.
const auto &[read_ok, data] = ReadBinaryFile(m_private_key_file);
if (read_ok) {
m_private_key.assign(data.begin(), data.end());
} else {
GenerateAndSavePrivateKey(*sock);
}
const std::string &private_key_b64 =
SwapBase64(EncodeBase64(m_private_key));
SendRequestAndGetReply(
*sock,
tfm::format(
"SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s "
"i2cp.leaseSetEncType=4,0 inbound.quantity=3 outbound.quantity=3",
session_id, private_key_b64));
}
m_my_addr = CService(DestBinToAddr(MyDestination()), I2P_SAM31_PORT);
m_session_id = session_id;
m_control_sock = std::move(sock);
Log("%s SAM session %s created, my address=%s", Capitalize(session_type),
m_session_id, m_my_addr.ToStringAddrPort());
}
std::unique_ptr<Sock> Session::StreamAccept() {
auto sock = Hello();
const Reply &reply = SendRequestAndGetReply(
*sock, tfm::format("STREAM ACCEPT ID=%s SILENT=false", m_session_id),
false);
const std::string &result = reply.Get("RESULT");
if (result == "OK") {
return sock;
}
if (result == "INVALID_ID") {
// If our session id is invalid, then force session re-creation on next
// usage.
Disconnect();
}
throw std::runtime_error(tfm::format("\"%s\"", reply.full));
}
void Session::Disconnect() {
if (m_control_sock) {
if (m_session_id.empty()) {
Log("Destroying incomplete SAM session");
} else {
Log("Destroying SAM session %s", m_session_id);
}
m_control_sock.reset();
}
m_session_id.clear();
}
} // namespace sam
} // namespace i2p

314
src/i2p/i2p.h Normal file
View File

@ -0,0 +1,314 @@
// Copyright (c) 2020-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_I2P_H
#define BITCOIN_I2P_H
// #include "compat/compat.h"
#include "netaddress.h"
#include "netbase.h"
#include "sync.h"
#include "util/fs.h"
#include "util/sock.h"
#include "util/threadinterrupt.h"
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
// #include "misc_log_ex.h"
/*
* quik notes area:
*
* required classes:
* Sock(util/sock.h)
* CService(netaddress.h)
* path(fs.h)
* Proxy(netbase.h)
* CThreadInterrupt(util/threadinterrupt.h)
* EXCLUSIVE_LOCKS_REQUIRED(sync.h)
* GUARDED_BY(sync.h)
* Mutex(sync.h)
*/
namespace i2p {
/**
* Binary data.
*/
// preland: this is my "checked line". nothing below this is checked.
using Binary = std::vector<uint8_t>;
/**
* An established connection with another peer.
*/
struct Connection {
/** Connected socket. */
std::unique_ptr<Sock> sock;
/** Our I2P address. */
CService me;
/** The peer's I2P address. */
CService peer;
};
namespace sam {
/**
* The maximum size of an incoming message from the I2P SAM proxy (in bytes).
* Used to avoid a runaway proxy from sending us an "unlimited" amount of data
* without a terminator. The longest known message is ~1400 bytes, so this is
* high enough not to be triggered during normal operation, yet low enough to
* avoid a malicious proxy from filling our memory.
*/
static constexpr size_t MAX_MSG_SIZE{65536};
/**
* I2P SAM session.
*/
class Session {
public:
/**
* Construct a session. This will not initiate any IO, the session will be
* lazily created later when first used.
* @param[in] private_key_file Path to a private key file. If the file does
* not exist then the private key will be generated and saved into the file.
* @param[in] control_host Location of the SAM proxy.
* @param[in,out] interrupt If this is signaled then all operations are
* canceled as soon as possible and executing methods throw an exception.
* Notice: only a pointer to the `CThreadInterrupt` object is saved, so it
* must not be destroyed earlier than this `Session` object.
*/
Session(const fs::path &private_key_file, const Proxy &control_host,
CThreadInterrupt *interrupt);
/**
* Construct a transient session which will generate its own I2P private key
* rather than read the one from disk (it will not be saved on disk either and
* will be lost once this object is destroyed). This will not initiate any IO,
* the session will be lazily created later when first used.
* @param[in] control_host Location of the SAM proxy.
* @param[in,out] interrupt If this is signaled then all operations are
* canceled as soon as possible and executing methods throw an exception.
* Notice: only a pointer to the `CThreadInterrupt` object is saved, so it
* must not be destroyed earlier than this `Session` object.
*/
Session(const Proxy &control_host, CThreadInterrupt *interrupt);
/**
* Destroy the session, closing the internally used sockets. The sockets that
* have been returned by `Accept()` or `Connect()` will not be closed, but
* they will be closed by the SAM proxy because the session is destroyed. So
* they will return an error next time we try to read or write to them.
*/
~Session();
/**
* Start listening for an incoming connection.
* @param[out] conn Upon successful completion the `sock` and `me` members
* will be set to the listening socket and address.
* @return true on success
*/
bool Listen(Connection &conn) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
/**
* Wait for and accept a new incoming connection.
* @param[in,out] conn The `sock` member is used for waiting and accepting.
* Upon successful completion the `peer` member will be set to the address of
* the incoming peer.
* @return true on success
*/
bool Accept(Connection &conn) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
/**
* Connect to an I2P peer.
* @param[in] to Peer to connect to.
* @param[out] conn Established connection. Only set if `true` is returned.
* @param[out] proxy_error If an error occurs due to proxy or general network
* failure, then this is set to `true`. If an error occurs due to unreachable
* peer (likely peer is down), then it is set to `false`. Only set if `false`
* is returned.
* @return true on success
*/
bool Connect(const CService &to, Connection &conn, bool &proxy_error)
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
private:
/**
* A reply from the SAM proxy.
*/
struct Reply {
/**
* Full, unparsed reply.
*/
std::string full;
/**
* Request, used for detailed error reporting.
*/
std::string request;
/**
* A map of keywords from the parsed reply.
* For example, if the reply is "A=X B C=YZ", then the map will be
* keys["A"] == "X"
* keys["B"] == (empty std::optional)
* keys["C"] == "YZ"
*/
std::unordered_map<std::string, std::optional<std::string>> keys;
/**
* Get the value of a given key.
* For example if the reply is "A=X B" then:
* Value("A") -> "X"
* Value("B") -> throws
* Value("C") -> throws
* @param[in] key Key whose value to retrieve
* @returns the key's value
* @throws std::runtime_error if the key is not present or if it has no
* value
*/
std::string Get(const std::string &key) const;
};
/**
* Log a message in the `BCLog::I2P` category.
* @param[in] fmt printf(3)-like format string.
* @param[in] args printf(3)-like arguments that correspond to `fmt`.
*/
template <typename... Args>
void Log(const std::string &fmt, const Args &...args) const;
/**
* Send request and get a reply from the SAM proxy.
* @param[in] sock A socket that is connected to the SAM proxy.
* @param[in] request Raw request to send, a newline terminator is appended to
* it.
* @param[in] check_result_ok If true then after receiving the reply a check
* is made whether it contains "RESULT=OK" and an exception is thrown if it
* does not.
* @throws std::runtime_error if an error occurs
*/
Reply SendRequestAndGetReply(const Sock &sock, const std::string &request,
bool check_result_ok = true) const;
/**
* Open a new connection to the SAM proxy.
* @return a connected socket
* @throws std::runtime_error if an error occurs
*/
std::unique_ptr<Sock> Hello() const EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
/**
* Check the control socket for errors and possibly disconnect.
*/
void CheckControlSock() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
/**
* Generate a new destination with the SAM proxy and set `m_private_key` to
* it.
* @param[in] sock Socket to use for talking to the SAM proxy.
* @throws std::runtime_error if an error occurs
*/
void DestGenerate(const Sock &sock) EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
/**
* Generate a new destination with the SAM proxy, set `m_private_key` to it
* and save it on disk to `m_private_key_file`.
* @param[in] sock Socket to use for talking to the SAM proxy.
* @throws std::runtime_error if an error occurs
*/
void GenerateAndSavePrivateKey(const Sock &sock)
EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
/**
* Derive own destination from `m_private_key`.
* @see https://geti2p.net/spec/common-structures#destination
* @return an I2P destination
*/
Binary MyDestination() const EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
/**
* Create the session if not already created. Reads the private key file and
* connects to the SAM proxy.
* @throws std::runtime_error if an error occurs
*/
void CreateIfNotCreatedAlready() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
/**
* Open a new connection to the SAM proxy and issue "STREAM ACCEPT" request
* using the existing session id.
* @return the idle socket that is waiting for a peer to connect to us
* @throws std::runtime_error if an error occurs
*/
std::unique_ptr<Sock> StreamAccept() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
/**
* Destroy the session, closing the internally used sockets.
*/
void Disconnect() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
/**
* The name of the file where this peer's private key is stored (in binary).
*/
const fs::path m_private_key_file;
/**
* The SAM control service proxy.
*/
const Proxy m_control_host;
/**
* Cease network activity when this is signaled.
*/
CThreadInterrupt *const m_interrupt;
/**
* Mutex protecting the members that can be concurrently accessed.
*/
mutable Mutex m_mutex;
/**
* The private key of this peer.
* @see The reply to the "DEST GENERATE" command in
* https://geti2p.net/en/docs/api/samv3
*/
Binary m_private_key GUARDED_BY(m_mutex);
/**
* SAM control socket.
* Used to connect to the I2P SAM service and create a session
* ("SESSION CREATE"). With the established session id we later open
* other connections to the SAM service to accept incoming I2P
* connections and make outgoing ones.
* If not connected then this unique_ptr will be empty.
* See https://geti2p.net/en/docs/api/samv3
*/
std::unique_ptr<Sock> m_control_sock GUARDED_BY(m_mutex);
/**
* Our .b32.i2p address.
* Derived from `m_private_key`.
*/
CService m_my_addr GUARDED_BY(m_mutex);
/**
* SAM session id.
*/
std::string m_session_id GUARDED_BY(m_mutex);
/**
* Whether this is a transient session (the I2P private key will not be
* read or written to disk).
*/
const bool m_transient;
};
} // namespace sam
} // namespace i2p
#endif // BITCOIN_I2P_H

43
src/i2p/kernel/chain.h Normal file
View File

@ -0,0 +1,43 @@
// Copyright (c) 2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_KERNEL_CHAIN_H
#define BITCOIN_KERNEL_CHAIN_H
#include <iostream>
class CBlock;
class CBlockIndex;
namespace interfaces {
struct BlockInfo;
} // namespace interfaces
namespace kernel {
//! Return data from block index.
interfaces::BlockInfo MakeBlockInfo(const CBlockIndex *block_index,
const CBlock *data = nullptr);
} // namespace kernel
//! This enum describes the various roles a specific Chainstate instance can
//! take. Other parts of the system sometimes need to vary in behavior depending
//! on the existence of a background validation chainstate, e.g. when building
//! indexes.
enum class ChainstateRole {
// Single chainstate in use, "normal" IBD mode.
NORMAL,
// Doing IBD-style validation in the background. Implies use of an
// assumed-valid
// chainstate.
BACKGROUND,
// Active assumed-valid chainstate. Implies use of a background IBD
// chainstate.
ASSUMEDVALID,
};
std::ostream &operator<<(std::ostream &os, const ChainstateRole &role);
#endif // BITCOIN_KERNEL_CHAIN_H

View File

@ -0,0 +1,198 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_KERNEL_CHAINPARAMS_H
#define BITCOIN_KERNEL_CHAINPARAMS_H
#include "../consensus/params.h"
#include "../primitives/block.h"
#include "../uint256.h"
#include "../util/chaintype.h"
#include "../util/hash_type.h"
#include "../util/vector.h"
#include "messagestartchars.h"
#include <cstdint>
#include <iterator>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
typedef std::map<int, uint256> MapCheckpoints;
struct CCheckpointData {
MapCheckpoints mapCheckpoints;
int GetHeight() const {
const auto &final_checkpoint = mapCheckpoints.rbegin();
return final_checkpoint->first /* height */;
}
};
struct AssumeutxoHash : public BaseHash<uint256> {
explicit AssumeutxoHash(const uint256 &hash) : BaseHash(hash) {}
};
/**
* Holds configuration for use during UTXO snapshot load and validation. The
* contents here are security critical, since they dictate which UTXO snapshots
* are recognized as valid.
*/
struct AssumeutxoData {
int height;
//! The expected hash of the deserialized UTXO set.
AssumeutxoHash hash_serialized;
//! Used to populate the nChainTx value, which is used during
//! BlockManager::LoadBlockIndex().
//!
//! We need to hardcode the value here because this is computed cumulatively
//! using block data, which we do not necessarily have at the time of snapshot
//! load.
unsigned int nChainTx;
//! The hash of the base block for this snapshot. Used to refer to assumeutxo
//! data prior to having a loaded blockindex.
uint256 blockhash;
};
/**
* Holds various statistics on transactions within a chain. Used to estimate
* verification progress during chain sync.
*
* See also: CChainParams::TxData, GuessVerificationProgress.
*/
struct ChainTxData {
int64_t nTime; //!< UNIX timestamp of last known number of transactions
int64_t nTxCount; //!< total number of transactions between genesis and that
//!< timestamp
double dTxRate; //!< estimated number of transactions per second after that
//!< timestamp
};
/**
* CChainParams defines various tweakable parameters of a given instance of the
* Bitcoin system.
*/
class CChainParams {
public:
enum Base58Type {
PUBKEY_ADDRESS,
SCRIPT_ADDRESS,
SECRET_KEY,
EXT_PUBLIC_KEY,
EXT_SECRET_KEY,
MAX_BASE58_TYPES
};
const Consensus::Params &GetConsensus() const { return consensus; }
const MessageStartChars &MessageStart() const { return pchMessageStart; }
uint16_t GetDefaultPort() const { return nDefaultPort; }
const CBlock &GenesisBlock() const { return genesis; }
/** Default value for -checkmempool and -checkblockindex argument */
bool DefaultConsistencyChecks() const { return fDefaultConsistencyChecks; }
/** If this chain is exclusively used for testing */
bool IsTestChain() const { return m_chain_type != ChainType::MAIN; }
/** If this chain allows time to be mocked */
bool IsMockableChain() const { return m_is_mockable_chain; }
uint64_t PruneAfterHeight() const { return nPruneAfterHeight; }
/** Minimum free space (in GB) needed for data directory */
uint64_t AssumedBlockchainSize() const { return m_assumed_blockchain_size; }
/** Minimum free space (in GB) needed for data directory when pruned; Does not
* include prune target*/
uint64_t AssumedChainStateSize() const { return m_assumed_chain_state_size; }
/** Whether it is possible to mine blocks on demand (no retargeting) */
bool MineBlocksOnDemand() const { return consensus.fPowNoRetargeting; }
/** Return the chain type string */
std::string GetChainTypeString() const {
return ChainTypeToString(m_chain_type);
}
/** Return the chain type */
ChainType GetChainType() const { return m_chain_type; }
/** Return the list of hostnames to look up for DNS seeds */
const std::vector<std::string> &DNSSeeds() const { return vSeeds; }
const std::vector<unsigned char> &Base58Prefix(Base58Type type) const {
return base58Prefixes[type];
}
const std::string &Bech32HRP() const { return bech32_hrp; }
const std::vector<uint8_t> &FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData &Checkpoints() const { return checkpointData; }
std::optional<AssumeutxoData> AssumeutxoForHeight(int height) const {
return FindFirst(m_assumeutxo_data,
[&](const auto &d) { return d.height == height; });
}
std::optional<AssumeutxoData>
AssumeutxoForBlockhash(const uint256 &blockhash) const {
return FindFirst(m_assumeutxo_data,
[&](const auto &d) { return d.blockhash == blockhash; });
}
const ChainTxData &TxData() const { return chainTxData; }
/**
* SigNetOptions holds configurations for creating a signet CChainParams.
*/
struct SigNetOptions {
std::optional<std::vector<uint8_t>> challenge{};
std::optional<std::vector<std::string>> seeds{};
};
/**
* VersionBitsParameters holds activation parameters
*/
struct VersionBitsParameters {
int64_t start_time;
int64_t timeout;
int min_activation_height;
};
/**
* RegTestOptions holds configurations for creating a regtest CChainParams.
*/
struct RegTestOptions {
std::unordered_map<Consensus::DeploymentPos, VersionBitsParameters>
version_bits_parameters{};
std::unordered_map<Consensus::BuriedDeployment, int> activation_heights{};
bool fastprune{false};
};
static std::unique_ptr<const CChainParams>
RegTest(const RegTestOptions &options);
static std::unique_ptr<const CChainParams>
SigNet(const SigNetOptions &options);
static std::unique_ptr<const CChainParams> Main();
static std::unique_ptr<const CChainParams> TestNet();
protected:
CChainParams() {}
Consensus::Params consensus;
MessageStartChars pchMessageStart;
uint16_t nDefaultPort;
uint64_t nPruneAfterHeight;
uint64_t m_assumed_blockchain_size;
uint64_t m_assumed_chain_state_size;
std::vector<std::string> vSeeds;
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
std::string bech32_hrp;
ChainType m_chain_type;
CBlock genesis;
std::vector<uint8_t> vFixedSeeds;
bool fDefaultConsistencyChecks;
bool m_is_mockable_chain;
CCheckpointData checkpointData;
std::vector<AssumeutxoData> m_assumeutxo_data;
ChainTxData chainTxData;
};
#endif // BITCOIN_KERNEL_CHAINPARAMS_H

View File

@ -0,0 +1,13 @@
// Copyright (c) 2023 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_KERNEL_MESSAGESTARTCHARS_H
#define BITCOIN_KERNEL_MESSAGESTARTCHARS_H
#include <array>
#include <cstdint>
using MessageStartChars = std::array<uint8_t, 4>;
#endif // BITCOIN_KERNEL_MESSAGESTARTCHARS_H

1081
src/i2p/netaddress.cpp Normal file

File diff suppressed because it is too large Load Diff

1181
src/i2p/netaddress.h Normal file

File diff suppressed because it is too large Load Diff

942
src/i2p/netbase.cpp Normal file
View File

@ -0,0 +1,942 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include "netbase.h"
#include "compat/compat.h"
#include "logging.h"
#include "sync.h"
#include "tinyformat.h"
#include "util/sock.h"
#include "util/strencodings.h"
#include "util/string.h"
#include "util/time.h"
#include <atomic>
#include <chrono>
#include <cstdint>
#include <functional>
#include <limits>
#include <memory>
#if HAVE_SOCKADDR_UN
#include <sys/un.h>
#endif
// Settings
static GlobalMutex g_proxyinfo_mutex;
static Proxy proxyInfo[NET_MAX] GUARDED_BY(g_proxyinfo_mutex);
static Proxy nameProxy GUARDED_BY(g_proxyinfo_mutex);
int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
bool fNameLookup = DEFAULT_NAME_LOOKUP;
// Need ample time for negotiation for very slow proxies such as Tor
std::chrono::milliseconds g_socks5_recv_timeout = 20s;
CThreadInterrupt g_socks5_interrupt;
ReachableNets g_reachable_nets;
std::vector<CNetAddr> WrappedGetAddrInfo(const std::string &name,
bool allow_lookup) {
addrinfo ai_hint{};
// We want a TCP port, which is a streaming socket type
ai_hint.ai_socktype = SOCK_STREAM;
ai_hint.ai_protocol = IPPROTO_TCP;
// We don't care which address family (IPv4 or IPv6) is returned
ai_hint.ai_family = AF_UNSPEC;
// If we allow lookups of hostnames, use the AI_ADDRCONFIG flag to only
// return addresses whose family we have an address configured for.
//
// If we don't allow lookups, then use the AI_NUMERICHOST flag for
// getaddrinfo to only decode numerical network addresses and suppress
// hostname lookups.
ai_hint.ai_flags = allow_lookup ? AI_ADDRCONFIG : AI_NUMERICHOST;
addrinfo *ai_res{nullptr};
const int n_err{getaddrinfo(name.c_str(), nullptr, &ai_hint, &ai_res)};
if (n_err != 0) {
return {};
}
// Traverse the linked list starting with ai_trav.
addrinfo *ai_trav{ai_res};
std::vector<CNetAddr> resolved_addresses;
while (ai_trav != nullptr) {
if (ai_trav->ai_family == AF_INET) {
assert(ai_trav->ai_addrlen >= sizeof(sockaddr_in));
resolved_addresses.emplace_back(
reinterpret_cast<sockaddr_in *>(ai_trav->ai_addr)->sin_addr);
}
if (ai_trav->ai_family == AF_INET6) {
assert(ai_trav->ai_addrlen >= sizeof(sockaddr_in6));
const sockaddr_in6 *s6{
reinterpret_cast<sockaddr_in6 *>(ai_trav->ai_addr)};
resolved_addresses.emplace_back(s6->sin6_addr, s6->sin6_scope_id);
}
ai_trav = ai_trav->ai_next;
}
freeaddrinfo(ai_res);
return resolved_addresses;
}
DNSLookupFn g_dns_lookup{WrappedGetAddrInfo};
enum Network ParseNetwork(const std::string &net_in) {
std::string net = ToLower(net_in);
if (net == "ipv4")
return NET_IPV4;
if (net == "ipv6")
return NET_IPV6;
if (net == "onion")
return NET_ONION;
if (net == "tor") {
LogPrintf("Warning: net name 'tor' is deprecated and will be removed in "
"the future. You should use 'onion' instead.\n");
return NET_ONION;
}
if (net == "i2p") {
return NET_I2P;
}
if (net == "cjdns") {
return NET_CJDNS;
}
return NET_UNROUTABLE;
}
std::string GetNetworkName(enum Network net) {
switch (net) {
case NET_UNROUTABLE:
return "not_publicly_routable";
case NET_IPV4:
return "ipv4";
case NET_IPV6:
return "ipv6";
case NET_ONION:
return "onion";
case NET_I2P:
return "i2p";
case NET_CJDNS:
return "cjdns";
case NET_INTERNAL:
return "internal";
case NET_MAX:
assert(false);
} // no default case, so the compiler can warn about missing cases
assert(false);
}
std::vector<std::string> GetNetworkNames(bool append_unroutable) {
std::vector<std::string> names;
for (int n = 0; n < NET_MAX; ++n) {
const enum Network network { static_cast<Network>(n) };
if (network == NET_UNROUTABLE || network == NET_INTERNAL)
continue;
names.emplace_back(GetNetworkName(network));
}
if (append_unroutable) {
names.emplace_back(GetNetworkName(NET_UNROUTABLE));
}
return names;
}
static std::vector<CNetAddr> LookupIntern(const std::string &name,
unsigned int nMaxSolutions,
bool fAllowLookup,
DNSLookupFn dns_lookup_function) {
if (!ContainsNoNUL(name))
return {};
{
CNetAddr addr;
// From our perspective, onion addresses are not hostnames but rather
// direct encodings of CNetAddr much like IPv4 dotted-decimal notation
// or IPv6 colon-separated hextet notation. Since we can't use
// getaddrinfo to decode them and it wouldn't make sense to resolve
// them, we return a network address representing it instead. See
// CNetAddr::SetSpecial(const std::string&) for more details.
if (addr.SetSpecial(name))
return {addr};
}
std::vector<CNetAddr> addresses;
for (const CNetAddr &resolved : dns_lookup_function(name, fAllowLookup)) {
if (nMaxSolutions > 0 && addresses.size() >= nMaxSolutions) {
break;
}
/* Never allow resolving to an internal address. Consider any such result
* invalid */
if (!resolved.IsInternal()) {
addresses.push_back(resolved);
}
}
return addresses;
}
std::vector<CNetAddr> LookupHost(const std::string &name,
unsigned int nMaxSolutions, bool fAllowLookup,
DNSLookupFn dns_lookup_function) {
if (!ContainsNoNUL(name))
return {};
std::string strHost = name;
if (strHost.empty())
return {};
if (strHost.front() == '[' && strHost.back() == ']') {
strHost = strHost.substr(1, strHost.size() - 2);
}
return LookupIntern(strHost, nMaxSolutions, fAllowLookup,
dns_lookup_function);
}
std::optional<CNetAddr> LookupHost(const std::string &name, bool fAllowLookup,
DNSLookupFn dns_lookup_function) {
const std::vector<CNetAddr> addresses{
LookupHost(name, 1, fAllowLookup, dns_lookup_function)};
return addresses.empty() ? std::nullopt
: std::make_optional(addresses.front());
}
std::vector<CService> Lookup(const std::string &name, uint16_t portDefault,
bool fAllowLookup, unsigned int nMaxSolutions,
DNSLookupFn dns_lookup_function) {
if (name.empty() || !ContainsNoNUL(name)) {
return {};
}
uint16_t port{portDefault};
std::string hostname;
SplitHostPort(name, port, hostname);
const std::vector<CNetAddr> addresses{
LookupIntern(hostname, nMaxSolutions, fAllowLookup, dns_lookup_function)};
if (addresses.empty())
return {};
std::vector<CService> services;
services.reserve(addresses.size());
for (const auto &addr : addresses)
services.emplace_back(addr, port);
return services;
}
std::optional<CService> Lookup(const std::string &name, uint16_t portDefault,
bool fAllowLookup,
DNSLookupFn dns_lookup_function) {
const std::vector<CService> services{
Lookup(name, portDefault, fAllowLookup, 1, dns_lookup_function)};
return services.empty() ? std::nullopt : std::make_optional(services.front());
}
CService LookupNumeric(const std::string &name, uint16_t portDefault,
DNSLookupFn dns_lookup_function) {
if (!ContainsNoNUL(name)) {
return {};
}
// "1.2:345" will fail to resolve the ip, but will still set the port.
// If the ip fails to resolve, re-init the result.
return Lookup(name, portDefault, /*fAllowLookup=*/false, dns_lookup_function)
.value_or(CService{});
}
bool IsUnixSocketPath(const std::string &name) {
#if HAVE_SOCKADDR_UN
if (name.find(ADDR_PREFIX_UNIX) != 0)
return false;
// Split off "unix:" prefix
std::string str{name.substr(ADDR_PREFIX_UNIX.length())};
// Path size limit is platform-dependent
// see https://manpages.ubuntu.com/manpages/xenial/en/man7/unix.7.html
if (str.size() + 1 > sizeof(((sockaddr_un *)nullptr)->sun_path))
return false;
return true;
#else
return false;
#endif
}
/** SOCKS version */
enum SOCKSVersion : uint8_t { SOCKS4 = 0x04, SOCKS5 = 0x05 };
/** Values defined for METHOD in RFC1928 */
enum SOCKS5Method : uint8_t {
NOAUTH = 0x00, //!< No authentication required
GSSAPI = 0x01, //!< GSSAPI
USER_PASS = 0x02, //!< Username/password
NO_ACCEPTABLE = 0xff, //!< No acceptable methods
};
/** Values defined for CMD in RFC1928 */
enum SOCKS5Command : uint8_t {
CONNECT = 0x01,
BIND = 0x02,
UDP_ASSOCIATE = 0x03
};
/** Values defined for REP in RFC1928 */
enum SOCKS5Reply : uint8_t {
SUCCEEDED = 0x00, //!< Succeeded
GENFAILURE = 0x01, //!< General failure
NOTALLOWED = 0x02, //!< Connection not allowed by ruleset
NETUNREACHABLE = 0x03, //!< Network unreachable
HOSTUNREACHABLE = 0x04, //!< Network unreachable
CONNREFUSED = 0x05, //!< Connection refused
TTLEXPIRED = 0x06, //!< TTL expired
CMDUNSUPPORTED = 0x07, //!< Command not supported
ATYPEUNSUPPORTED = 0x08, //!< Address type not supported
};
/** Values defined for ATYPE in RFC1928 */
enum SOCKS5Atyp : uint8_t {
IPV4 = 0x01,
DOMAINNAME = 0x03,
IPV6 = 0x04,
};
/** Status codes that can be returned by InterruptibleRecv */
enum class IntrRecvError {
OK,
Timeout,
Disconnected,
NetworkError,
Interrupted
};
/**
* Try to read a specified number of bytes from a socket. Please read the "see
* also" section for more detail.
*
* @param data The buffer where the read bytes should be stored.
* @param len The number of bytes to read into the specified buffer.
* @param timeout The total timeout for this read.
* @param sock The socket (has to be in non-blocking mode) from which to read
* bytes.
*
* @returns An IntrRecvError indicating the resulting status of this read.
* IntrRecvError::OK only if all of the specified number of bytes were
* read.
*
* @see This function can be interrupted by calling g_socks5_interrupt().
* Sockets can be made non-blocking with Sock::SetNonBlocking().
*/
static IntrRecvError InterruptibleRecv(uint8_t *data, size_t len,
std::chrono::milliseconds timeout,
const Sock &sock) {
auto curTime{Now<SteadyMilliseconds>()};
const auto endTime{curTime + timeout};
while (len > 0 && curTime < endTime) {
ssize_t ret = sock.Recv(data, len, 0); // Optimistically try the recv first
if (ret > 0) {
len -= ret;
data += ret;
} else if (ret == 0) { // Unexpected disconnection
return IntrRecvError::Disconnected;
} else { // Other error or blocking
int nErr = WSAGetLastError();
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK ||
nErr == WSAEINVAL) {
// Only wait at most MAX_WAIT_FOR_IO at a time, unless
// we're approaching the end of the specified total timeout
const auto remaining = std::chrono::milliseconds{endTime - curTime};
const auto timeout =
std::min(remaining, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
if (!sock.Wait(timeout, Sock::RECV)) {
return IntrRecvError::NetworkError;
}
} else {
return IntrRecvError::NetworkError;
}
}
if (g_socks5_interrupt) {
return IntrRecvError::Interrupted;
}
curTime = Now<SteadyMilliseconds>();
}
return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout;
}
/** Convert SOCKS5 reply to an error message */
static std::string Socks5ErrorString(uint8_t err) {
switch (err) {
case SOCKS5Reply::GENFAILURE:
return "general failure";
case SOCKS5Reply::NOTALLOWED:
return "connection not allowed";
case SOCKS5Reply::NETUNREACHABLE:
return "network unreachable";
case SOCKS5Reply::HOSTUNREACHABLE:
return "host unreachable";
case SOCKS5Reply::CONNREFUSED:
return "connection refused";
case SOCKS5Reply::TTLEXPIRED:
return "TTL expired";
case SOCKS5Reply::CMDUNSUPPORTED:
return "protocol error";
case SOCKS5Reply::ATYPEUNSUPPORTED:
return "address type not supported";
default:
return "unknown";
}
}
bool Socks5(const std::string &strDest, uint16_t port,
const ProxyCredentials *auth, const Sock &sock) {
try {
IntrRecvError recvr;
LogPrint(BCLog::NET, "SOCKS5 connecting %s\n", strDest);
if (strDest.size() > 255) {
LogError("Hostname too long\n");
return false;
}
// Construct the version identifier/method selection message
std::vector<uint8_t> vSocks5Init;
vSocks5Init.push_back(SOCKSVersion::SOCKS5); // We want the SOCK5 protocol
if (auth) {
vSocks5Init.push_back(0x02); // 2 method identifiers follow...
vSocks5Init.push_back(SOCKS5Method::NOAUTH);
vSocks5Init.push_back(SOCKS5Method::USER_PASS);
} else {
vSocks5Init.push_back(0x01); // 1 method identifier follows...
vSocks5Init.push_back(SOCKS5Method::NOAUTH);
}
sock.SendComplete(vSocks5Init, g_socks5_recv_timeout, g_socks5_interrupt);
uint8_t pchRet1[2];
if (InterruptibleRecv(pchRet1, 2, g_socks5_recv_timeout, sock) !=
IntrRecvError::OK) {
LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout "
"or other failure\n",
strDest, port);
return false;
}
if (pchRet1[0] != SOCKSVersion::SOCKS5) {
LogError("Proxy failed to initialize\n");
return false;
}
if (pchRet1[1] == SOCKS5Method::USER_PASS && auth) {
// Perform username/password authentication (as described in RFC1929)
std::vector<uint8_t> vAuth;
vAuth.push_back(
0x01); // Current (and only) version of user/pass subnegotiation
if (auth->username.size() > 255 || auth->password.size() > 255) {
LogError("Proxy username or password too long\n");
return false;
}
vAuth.push_back(auth->username.size());
vAuth.insert(vAuth.end(), auth->username.begin(), auth->username.end());
vAuth.push_back(auth->password.size());
vAuth.insert(vAuth.end(), auth->password.begin(), auth->password.end());
sock.SendComplete(vAuth, g_socks5_recv_timeout, g_socks5_interrupt);
LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n",
auth->username, auth->password);
uint8_t pchRetA[2];
if (InterruptibleRecv(pchRetA, 2, g_socks5_recv_timeout, sock) !=
IntrRecvError::OK) {
LogError("Error reading proxy authentication response\n");
return false;
}
if (pchRetA[0] != 0x01 || pchRetA[1] != 0x00) {
LogError("Proxy authentication unsuccessful\n");
return false;
}
} else if (pchRet1[1] == SOCKS5Method::NOAUTH) {
// Perform no authentication
} else {
LogError("Proxy requested wrong authentication method %02x\n",
pchRet1[1]);
return false;
}
std::vector<uint8_t> vSocks5;
vSocks5.push_back(SOCKSVersion::SOCKS5); // VER protocol version
vSocks5.push_back(SOCKS5Command::CONNECT); // CMD CONNECT
vSocks5.push_back(0x00); // RSV Reserved must be 0
vSocks5.push_back(SOCKS5Atyp::DOMAINNAME); // ATYP DOMAINNAME
vSocks5.push_back(
strDest.size()); // Length<=255 is checked at beginning of function
vSocks5.insert(vSocks5.end(), strDest.begin(), strDest.end());
vSocks5.push_back((port >> 8) & 0xFF);
vSocks5.push_back((port >> 0) & 0xFF);
sock.SendComplete(vSocks5, g_socks5_recv_timeout, g_socks5_interrupt);
uint8_t pchRet2[4];
if ((recvr = InterruptibleRecv(pchRet2, 4, g_socks5_recv_timeout, sock)) !=
IntrRecvError::OK) {
if (recvr == IntrRecvError::Timeout) {
/* If a timeout happens here, this effectively means we timed out while
* connecting to the remote node. This is very common for Tor, so do not
* print an error message. */
return false;
} else {
LogError("Error while reading proxy response\n");
return false;
}
}
if (pchRet2[0] != SOCKSVersion::SOCKS5) {
LogError("Proxy failed to accept request\n");
return false;
}
if (pchRet2[1] != SOCKS5Reply::SUCCEEDED) {
// Failures to connect to a peer that are not proxy errors
LogPrintf("Socks5() connect to %s:%d failed: %s\n", strDest, port,
Socks5ErrorString(pchRet2[1]));
return false;
}
if (pchRet2[2] != 0x00) { // Reserved field must be 0
LogError("Error: malformed proxy response\n");
return false;
}
uint8_t pchRet3[256];
switch (pchRet2[3]) {
case SOCKS5Atyp::IPV4:
recvr = InterruptibleRecv(pchRet3, 4, g_socks5_recv_timeout, sock);
break;
case SOCKS5Atyp::IPV6:
recvr = InterruptibleRecv(pchRet3, 16, g_socks5_recv_timeout, sock);
break;
case SOCKS5Atyp::DOMAINNAME: {
recvr = InterruptibleRecv(pchRet3, 1, g_socks5_recv_timeout, sock);
if (recvr != IntrRecvError::OK) {
LogError("Error reading from proxy\n");
return false;
}
int nRecv = pchRet3[0];
recvr = InterruptibleRecv(pchRet3, nRecv, g_socks5_recv_timeout, sock);
break;
}
default: {
LogError("Error: malformed proxy response\n");
return false;
}
}
if (recvr != IntrRecvError::OK) {
LogError("Error reading from proxy\n");
return false;
}
if (InterruptibleRecv(pchRet3, 2, g_socks5_recv_timeout, sock) !=
IntrRecvError::OK) {
LogError("Error reading from proxy\n");
return false;
}
LogPrint(BCLog::NET, "SOCKS5 connected %s\n", strDest);
return true;
} catch (const std::runtime_error &e) {
LogError("Error during SOCKS5 proxy handshake: %s\n", e.what());
return false;
}
}
std::unique_ptr<Sock> CreateSockOS(sa_family_t address_family) {
// Not IPv4, IPv6 or UNIX
if (address_family == AF_UNSPEC)
return nullptr;
int protocol{IPPROTO_TCP};
#if HAVE_SOCKADDR_UN
if (address_family == AF_UNIX)
protocol = 0;
#endif
// Create a socket in the specified address family.
SOCKET hSocket = socket(address_family, SOCK_STREAM, protocol);
if (hSocket == INVALID_SOCKET) {
return nullptr;
}
auto sock = std::make_unique<Sock>(hSocket);
// Ensure that waiting for I/O on this socket won't result in undefined
// behavior.
if (!sock->IsSelectable()) {
LogPrintf("Cannot create connection: non-selectable socket created (fd >= "
"FD_SETSIZE ?)\n");
return nullptr;
}
#ifdef SO_NOSIGPIPE
int set = 1;
// Set the no-sigpipe option on the socket for BSD systems, other UNIXes
// should use the MSG_NOSIGNAL flag for every send.
if (sock->SetSockOpt(SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)) ==
SOCKET_ERROR) {
LogPrintf("Error setting SO_NOSIGPIPE on socket: %s, continuing anyway\n",
NetworkErrorString(WSAGetLastError()));
}
#endif
// Set the non-blocking option on the socket.
if (!sock->SetNonBlocking()) {
LogPrintf("Error setting socket to non-blocking: %s\n",
NetworkErrorString(WSAGetLastError()));
return nullptr;
}
#if HAVE_SOCKADDR_UN
if (address_family == AF_UNIX)
return sock;
#endif
// Set the no-delay option (disable Nagle's algorithm) on the TCP socket.
const int on{1};
if (sock->SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) ==
SOCKET_ERROR) {
LogPrint(BCLog::NET, "Unable to set TCP_NODELAY on a newly created socket, "
"continuing anyway\n");
}
return sock;
}
std::function<std::unique_ptr<Sock>(const sa_family_t &)> CreateSock =
CreateSockOS;
template <typename... Args>
static void LogConnectFailure(bool manual_connection, const char *fmt,
const Args &...args) {
std::string error_message = tfm::format(fmt, args...);
if (manual_connection) {
LogPrintf("%s\n", error_message);
} else {
LogPrint(BCLog::NET, "%s\n", error_message);
}
}
static bool ConnectToSocket(const Sock &sock, struct sockaddr *sockaddr,
socklen_t len, const std::string &dest_str,
bool manual_connection) {
// Connect to `sockaddr` using `sock`.
if (sock.Connect(sockaddr, len) == SOCKET_ERROR) {
int nErr = WSAGetLastError();
// WSAEINVAL is here because some legacy version of winsock uses it
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) {
// Connection didn't actually fail, but is being established
// asynchronously. Thus, use async I/O api (select/poll)
// synchronously to check for successful connection with a timeout.
const Sock::Event requested = Sock::RECV | Sock::SEND;
Sock::Event occurred;
if (!sock.Wait(std::chrono::milliseconds{nConnectTimeout}, requested,
&occurred)) {
LogPrintf("wait for connect to %s failed: %s\n", dest_str,
NetworkErrorString(WSAGetLastError()));
return false;
} else if (occurred == 0) {
LogPrint(BCLog::NET, "connection attempt to %s timed out\n", dest_str);
return false;
}
// Even if the wait was successful, the connect might not
// have been successful. The reason for this failure is hidden away
// in the SO_ERROR for the socket in modern systems. We read it into
// sockerr here.
int sockerr;
socklen_t sockerr_len = sizeof(sockerr);
if (sock.GetSockOpt(SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&sockerr,
&sockerr_len) == SOCKET_ERROR) {
LogPrintf("getsockopt() for %s failed: %s\n", dest_str,
NetworkErrorString(WSAGetLastError()));
return false;
}
if (sockerr != 0) {
LogConnectFailure(manual_connection,
"connect() to %s failed after wait: %s", dest_str,
NetworkErrorString(sockerr));
return false;
}
}
#ifdef WIN32
else if (WSAGetLastError() != WSAEISCONN)
#else
else
#endif
{
LogConnectFailure(manual_connection, "connect() to %s failed: %s",
dest_str, NetworkErrorString(WSAGetLastError()));
return false;
}
}
return true;
}
std::unique_ptr<Sock> ConnectDirectly(const CService &dest,
bool manual_connection) {
auto sock = CreateSock(dest.GetSAFamily());
if (!sock) {
LogPrintLevel(BCLog::NET, BCLog::Level::Error,
"Cannot create a socket for connecting to %s\n",
dest.ToStringAddrPort());
return {};
}
// Create a sockaddr from the specified service.
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
if (!dest.GetSockAddr((struct sockaddr *)&sockaddr, &len)) {
LogPrintf("Cannot get sockaddr for %s: unsupported network\n",
dest.ToStringAddrPort());
return {};
}
if (!ConnectToSocket(*sock, (struct sockaddr *)&sockaddr, len,
dest.ToStringAddrPort(), manual_connection)) {
return {};
}
return sock;
}
std::unique_ptr<Sock> Proxy::Connect() const {
if (!IsValid()) {
LogPrintf("Cannot connect to invalid Proxy\n");
return {};
}
if (!m_is_unix_socket)
return ConnectDirectly(proxy, /*manual_connection=*/true);
#if HAVE_SOCKADDR_UN
auto sock = CreateSock(AF_UNIX);
if (!sock) {
LogPrintLevel(BCLog::NET, BCLog::Level::Error,
"Cannot create a socket for connecting to %s\n",
m_unix_socket_path);
return {};
}
const std::string path{m_unix_socket_path.substr(ADDR_PREFIX_UNIX.length())};
struct sockaddr_un addrun;
memset(&addrun, 0, sizeof(addrun));
addrun.sun_family = AF_UNIX;
// leave the last char in addrun.sun_path[] to be always '\0'
memcpy(addrun.sun_path, path.c_str(),
std::min(sizeof(addrun.sun_path) - 1, path.length()));
socklen_t len = sizeof(addrun);
if (!ConnectToSocket(*sock, (struct sockaddr *)&addrun, len, path,
/*manual_connection=*/true)) {
LogPrintf("Cannot connect to socket for %s\n", path);
return {};
}
return sock;
#else
return {};
#endif
}
bool SetProxy(enum Network net, const Proxy &addrProxy) {
assert(net >= 0 && net < NET_MAX);
if (!addrProxy.IsValid())
return false;
LOCK(g_proxyinfo_mutex);
proxyInfo[net] = addrProxy;
return true;
}
bool GetProxy(enum Network net, Proxy &proxyInfoOut) {
assert(net >= 0 && net < NET_MAX);
LOCK(g_proxyinfo_mutex);
if (!proxyInfo[net].IsValid())
return false;
proxyInfoOut = proxyInfo[net];
return true;
}
bool SetNameProxy(const Proxy &addrProxy) {
if (!addrProxy.IsValid())
return false;
LOCK(g_proxyinfo_mutex);
nameProxy = addrProxy;
return true;
}
bool GetNameProxy(Proxy &nameProxyOut) {
LOCK(g_proxyinfo_mutex);
if (!nameProxy.IsValid())
return false;
nameProxyOut = nameProxy;
return true;
}
bool HaveNameProxy() {
LOCK(g_proxyinfo_mutex);
return nameProxy.IsValid();
}
bool IsProxy(const CNetAddr &addr) {
LOCK(g_proxyinfo_mutex);
for (int i = 0; i < NET_MAX; i++) {
if (addr == static_cast<CNetAddr>(proxyInfo[i].proxy))
return true;
}
return false;
}
std::unique_ptr<Sock> ConnectThroughProxy(const Proxy &proxy,
const std::string &dest,
uint16_t port,
bool &proxy_connection_failed) {
// first connect to proxy server
auto sock = proxy.Connect();
if (!sock) {
proxy_connection_failed = true;
return {};
}
// do socks negotiation
if (proxy.m_randomize_credentials) {
ProxyCredentials random_auth;
static std::atomic_int counter(0);
random_auth.username = random_auth.password = strprintf("%i", counter++);
if (!Socks5(dest, port, &random_auth, *sock)) {
return {};
}
} else {
if (!Socks5(dest, port, nullptr, *sock)) {
return {};
}
}
return sock;
}
CSubNet LookupSubNet(const std::string &subnet_str) {
CSubNet subnet;
assert(!subnet.IsValid());
if (!ContainsNoNUL(subnet_str)) {
return subnet;
}
const size_t slash_pos{subnet_str.find_last_of('/')};
const std::string str_addr{subnet_str.substr(0, slash_pos)};
std::optional<CNetAddr> addr{LookupHost(str_addr, /*fAllowLookup=*/false)};
if (addr.has_value()) {
addr = static_cast<CNetAddr>(
MaybeFlipIPv6toCJDNS(CService{addr.value(), /*port=*/0}));
if (slash_pos != subnet_str.npos) {
const std::string netmask_str{subnet_str.substr(slash_pos + 1)};
uint8_t netmask;
if (ParseUInt8(netmask_str, &netmask)) {
// Valid number; assume CIDR variable-length subnet masking.
subnet = CSubNet{addr.value(), netmask};
} else {
// Invalid number; try full netmask syntax. Never allow lookup for
// netmask.
const std::optional<CNetAddr> full_netmask{
LookupHost(netmask_str, /*fAllowLookup=*/false)};
if (full_netmask.has_value()) {
subnet = CSubNet{addr.value(), full_netmask.value()};
}
}
} else {
// Single IP subnet (<ipv4>/32 or <ipv6>/128).
subnet = CSubNet{addr.value()};
}
}
return subnet;
}
bool IsBadPort(uint16_t port) {
/* Don't forget to update doc/p2p-bad-ports.md if you change this list. */
switch (port) {
case 1: // tcpmux
case 7: // echo
case 9: // discard
case 11: // systat
case 13: // daytime
case 15: // netstat
case 17: // qotd
case 19: // chargen
case 20: // ftp data
case 21: // ftp access
case 22: // ssh
case 23: // telnet
case 25: // smtp
case 37: // time
case 42: // name
case 43: // nicname
case 53: // domain
case 69: // tftp
case 77: // priv-rjs
case 79: // finger
case 87: // ttylink
case 95: // supdup
case 101: // hostname
case 102: // iso-tsap
case 103: // gppitnp
case 104: // acr-nema
case 109: // pop2
case 110: // pop3
case 111: // sunrpc
case 113: // auth
case 115: // sftp
case 117: // uucp-path
case 119: // nntp
case 123: // NTP
case 135: // loc-srv /epmap
case 137: // netbios
case 139: // netbios
case 143: // imap2
case 161: // snmp
case 179: // BGP
case 389: // ldap
case 427: // SLP (Also used by Apple Filing Protocol)
case 465: // smtp+ssl
case 512: // print / exec
case 513: // login
case 514: // shell
case 515: // printer
case 526: // tempo
case 530: // courier
case 531: // chat
case 532: // netnews
case 540: // uucp
case 548: // AFP (Apple Filing Protocol)
case 554: // rtsp
case 556: // remotefs
case 563: // nntp+ssl
case 587: // smtp (rfc6409)
case 601: // syslog-conn (rfc3195)
case 636: // ldap+ssl
case 989: // ftps-data
case 990: // ftps
case 993: // ldap+ssl
case 995: // pop3+ssl
case 1719: // h323gatestat
case 1720: // h323hostcall
case 1723: // pptp
case 2049: // nfs
case 3659: // apple-sasl / PasswordServer
case 4045: // lockd
case 5060: // sip
case 5061: // sips
case 6000: // X11
case 6566: // sane-port
case 6665: // Alternate IRC
case 6666: // Alternate IRC
case 6667: // Standard IRC
case 6668: // Alternate IRC
case 6669: // Alternate IRC
case 6697: // IRC + TLS
case 10080: // Amanda
return true;
}
return false;
}
CService MaybeFlipIPv6toCJDNS(const CService &service) {
CService ret{service};
if (ret.IsIPv6() && ret.HasCJDNSPrefix() &&
g_reachable_nets.Contains(NET_CJDNS)) {
ret.m_net = NET_CJDNS;
}
return ret;
}

345
src/i2p/netbase.h Normal file
View File

@ -0,0 +1,345 @@
// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_NETBASE_H
#define BITCOIN_NETBASE_H
#include "compat/compat.h"
#include "netaddress.h"
#include "serialize.h"
#include "util/sock.h"
#include "util/threadinterrupt.h"
#include <functional>
#include <memory>
#include <stdint.h>
#include <string>
#include <type_traits>
#include <unordered_set>
#include <vector>
extern int nConnectTimeout;
extern bool fNameLookup;
//! -timeout default
static const int DEFAULT_CONNECT_TIMEOUT = 5000;
//! -dns default
static const int DEFAULT_NAME_LOOKUP = true;
/** Prefix for unix domain socket addresses (which are local filesystem paths) */
const std::string ADDR_PREFIX_UNIX = "unix:";
enum class ConnectionDirection {
None = 0,
In = (1U << 0),
Out = (1U << 1),
Both = (In | Out),
};
static inline ConnectionDirection& operator|=(ConnectionDirection& a, ConnectionDirection b) {
using underlying = typename std::underlying_type<ConnectionDirection>::type;
a = ConnectionDirection(underlying(a) | underlying(b));
return a;
}
static inline bool operator&(ConnectionDirection a, ConnectionDirection b) {
using underlying = typename std::underlying_type<ConnectionDirection>::type;
return (underlying(a) & underlying(b));
}
/**
* Check if a string is a valid UNIX domain socket path
*
* @param name The string provided by the user representing a local path
*
* @returns Whether the string has proper format, length, and points to an existing file path
*/
bool IsUnixSocketPath(const std::string& name);
class Proxy
{
public:
Proxy() : m_is_unix_socket(false), m_randomize_credentials(false) {}
explicit Proxy(const CService& _proxy, bool _randomize_credentials = false) : proxy(_proxy), m_is_unix_socket(false), m_randomize_credentials(_randomize_credentials) {}
explicit Proxy(const std::string path, bool _randomize_credentials = false) : m_unix_socket_path(path), m_is_unix_socket(true), m_randomize_credentials(_randomize_credentials) {}
CService proxy;
std::string m_unix_socket_path;
bool m_is_unix_socket;
bool m_randomize_credentials;
bool IsValid() const
{
if (m_is_unix_socket) return IsUnixSocketPath(m_unix_socket_path);
return proxy.IsValid();
}
sa_family_t GetFamily() const
{
if (m_is_unix_socket) return AF_UNIX;
return proxy.GetSAFamily();
}
std::string ToString() const
{
if (m_is_unix_socket) return m_unix_socket_path;
return proxy.ToStringAddrPort();
}
std::unique_ptr<Sock> Connect() const;
};
/** Credentials for proxy authentication */
struct ProxyCredentials
{
std::string username;
std::string password;
};
/**
* List of reachable networks. Everything is reachable by default.
*/
class ReachableNets {
public:
void Add(Network net) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
AssertLockNotHeld(m_mutex);
LOCK(m_mutex);
m_reachable.insert(net);
}
void Remove(Network net) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
AssertLockNotHeld(m_mutex);
LOCK(m_mutex);
m_reachable.erase(net);
}
void RemoveAll() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
AssertLockNotHeld(m_mutex);
LOCK(m_mutex);
m_reachable.clear();
}
[[nodiscard]] bool Contains(Network net) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
AssertLockNotHeld(m_mutex);
LOCK(m_mutex);
return m_reachable.count(net) > 0;
}
[[nodiscard]] bool Contains(const CNetAddr& addr) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
AssertLockNotHeld(m_mutex);
return Contains(addr.GetNetwork());
}
private:
mutable Mutex m_mutex;
std::unordered_set<Network> m_reachable GUARDED_BY(m_mutex){
NET_UNROUTABLE,
NET_IPV4,
NET_IPV6,
NET_ONION,
NET_I2P,
NET_CJDNS,
NET_INTERNAL
};
};
extern ReachableNets g_reachable_nets;
/**
* Wrapper for getaddrinfo(3). Do not use directly: call Lookup/LookupHost/LookupNumeric/LookupSubNet.
*/
std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup);
enum Network ParseNetwork(const std::string& net);
std::string GetNetworkName(enum Network net);
/** Return a vector of publicly routable Network names; optionally append NET_UNROUTABLE. */
std::vector<std::string> GetNetworkNames(bool append_unroutable = false);
bool SetProxy(enum Network net, const Proxy &addrProxy);
bool GetProxy(enum Network net, Proxy &proxyInfoOut);
bool IsProxy(const CNetAddr &addr);
/**
* Set the name proxy to use for all connections to nodes specified by a
* hostname. After setting this proxy, connecting to a node specified by a
* hostname won't result in a local lookup of said hostname, rather, connect to
* the node by asking the name proxy for a proxy connection to the hostname,
* effectively delegating the hostname lookup to the specified proxy.
*
* This delegation increases privacy for those who set the name proxy as they no
* longer leak their external hostname queries to their DNS servers.
*
* @returns Whether or not the operation succeeded.
*
* @note SOCKS5's support for UDP-over-SOCKS5 has been considered, but no SOCK5
* server in common use (most notably Tor) actually implements UDP
* support, and a DNS resolver is beyond the scope of this project.
*/
bool SetNameProxy(const Proxy &addrProxy);
bool HaveNameProxy();
bool GetNameProxy(Proxy &nameProxyOut);
using DNSLookupFn = std::function<std::vector<CNetAddr>(const std::string&, bool)>;
extern DNSLookupFn g_dns_lookup;
/**
* Resolve a host string to its corresponding network addresses.
*
* @param name The string representing a host. Could be a name or a numerical
* IP address (IPv6 addresses in their bracketed form are
* allowed).
*
* @returns The resulting network addresses to which the specified host
* string resolved.
*
* @see Lookup(const std::string&, uint16_t, bool, unsigned int, DNSLookupFn)
* for additional parameter descriptions.
*/
std::vector<CNetAddr> LookupHost(const std::string& name, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup);
/**
* Resolve a host string to its first corresponding network address.
*
* @returns The resulting network address to which the specified host
* string resolved or std::nullopt if host does not resolve to an address.
*
* @see LookupHost(const std::string&, unsigned int, bool, DNSLookupFn)
* for additional parameter descriptions.
*/
std::optional<CNetAddr> LookupHost(const std::string& name, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup);
/**
* Resolve a service string to its corresponding service.
*
* @param name The string representing a service. Could be a name or a
* numerical IP address (IPv6 addresses should be in their
* disambiguated bracketed form), optionally followed by a uint16_t port
* number. (e.g. example.com:8333 or
* [2001:db8:85a3:8d3:1319:8a2e:370:7348]:420)
* @param portDefault The default port for resulting services if not specified
* by the service string.
* @param fAllowLookup Whether or not hostname lookups are permitted. If yes,
* external queries may be performed.
* @param nMaxSolutions The maximum number of results we want, specifying 0
* means "as many solutions as we get."
*
* @returns The resulting services to which the specified service string
* resolved.
*/
std::vector<CService> Lookup(const std::string& name, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function = g_dns_lookup);
/**
* Resolve a service string to its first corresponding service.
*
* @see Lookup(const std::string&, uint16_t, bool, unsigned int, DNSLookupFn)
* for additional parameter descriptions.
*/
std::optional<CService> Lookup(const std::string& name, uint16_t portDefault, bool fAllowLookup, DNSLookupFn dns_lookup_function = g_dns_lookup);
/**
* Resolve a service string with a numeric IP to its first corresponding
* service.
*
* @returns The resulting CService if the resolution was successful, [::]:0 otherwise.
*
* @see Lookup(const std::string&, uint16_t, bool, unsigned int, DNSLookupFn)
* for additional parameter descriptions.
*/
CService LookupNumeric(const std::string& name, uint16_t portDefault = 0, DNSLookupFn dns_lookup_function = g_dns_lookup);
/**
* Parse and resolve a specified subnet string into the appropriate internal
* representation.
*
* @param[in] subnet_str A string representation of a subnet of the form
* `network address [ "/", ( CIDR-style suffix | netmask ) ]`
* e.g. "2001:db8::/32", "192.0.2.0/255.255.255.0" or "8.8.8.8".
* @returns a CSubNet object (that may or may not be valid).
*/
CSubNet LookupSubNet(const std::string& subnet_str);
/**
* Create a TCP or UNIX socket in the given address family.
* @param[in] address_family to use for the socket.
* @return pointer to the created Sock object or unique_ptr that owns nothing in case of failure
*/
std::unique_ptr<Sock> CreateSockOS(sa_family_t address_family);
/**
* Socket factory. Defaults to `CreateSockOS()`, but can be overridden by unit tests.
*/
extern std::function<std::unique_ptr<Sock>(const sa_family_t&)> CreateSock;
/**
* Create a socket and try to connect to the specified service.
*
* @param[in] dest The service to which to connect.
* @param[in] manual_connection Whether or not the connection was manually requested (e.g. through the addnode RPC)
*
* @returns the connected socket if the operation succeeded, empty unique_ptr otherwise
*/
std::unique_ptr<Sock> ConnectDirectly(const CService& dest, bool manual_connection);
/**
* Connect to a specified destination service through a SOCKS5 proxy by first
* connecting to the SOCKS5 proxy.
*
* @param[in] proxy The SOCKS5 proxy.
* @param[in] dest The destination service to which to connect.
* @param[in] port The destination port.
* @param[out] proxy_connection_failed Whether or not the connection to the SOCKS5 proxy failed.
*
* @returns the connected socket if the operation succeeded. Otherwise an empty unique_ptr.
*/
std::unique_ptr<Sock> ConnectThroughProxy(const Proxy& proxy,
const std::string& dest,
uint16_t port,
bool& proxy_connection_failed);
/**
* Interrupt SOCKS5 reads or writes.
*/
extern CThreadInterrupt g_socks5_interrupt;
/**
* Connect to a specified destination service through an already connected
* SOCKS5 proxy.
*
* @param strDest The destination fully-qualified domain name.
* @param port The destination port.
* @param auth The credentials with which to authenticate with the specified
* SOCKS5 proxy.
* @param socket The SOCKS5 proxy socket.
*
* @returns Whether or not the operation succeeded.
*
* @note The specified SOCKS5 proxy socket must already be connected to the
* SOCKS5 proxy.
*
* @see <a href="https://www.ietf.org/rfc/rfc1928.txt">RFC1928: SOCKS Protocol
* Version 5</a>
*/
bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* auth, const Sock& socket);
/**
* Determine if a port is "bad" from the perspective of attempting to connect
* to a node on that port.
* @see doc/p2p-bad-ports.md
* @param[in] port Port to check.
* @returns whether the port is bad
*/
bool IsBadPort(uint16_t port);
/**
* If an IPv6 address belongs to the address range used by the CJDNS network and
* the CJDNS network is reachable (-cjdnsreachable config is set), then change
* the type from NET_IPV6 to NET_CJDNS.
* @param[in] service Address to potentially convert.
* @return a copy of `service` either unmodified or changed to CJDNS.
*/
CService MaybeFlipIPv6toCJDNS(const CService& service);
#endif // BITCOIN_NETBASE_H

541
src/i2p/prevector.h Normal file
View File

@ -0,0 +1,541 @@
// Copyright (c) 2015-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_PREVECTOR_H
#define BITCOIN_PREVECTOR_H
#include <assert.h>
#include <cstdlib>
#include <stdint.h>
#include <string.h>
#include <algorithm>
#include <cstddef>
#include <type_traits>
#include <utility>
/** Implements a drop-in replacement for std::vector<T> which stores up to N
* elements directly (without heap allocation). The types Size and Diff are
* used to store element counts, and can be any unsigned + signed type.
*
* Storage layout is either:
* - Direct allocation:
* - Size _size: the number of used elements (between 0 and N)
* - T direct[N]: an array of N elements of type T
* (only the first _size are initialized).
* - Indirect allocation:
* - Size _size: the number of used elements plus N + 1
* - Size capacity: the number of allocated elements
* - T* indirect: a pointer to an array of capacity elements of type T
* (only the first _size are initialized).
*
* The data type T must be movable by memmove/realloc(). Once we switch to C++,
* move constructors can be used instead.
*/
template<unsigned int N, typename T, typename Size = uint32_t, typename Diff = int32_t>
class prevector {
static_assert(std::is_trivially_copyable_v<T>);
public:
typedef Size size_type;
typedef Diff difference_type;
typedef T value_type;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef value_type* pointer;
typedef const value_type* const_pointer;
class iterator {
T* ptr{};
public:
typedef Diff difference_type;
typedef T value_type;
typedef T* pointer;
typedef T& reference;
using element_type = T;
using iterator_category = std::contiguous_iterator_tag;
iterator() = default;
iterator(T* ptr_) : ptr(ptr_) {}
T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
T& operator[](size_type pos) const { return ptr[pos]; }
iterator& operator++() { ptr++; return *this; }
iterator& operator--() { ptr--; return *this; }
iterator operator++(int) { iterator copy(*this); ++(*this); return copy; }
iterator operator--(int) { iterator copy(*this); --(*this); return copy; }
difference_type friend operator-(iterator a, iterator b) { return (&(*a) - &(*b)); }
iterator operator+(size_type n) const { return iterator(ptr + n); }
iterator friend operator+(size_type n, iterator x) { return x + n; }
iterator& operator+=(size_type n) { ptr += n; return *this; }
iterator operator-(size_type n) const { return iterator(ptr - n); }
iterator& operator-=(size_type n) { ptr -= n; return *this; }
bool operator==(iterator x) const { return ptr == x.ptr; }
bool operator!=(iterator x) const { return ptr != x.ptr; }
bool operator>=(iterator x) const { return ptr >= x.ptr; }
bool operator<=(iterator x) const { return ptr <= x.ptr; }
bool operator>(iterator x) const { return ptr > x.ptr; }
bool operator<(iterator x) const { return ptr < x.ptr; }
};
class reverse_iterator {
T* ptr{};
public:
typedef Diff difference_type;
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef std::bidirectional_iterator_tag iterator_category;
reverse_iterator() = default;
reverse_iterator(T* ptr_) : ptr(ptr_) {}
T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
reverse_iterator& operator--() { ptr++; return *this; }
reverse_iterator& operator++() { ptr--; return *this; }
reverse_iterator operator++(int) { reverse_iterator copy(*this); ++(*this); return copy; }
reverse_iterator operator--(int) { reverse_iterator copy(*this); --(*this); return copy; }
bool operator==(reverse_iterator x) const { return ptr == x.ptr; }
bool operator!=(reverse_iterator x) const { return ptr != x.ptr; }
};
class const_iterator {
const T* ptr{};
public:
typedef Diff difference_type;
typedef const T value_type;
typedef const T* pointer;
typedef const T& reference;
using element_type = const T;
using iterator_category = std::contiguous_iterator_tag;
const_iterator() = default;
const_iterator(const T* ptr_) : ptr(ptr_) {}
const_iterator(iterator x) : ptr(&(*x)) {}
const T& operator*() const { return *ptr; }
const T* operator->() const { return ptr; }
const T& operator[](size_type pos) const { return ptr[pos]; }
const_iterator& operator++() { ptr++; return *this; }
const_iterator& operator--() { ptr--; return *this; }
const_iterator operator++(int) { const_iterator copy(*this); ++(*this); return copy; }
const_iterator operator--(int) { const_iterator copy(*this); --(*this); return copy; }
difference_type friend operator-(const_iterator a, const_iterator b) { return (&(*a) - &(*b)); }
const_iterator operator+(size_type n) const { return const_iterator(ptr + n); }
const_iterator friend operator+(size_type n, const_iterator x) { return x + n; }
const_iterator& operator+=(size_type n) { ptr += n; return *this; }
const_iterator operator-(size_type n) const { return const_iterator(ptr - n); }
const_iterator& operator-=(size_type n) { ptr -= n; return *this; }
bool operator==(const_iterator x) const { return ptr == x.ptr; }
bool operator!=(const_iterator x) const { return ptr != x.ptr; }
bool operator>=(const_iterator x) const { return ptr >= x.ptr; }
bool operator<=(const_iterator x) const { return ptr <= x.ptr; }
bool operator>(const_iterator x) const { return ptr > x.ptr; }
bool operator<(const_iterator x) const { return ptr < x.ptr; }
};
class const_reverse_iterator {
const T* ptr{};
public:
typedef Diff difference_type;
typedef const T value_type;
typedef const T* pointer;
typedef const T& reference;
typedef std::bidirectional_iterator_tag iterator_category;
const_reverse_iterator() = default;
const_reverse_iterator(const T* ptr_) : ptr(ptr_) {}
const_reverse_iterator(reverse_iterator x) : ptr(&(*x)) {}
const T& operator*() const { return *ptr; }
const T* operator->() const { return ptr; }
const_reverse_iterator& operator--() { ptr++; return *this; }
const_reverse_iterator& operator++() { ptr--; return *this; }
const_reverse_iterator operator++(int) { const_reverse_iterator copy(*this); ++(*this); return copy; }
const_reverse_iterator operator--(int) { const_reverse_iterator copy(*this); --(*this); return copy; }
bool operator==(const_reverse_iterator x) const { return ptr == x.ptr; }
bool operator!=(const_reverse_iterator x) const { return ptr != x.ptr; }
};
private:
#pragma pack(push, 1)
union direct_or_indirect {
char direct[sizeof(T) * N];
struct {
char* indirect;
size_type capacity;
} indirect_contents;
};
#pragma pack(pop)
alignas(char*) direct_or_indirect _union = {};
size_type _size = 0;
static_assert(alignof(char*) % alignof(size_type) == 0 && sizeof(char*) % alignof(size_type) == 0, "size_type cannot have more restrictive alignment requirement than pointer");
static_assert(alignof(char*) % alignof(T) == 0, "value_type T cannot have more restrictive alignment requirement than pointer");
T* direct_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.direct) + pos; }
const T* direct_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.direct) + pos; }
T* indirect_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.indirect_contents.indirect) + pos; }
const T* indirect_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.indirect_contents.indirect) + pos; }
bool is_direct() const { return _size <= N; }
void change_capacity(size_type new_capacity) {
if (new_capacity <= N) {
if (!is_direct()) {
T* indirect = indirect_ptr(0);
T* src = indirect;
T* dst = direct_ptr(0);
memcpy(dst, src, size() * sizeof(T));
free(indirect);
_size -= N + 1;
}
} else {
if (!is_direct()) {
/* FIXME: Because malloc/realloc here won't call new_handler if allocation fails, assert
success. These should instead use an allocator or new/delete so that handlers
are called as necessary, but performance would be slightly degraded by doing so. */
_union.indirect_contents.indirect = static_cast<char*>(realloc(_union.indirect_contents.indirect, ((size_t)sizeof(T)) * new_capacity));
assert(_union.indirect_contents.indirect);
_union.indirect_contents.capacity = new_capacity;
} else {
char* new_indirect = static_cast<char*>(malloc(((size_t)sizeof(T)) * new_capacity));
assert(new_indirect);
T* src = direct_ptr(0);
T* dst = reinterpret_cast<T*>(new_indirect);
memcpy(dst, src, size() * sizeof(T));
_union.indirect_contents.indirect = new_indirect;
_union.indirect_contents.capacity = new_capacity;
_size += N + 1;
}
}
}
T* item_ptr(difference_type pos) { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
const T* item_ptr(difference_type pos) const { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
void fill(T* dst, ptrdiff_t count, const T& value = T{}) {
std::fill_n(dst, count, value);
}
template<typename InputIterator>
void fill(T* dst, InputIterator first, InputIterator last) {
while (first != last) {
new(static_cast<void*>(dst)) T(*first);
++dst;
++first;
}
}
public:
void assign(size_type n, const T& val) {
clear();
if (capacity() < n) {
change_capacity(n);
}
_size += n;
fill(item_ptr(0), n, val);
}
template<typename InputIterator>
void assign(InputIterator first, InputIterator last) {
size_type n = last - first;
clear();
if (capacity() < n) {
change_capacity(n);
}
_size += n;
fill(item_ptr(0), first, last);
}
prevector() {}
explicit prevector(size_type n) {
resize(n);
}
explicit prevector(size_type n, const T& val) {
change_capacity(n);
_size += n;
fill(item_ptr(0), n, val);
}
template<typename InputIterator>
prevector(InputIterator first, InputIterator last) {
size_type n = last - first;
change_capacity(n);
_size += n;
fill(item_ptr(0), first, last);
}
prevector(const prevector<N, T, Size, Diff>& other) {
size_type n = other.size();
change_capacity(n);
_size += n;
fill(item_ptr(0), other.begin(), other.end());
}
prevector(prevector<N, T, Size, Diff>&& other) noexcept
: _union(std::move(other._union)), _size(other._size)
{
other._size = 0;
}
prevector& operator=(const prevector<N, T, Size, Diff>& other) {
if (&other == this) {
return *this;
}
assign(other.begin(), other.end());
return *this;
}
prevector& operator=(prevector<N, T, Size, Diff>&& other) noexcept {
if (!is_direct()) {
free(_union.indirect_contents.indirect);
}
_union = std::move(other._union);
_size = other._size;
other._size = 0;
return *this;
}
size_type size() const {
return is_direct() ? _size : _size - N - 1;
}
bool empty() const {
return size() == 0;
}
iterator begin() { return iterator(item_ptr(0)); }
const_iterator begin() const { return const_iterator(item_ptr(0)); }
iterator end() { return iterator(item_ptr(size())); }
const_iterator end() const { return const_iterator(item_ptr(size())); }
reverse_iterator rbegin() { return reverse_iterator(item_ptr(size() - 1)); }
const_reverse_iterator rbegin() const { return const_reverse_iterator(item_ptr(size() - 1)); }
reverse_iterator rend() { return reverse_iterator(item_ptr(-1)); }
const_reverse_iterator rend() const { return const_reverse_iterator(item_ptr(-1)); }
size_t capacity() const {
if (is_direct()) {
return N;
} else {
return _union.indirect_contents.capacity;
}
}
T& operator[](size_type pos) {
return *item_ptr(pos);
}
const T& operator[](size_type pos) const {
return *item_ptr(pos);
}
void resize(size_type new_size) {
size_type cur_size = size();
if (cur_size == new_size) {
return;
}
if (cur_size > new_size) {
erase(item_ptr(new_size), end());
return;
}
if (new_size > capacity()) {
change_capacity(new_size);
}
ptrdiff_t increase = new_size - cur_size;
fill(item_ptr(cur_size), increase);
_size += increase;
}
void reserve(size_type new_capacity) {
if (new_capacity > capacity()) {
change_capacity(new_capacity);
}
}
void shrink_to_fit() {
change_capacity(size());
}
void clear() {
resize(0);
}
iterator insert(iterator pos, const T& value) {
size_type p = pos - begin();
size_type new_size = size() + 1;
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
T* ptr = item_ptr(p);
memmove(ptr + 1, ptr, (size() - p) * sizeof(T));
_size++;
new(static_cast<void*>(ptr)) T(value);
return iterator(ptr);
}
void insert(iterator pos, size_type count, const T& value) {
size_type p = pos - begin();
size_type new_size = size() + count;
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
T* ptr = item_ptr(p);
memmove(ptr + count, ptr, (size() - p) * sizeof(T));
_size += count;
fill(item_ptr(p), count, value);
}
template<typename InputIterator>
void insert(iterator pos, InputIterator first, InputIterator last) {
size_type p = pos - begin();
difference_type count = last - first;
size_type new_size = size() + count;
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
T* ptr = item_ptr(p);
memmove(ptr + count, ptr, (size() - p) * sizeof(T));
_size += count;
fill(ptr, first, last);
}
inline void resize_uninitialized(size_type new_size) {
// resize_uninitialized changes the size of the prevector but does not initialize it.
// If size < new_size, the added elements must be initialized explicitly.
if (capacity() < new_size) {
change_capacity(new_size);
_size += new_size - size();
return;
}
if (new_size < size()) {
erase(item_ptr(new_size), end());
} else {
_size += new_size - size();
}
}
iterator erase(iterator pos) {
return erase(pos, pos + 1);
}
iterator erase(iterator first, iterator last) {
// Erase is not allowed to the change the object's capacity. That means
// that when starting with an indirectly allocated prevector with
// size and capacity > N, the result may be a still indirectly allocated
// prevector with size <= N and capacity > N. A shrink_to_fit() call is
// necessary to switch to the (more efficient) directly allocated
// representation (with capacity N and size <= N).
iterator p = first;
char* endp = (char*)&(*end());
_size -= last - p;
memmove(&(*first), &(*last), endp - ((char*)(&(*last))));
return first;
}
template<typename... Args>
void emplace_back(Args&&... args) {
size_type new_size = size() + 1;
if (capacity() < new_size) {
change_capacity(new_size + (new_size >> 1));
}
new(item_ptr(size())) T(std::forward<Args>(args)...);
_size++;
}
void push_back(const T& value) {
emplace_back(value);
}
void pop_back() {
erase(end() - 1, end());
}
T& front() {
return *item_ptr(0);
}
const T& front() const {
return *item_ptr(0);
}
T& back() {
return *item_ptr(size() - 1);
}
const T& back() const {
return *item_ptr(size() - 1);
}
void swap(prevector<N, T, Size, Diff>& other) noexcept
{
std::swap(_union, other._union);
std::swap(_size, other._size);
}
~prevector() {
if (!is_direct()) {
free(_union.indirect_contents.indirect);
_union.indirect_contents.indirect = nullptr;
}
}
bool operator==(const prevector<N, T, Size, Diff>& other) const {
if (other.size() != size()) {
return false;
}
const_iterator b1 = begin();
const_iterator b2 = other.begin();
const_iterator e1 = end();
while (b1 != e1) {
if ((*b1) != (*b2)) {
return false;
}
++b1;
++b2;
}
return true;
}
bool operator!=(const prevector<N, T, Size, Diff>& other) const {
return !(*this == other);
}
bool operator<(const prevector<N, T, Size, Diff>& other) const {
if (size() < other.size()) {
return true;
}
if (size() > other.size()) {
return false;
}
const_iterator b1 = begin();
const_iterator b2 = other.begin();
const_iterator e1 = end();
while (b1 != e1) {
if ((*b1) < (*b2)) {
return true;
}
if ((*b2) < (*b1)) {
return false;
}
++b1;
++b2;
}
return false;
}
size_t allocated_memory() const {
if (is_direct()) {
return 0;
} else {
return ((size_t)(sizeof(T))) * _union.indirect_contents.capacity;
}
}
value_type* data() {
return item_ptr(0);
}
const value_type* data() const {
return item_ptr(0);
}
};
#endif // BITCOIN_PREVECTOR_H

158
src/i2p/primitives/block.h Normal file
View File

@ -0,0 +1,158 @@
// // Copyright (c) 2009-2010 Satoshi Nakamoto
// // Copyright (c) 2009-2022 The Bitcoin Core developers
// // Distributed under the MIT software license, see the accompanying
// // file COPYING or http://www.opensource.org/licenses/mit-license.php.
// *
// #ifndef BITCOIN_PRIMITIVES_BLOCK_H
// #define BITCOIN_PRIMITIVES_BLOCK_H
//
// #include "transaction.h"
// #include "serialize.h"
// #include "uint256.h"
// #include "util/time.h"
//
// /** Nodes collect new transactions into a block, hash them into a hash tree,
// * and scan through nonce values to make the block's hash satisfy proof-of-work
// * requirements. When they solve the proof-of-work, they broadcast the block
// * to everyone and the block is added to the block chain. The first transaction
// * in the block is a special one that creates a new coin owned by the creator
// * of the block.
// */
// class CBlockHeader
// {
// public:
// // header
// int32_t nVersion;
// uint256 hashPrevBlock;
// uint256 hashMerkleRoot;
// uint32_t nTime;
// uint32_t nBits;
// uint32_t nNonce;
//
// CBlockHeader()
// {
// SetNull();
// }
//
// SERIALIZE_METHODS(CBlockHeader, obj) { READWRITE(obj.nVersion, obj.hashPrevBlock, obj.hashMerkleRoot, obj.nTime, obj.nBits, obj.nNonce); }
//
// void SetNull()
// {
// nVersion = 0;
// hashPrevBlock.SetNull();
// hashMerkleRoot.SetNull();
// nTime = 0;
// nBits = 0;
// nNonce = 0;
// }
//
// bool IsNull() const
// {
// return (nBits == 0);
// }
//
// uint256 GetHash() const;
//
// NodeSeconds Time() const
// {
// return NodeSeconds{std::chrono::seconds{nTime}};
// }
//
// int64_t GetBlockTime() const
// {
// return (int64_t)nTime;
// }
// };
//
//
// class CBlock : public CBlockHeader
// {
// public:
// // network and disk
// std::vector<CTransactionRef> vtx;
//
// // Memory-only flags for caching expensive checks
// mutable bool fChecked; // CheckBlock()
// mutable bool m_checked_witness_commitment{false}; // CheckWitnessCommitment()
// mutable bool m_checked_merkle_root{false}; // CheckMerkleRoot()
//
// CBlock()
// {
// SetNull();
// }
//
// CBlock(const CBlockHeader &header)
// {
// SetNull();
// *(static_cast<CBlockHeader*>(this)) = header;
// }
//
// SERIALIZE_METHODS(CBlock, obj)
// {
// READWRITE(AsBase<CBlockHeader>(obj), obj.vtx);
// }
//
// void SetNull()
// {
// CBlockHeader::SetNull();
// vtx.clear();
// fChecked = false;
// m_checked_witness_commitment = false;
// m_checked_merkle_root = false;
// }
//
// CBlockHeader GetBlockHeader() const
// {
// CBlockHeader block;
// block.nVersion = nVersion;
// block.hashPrevBlock = hashPrevBlock;
// block.hashMerkleRoot = hashMerkleRoot;
// block.nTime = nTime;
// block.nBits = nBits;
// block.nNonce = nNonce;
// return block;
// }
//
// std::string ToString() const;
// };
//
// /** Describes a place in the block chain to another node such that if the
// * other node doesn't have the same branch, it can find a recent common trunk.
// * The further back it is, the further before the fork it may be.
// */
// struct CBlockLocator
// {
// /** Historically CBlockLocator's version field has been written to network
// * streams as the negotiated protocol version and to disk streams as the
// * client version, but the value has never been used.
// *
// * Hard-code to the highest protocol version ever written to a network stream.
// * SerParams can be used if the field requires any meaning in the future,
// **/
// static constexpr int DUMMY_VERSION = 70016;
//
// std::vector<uint256> vHave;
//
// CBlockLocator() {}
//
// explicit CBlockLocator(std::vector<uint256>&& have) : vHave(std::move(have)) {}
//
// SERIALIZE_METHODS(CBlockLocator, obj)
// {
// int nVersion = DUMMY_VERSION;
// READWRITE(nVersion);
// READWRITE(obj.vHave);
// }
//
// void SetNull()
// {
// vHave.clear();
// }
//
// bool IsNull() const
// {
// return vHave.empty();
// }
// };
//
// #endif */ // BITCOIN_PRIMITIVES_BLOCK_H

View File

@ -0,0 +1,442 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_PRIMITIVES_TRANSACTION_H
#define BITCOIN_PRIMITIVES_TRANSACTION_H
#include "../attributes.h"
#include "../consensus/amount.h"
#include "../script/script.h"
#include "../serialize.h"
#include "../uint256.h"
#include "../util/transaction_identifier.h" // IWYU pragma: export
#include <cstddef>
#include <cstdint>
#include <ios>
#include <limits>
#include <memory>
#include <numeric>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
/** An outpoint - a combination of a transaction hash and an index n into its vout */
class COutPoint
{
public:
Txid hash;
uint32_t n;
static constexpr uint32_t NULL_INDEX = std::numeric_limits<uint32_t>::max();
COutPoint(): n(NULL_INDEX) { }
COutPoint(const Txid& hashIn, uint32_t nIn): hash(hashIn), n(nIn) { }
SERIALIZE_METHODS(COutPoint, obj) { READWRITE(obj.hash, obj.n); }
void SetNull() { hash.SetNull(); n = NULL_INDEX; }
bool IsNull() const { return (hash.IsNull() && n == NULL_INDEX); }
friend bool operator<(const COutPoint& a, const COutPoint& b)
{
return std::tie(a.hash, a.n) < std::tie(b.hash, b.n);
}
friend bool operator==(const COutPoint& a, const COutPoint& b)
{
return (a.hash == b.hash && a.n == b.n);
}
friend bool operator!=(const COutPoint& a, const COutPoint& b)
{
return !(a == b);
}
std::string ToString() const;
};
/** An input of a transaction. It contains the location of the previous
* transaction's output that it claims and a signature that matches the
* output's public key.
*/
class CTxIn
{
public:
COutPoint prevout;
CScript scriptSig;
uint32_t nSequence;
CScriptWitness scriptWitness; //!< Only serialized through CTransaction
/**
* Setting nSequence to this value for every input in a transaction
* disables nLockTime/IsFinalTx().
* It fails OP_CHECKLOCKTIMEVERIFY/CheckLockTime() for any input that has
* it set (BIP 65).
* It has SEQUENCE_LOCKTIME_DISABLE_FLAG set (BIP 68/112).
*/
static const uint32_t SEQUENCE_FINAL = 0xffffffff;
/**
* This is the maximum sequence number that enables both nLockTime and
* OP_CHECKLOCKTIMEVERIFY (BIP 65).
* It has SEQUENCE_LOCKTIME_DISABLE_FLAG set (BIP 68/112).
*/
static const uint32_t MAX_SEQUENCE_NONFINAL{SEQUENCE_FINAL - 1};
// Below flags apply in the context of BIP 68. BIP 68 requires the tx
// version to be set to 2, or higher.
/**
* If this flag is set, CTxIn::nSequence is NOT interpreted as a
* relative lock-time.
* It skips SequenceLocks() for any input that has it set (BIP 68).
* It fails OP_CHECKSEQUENCEVERIFY/CheckSequence() for any input that has
* it set (BIP 112).
*/
static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1U << 31);
/**
* If CTxIn::nSequence encodes a relative lock-time and this flag
* is set, the relative lock-time has units of 512 seconds,
* otherwise it specifies blocks with a granularity of 1. */
static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22);
/**
* If CTxIn::nSequence encodes a relative lock-time, this mask is
* applied to extract that lock-time from the sequence field. */
static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff;
/**
* In order to use the same number of bits to encode roughly the
* same wall-clock duration, and because blocks are naturally
* limited to occur every 600s on average, the minimum granularity
* for time-based relative lock-time is fixed at 512 seconds.
* Converting from CTxIn::nSequence to seconds is performed by
* multiplying by 512 = 2^9, or equivalently shifting up by
* 9 bits. */
static const int SEQUENCE_LOCKTIME_GRANULARITY = 9;
CTxIn()
{
nSequence = SEQUENCE_FINAL;
}
explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL);
CTxIn(Txid hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL);
SERIALIZE_METHODS(CTxIn, obj) { READWRITE(obj.prevout, obj.scriptSig, obj.nSequence); }
friend bool operator==(const CTxIn& a, const CTxIn& b)
{
return (a.prevout == b.prevout &&
a.scriptSig == b.scriptSig &&
a.nSequence == b.nSequence);
}
friend bool operator!=(const CTxIn& a, const CTxIn& b)
{
return !(a == b);
}
std::string ToString() const;
};
/** An output of a transaction. It contains the public key that the next input
* must be able to sign with to claim it.
*/
class CTxOut
{
public:
CAmount nValue;
CScript scriptPubKey;
CTxOut()
{
SetNull();
}
CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn);
SERIALIZE_METHODS(CTxOut, obj) { READWRITE(obj.nValue, obj.scriptPubKey); }
void SetNull()
{
nValue = -1;
scriptPubKey.clear();
}
bool IsNull() const
{
return (nValue == -1);
}
friend bool operator==(const CTxOut& a, const CTxOut& b)
{
return (a.nValue == b.nValue &&
a.scriptPubKey == b.scriptPubKey);
}
friend bool operator!=(const CTxOut& a, const CTxOut& b)
{
return !(a == b);
}
std::string ToString() const;
};
struct CMutableTransaction;
struct TransactionSerParams {
const bool allow_witness;
SER_PARAMS_OPFUNC
};
static constexpr TransactionSerParams TX_WITH_WITNESS{.allow_witness = true};
static constexpr TransactionSerParams TX_NO_WITNESS{.allow_witness = false};
/**
* Basic transaction serialization format:
* - int32_t nVersion
* - std::vector<CTxIn> vin
* - std::vector<CTxOut> vout
* - uint32_t nLockTime
*
* Extended transaction serialization format:
* - int32_t nVersion
* - unsigned char dummy = 0x00
* - unsigned char flags (!= 0)
* - std::vector<CTxIn> vin
* - std::vector<CTxOut> vout
* - if (flags & 1):
* - CScriptWitness scriptWitness; (deserialized into CTxIn)
* - uint32_t nLockTime
*/
template<typename Stream, typename TxType>
void UnserializeTransaction(TxType& tx, Stream& s, const TransactionSerParams& params)
{
const bool fAllowWitness = params.allow_witness;
s >> tx.nVersion;
unsigned char flags = 0;
tx.vin.clear();
tx.vout.clear();
/* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */
s >> tx.vin;
if (tx.vin.size() == 0 && fAllowWitness) {
/* We read a dummy or an empty vin. */
s >> flags;
if (flags != 0) {
s >> tx.vin;
s >> tx.vout;
}
} else {
/* We read a non-empty vin. Assume a normal vout follows. */
s >> tx.vout;
}
if ((flags & 1) && fAllowWitness) {
/* The witness flag is present, and we support witnesses. */
flags ^= 1;
for (size_t i = 0; i < tx.vin.size(); i++) {
s >> tx.vin[i].scriptWitness.stack;
}
if (!tx.HasWitness()) {
/* It's illegal to encode witnesses when all witness stacks are empty. */
throw std::ios_base::failure("Superfluous witness record");
}
}
if (flags) {
/* Unknown flag in the serialization */
throw std::ios_base::failure("Unknown transaction optional data");
}
s >> tx.nLockTime;
}
template<typename Stream, typename TxType>
void SerializeTransaction(const TxType& tx, Stream& s, const TransactionSerParams& params)
{
const bool fAllowWitness = params.allow_witness;
s << tx.nVersion;
unsigned char flags = 0;
// Consistency check
if (fAllowWitness) {
/* Check whether witnesses need to be serialized. */
if (tx.HasWitness()) {
flags |= 1;
}
}
if (flags) {
/* Use extended format in case witnesses are to be serialized. */
std::vector<CTxIn> vinDummy;
s << vinDummy;
s << flags;
}
s << tx.vin;
s << tx.vout;
if (flags & 1) {
for (size_t i = 0; i < tx.vin.size(); i++) {
s << tx.vin[i].scriptWitness.stack;
}
}
s << tx.nLockTime;
}
template<typename TxType>
inline CAmount CalculateOutputValue(const TxType& tx)
{
return std::accumulate(tx.vout.cbegin(), tx.vout.cend(), CAmount{0}, [](CAmount sum, const auto& txout) { return sum + txout.nValue; });
}
/** The basic transaction that is broadcasted on the network and contained in
* blocks. A transaction can contain multiple inputs and outputs.
*/
class CTransaction
{
public:
// Default transaction version.
static const int32_t CURRENT_VERSION=2;
// The local variables are made const to prevent unintended modification
// without updating the cached hash value. However, CTransaction is not
// actually immutable; deserialization and assignment are implemented,
// and bypass the constness. This is safe, as they update the entire
// structure, including the hash.
const std::vector<CTxIn> vin;
const std::vector<CTxOut> vout;
const int32_t nVersion;
const uint32_t nLockTime;
private:
/** Memory only. */
const bool m_has_witness;
const Txid hash;
const Wtxid m_witness_hash;
Txid ComputeHash() const;
Wtxid ComputeWitnessHash() const;
bool ComputeHasWitness() const;
public:
/** Convert a CMutableTransaction into a CTransaction. */
explicit CTransaction(const CMutableTransaction& tx);
explicit CTransaction(CMutableTransaction&& tx);
template <typename Stream>
inline void Serialize(Stream& s) const {
SerializeTransaction(*this, s, s.GetParams());
}
/** This deserializing constructor is provided instead of an Unserialize method.
* Unserialize is not possible, since it would require overwriting const fields. */
template <typename Stream>
CTransaction(deserialize_type, const TransactionSerParams& params, Stream& s) : CTransaction(CMutableTransaction(deserialize, params, s)) {}
template <typename Stream>
CTransaction(deserialize_type, ParamsStream<TransactionSerParams,Stream>& s) : CTransaction(CMutableTransaction(deserialize, s)) {}
bool IsNull() const {
return vin.empty() && vout.empty();
}
const Txid& GetHash() const LIFETIMEBOUND { return hash; }
const Wtxid& GetWitnessHash() const LIFETIMEBOUND { return m_witness_hash; };
// Return sum of txouts.
CAmount GetValueOut() const;
/**
* Get the total transaction size in bytes, including witness data.
* "Total Size" defined in BIP141 and BIP144.
* @return Total transaction size in bytes
*/
unsigned int GetTotalSize() const;
bool IsCoinBase() const
{
return (vin.size() == 1 && vin[0].prevout.IsNull());
}
friend bool operator==(const CTransaction& a, const CTransaction& b)
{
return a.hash == b.hash;
}
friend bool operator!=(const CTransaction& a, const CTransaction& b)
{
return a.hash != b.hash;
}
std::string ToString() const;
bool HasWitness() const { return m_has_witness; }
};
/** A mutable version of CTransaction. */
struct CMutableTransaction
{
std::vector<CTxIn> vin;
std::vector<CTxOut> vout;
int32_t nVersion;
uint32_t nLockTime;
explicit CMutableTransaction();
explicit CMutableTransaction(const CTransaction& tx);
template <typename Stream>
inline void Serialize(Stream& s) const {
SerializeTransaction(*this, s, s.GetParams());
}
template <typename Stream>
inline void Unserialize(Stream& s) {
UnserializeTransaction(*this, s, s.GetParams());
}
template <typename Stream>
CMutableTransaction(deserialize_type, const TransactionSerParams& params, Stream& s) {
UnserializeTransaction(*this, s, params);
}
template <typename Stream>
CMutableTransaction(deserialize_type, ParamsStream<TransactionSerParams,Stream>& s) {
Unserialize(s);
}
/** Compute the hash of this CMutableTransaction. This is computed on the
* fly, as opposed to GetHash() in CTransaction, which uses a cached result.
*/
Txid GetHash() const;
bool HasWitness() const
{
for (size_t i = 0; i < vin.size(); i++) {
if (!vin[i].scriptWitness.IsNull()) {
return true;
}
}
return false;
}
};
typedef std::shared_ptr<const CTransaction> CTransactionRef;
template <typename Tx> static inline CTransactionRef MakeTransactionRef(Tx&& txIn) { return std::make_shared<const CTransaction>(std::forward<Tx>(txIn)); }
/** A generic txid reference (txid or wtxid). */
class GenTxid
{
bool m_is_wtxid;
uint256 m_hash;
GenTxid(bool is_wtxid, const uint256& hash) : m_is_wtxid(is_wtxid), m_hash(hash) {}
public:
static GenTxid Txid(const uint256& hash) { return GenTxid{false, hash}; }
static GenTxid Wtxid(const uint256& hash) { return GenTxid{true, hash}; }
bool IsWtxid() const { return m_is_wtxid; }
const uint256& GetHash() const LIFETIMEBOUND { return m_hash; }
friend bool operator==(const GenTxid& a, const GenTxid& b) { return a.m_is_wtxid == b.m_is_wtxid && a.m_hash == b.m_hash; }
friend bool operator<(const GenTxid& a, const GenTxid& b) { return std::tie(a.m_is_wtxid, a.m_hash) < std::tie(b.m_is_wtxid, b.m_hash); }
};
#endif // BITCOIN_PRIMITIVES_TRANSACTION_H

302
src/i2p/random.h Normal file
View File

@ -0,0 +1,302 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_RANDOM_H
#define BITCOIN_RANDOM_H
#include "crypto/chacha20.h"
#include "crypto/common.h"
#include "span.h"
#include "uint256.h"
#include <bit>
#include <cassert>
#include <chrono>
#include <cstdint>
#include <limits>
#include <vector>
/**
* Overall design of the RNG and entropy sources.
*
* We maintain a single global 256-bit RNG state for all high-quality randomness.
* The following (classes of) functions interact with that state by mixing in new
* entropy, and optionally extracting random output from it:
*
* - The GetRand*() class of functions, as well as construction of FastRandomContext objects,
* perform 'fast' seeding, consisting of mixing in:
* - A stack pointer (indirectly committing to calling thread and call stack)
* - A high-precision timestamp (rdtsc when available, c++ high_resolution_clock otherwise)
* - 64 bits from the hardware RNG (rdrand) when available.
* These entropy sources are very fast, and only designed to protect against situations
* where a VM state restore/copy results in multiple systems with the same randomness.
* FastRandomContext on the other hand does not protect against this once created, but
* is even faster (and acceptable to use inside tight loops).
*
* - The GetStrongRand*() class of function perform 'slow' seeding, including everything
* that fast seeding includes, but additionally:
* - OS entropy (/dev/urandom, getrandom(), ...). The application will terminate if
* this entropy source fails.
* - Another high-precision timestamp (indirectly committing to a benchmark of all the
* previous sources).
* These entropy sources are slower, but designed to make sure the RNG state contains
* fresh data that is unpredictable to attackers.
*
* - RandAddPeriodic() seeds everything that fast seeding includes, but additionally:
* - A high-precision timestamp
* - Dynamic environment data (performance monitoring, ...)
* - Strengthen the entropy for 10 ms using repeated SHA512.
* This is run once every minute.
*
* On first use of the RNG (regardless of what function is called first), all entropy
* sources used in the 'slow' seeder are included, but also:
* - 256 bits from the hardware RNG (rdseed or rdrand) when available.
* - Dynamic environment data (performance monitoring, ...)
* - Static environment data
* - Strengthen the entropy for 100 ms using repeated SHA512.
*
* When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and
* (up to) the first 32 bytes of H are produced as output, while the last 32 bytes
* become the new RNG state.
*/
/**
* Generate random data via the internal PRNG.
*
* These functions are designed to be fast (sub microsecond), but do not necessarily
* meaningfully add entropy to the PRNG state.
*
* Thread-safe.
*/
void GetRandBytes(Span<unsigned char> bytes) noexcept;
/** Generate a uniform random integer in the range [0..range). Precondition: range > 0 */
uint64_t GetRandInternal(uint64_t nMax) noexcept;
/** Generate a uniform random integer of type T in the range [0..nMax)
* nMax defaults to std::numeric_limits<T>::max()
* Precondition: nMax > 0, T is an integral type, no larger than uint64_t
*/
template<typename T>
T GetRand(T nMax=std::numeric_limits<T>::max()) noexcept {
static_assert(std::is_integral<T>(), "T must be integral");
static_assert(std::numeric_limits<T>::max() <= std::numeric_limits<uint64_t>::max(), "GetRand only supports up to uint64_t");
return T(GetRandInternal(nMax));
}
/** Generate a uniform random duration in the range [0..max). Precondition: max.count() > 0 */
template <typename D>
D GetRandomDuration(typename std::common_type<D>::type max) noexcept
// Having the compiler infer the template argument from the function argument
// is dangerous, because the desired return value generally has a different
// type than the function argument. So std::common_type is used to force the
// call site to specify the type of the return value.
{
assert(max.count() > 0);
return D{GetRand(max.count())};
};
constexpr auto GetRandMicros = GetRandomDuration<std::chrono::microseconds>;
constexpr auto GetRandMillis = GetRandomDuration<std::chrono::milliseconds>;
/**
* Return a timestamp in the future sampled from an exponential distribution
* (https://en.wikipedia.org/wiki/Exponential_distribution). This distribution
* is memoryless and should be used for repeated network events (e.g. sending a
* certain type of message) to minimize leaking information to observers.
*
* The probability of an event occurring before time x is 1 - e^-(x/a) where a
* is the average interval between events.
* */
std::chrono::microseconds GetExponentialRand(std::chrono::microseconds now, std::chrono::seconds average_interval);
uint256 GetRandHash() noexcept;
/**
* Gather entropy from various sources, feed it into the internal PRNG, and
* generate random data using it.
*
* This function will cause failure whenever the OS RNG fails.
*
* Thread-safe.
*/
void GetStrongRandBytes(Span<unsigned char> bytes) noexcept;
/**
* Gather entropy from various expensive sources, and feed them to the PRNG state.
*
* Thread-safe.
*/
void RandAddPeriodic() noexcept;
/**
* Gathers entropy from the low bits of the time at which events occur. Should
* be called with a uint32_t describing the event at the time an event occurs.
*
* Thread-safe.
*/
void RandAddEvent(const uint32_t event_info) noexcept;
/**
* Fast randomness source. This is seeded once with secure random data, but
* is completely deterministic and does not gather more entropy after that.
*
* This class is not thread-safe.
*/
class FastRandomContext
{
private:
bool requires_seed;
ChaCha20 rng;
uint64_t bitbuf;
int bitbuf_size;
void RandomSeed();
void FillBitBuffer()
{
bitbuf = rand64();
bitbuf_size = 64;
}
public:
explicit FastRandomContext(bool fDeterministic = false) noexcept;
/** Initialize with explicit seed (only for testing) */
explicit FastRandomContext(const uint256& seed) noexcept;
// Do not permit copying a FastRandomContext (move it, or create a new one to get reseeded).
FastRandomContext(const FastRandomContext&) = delete;
FastRandomContext(FastRandomContext&&) = delete;
FastRandomContext& operator=(const FastRandomContext&) = delete;
/** Move a FastRandomContext. If the original one is used again, it will be reseeded. */
FastRandomContext& operator=(FastRandomContext&& from) noexcept;
/** Generate a random 64-bit integer. */
uint64_t rand64() noexcept
{
if (requires_seed) RandomSeed();
std::array<std::byte, 8> buf;
rng.Keystream(buf);
return ReadLE64(UCharCast(buf.data()));
}
/** Generate a random (bits)-bit integer. */
uint64_t randbits(int bits) noexcept
{
if (bits == 0) {
return 0;
} else if (bits > 32) {
return rand64() >> (64 - bits);
} else {
if (bitbuf_size < bits) FillBitBuffer();
uint64_t ret = bitbuf & (~uint64_t{0} >> (64 - bits));
bitbuf >>= bits;
bitbuf_size -= bits;
return ret;
}
}
/** Generate a random integer in the range [0..range).
* Precondition: range > 0.
*/
uint64_t randrange(uint64_t range) noexcept
{
assert(range);
--range;
int bits = std::bit_width(range);
while (true) {
uint64_t ret = randbits(bits);
if (ret <= range) return ret;
}
}
/** Generate random bytes. */
template <typename B = unsigned char>
std::vector<B> randbytes(size_t len);
/** Fill a byte Span with random bytes. */
void fillrand(Span<std::byte> output);
/** Generate a random 32-bit integer. */
uint32_t rand32() noexcept { return randbits(32); }
/** generate a random uint256. */
uint256 rand256() noexcept;
/** Generate a random boolean. */
bool randbool() noexcept { return randbits(1); }
/** Return the time point advanced by a uniform random duration. */
template <typename Tp>
Tp rand_uniform_delay(const Tp& time, typename Tp::duration range)
{
return time + rand_uniform_duration<Tp>(range);
}
/** Generate a uniform random duration in the range from 0 (inclusive) to range (exclusive). */
template <typename Chrono>
typename Chrono::duration rand_uniform_duration(typename Chrono::duration range) noexcept
{
using Dur = typename Chrono::duration;
return range.count() > 0 ? /* interval [0..range) */ Dur{randrange(range.count())} :
range.count() < 0 ? /* interval (range..0] */ -Dur{randrange(-range.count())} :
/* interval [0..0] */ Dur{0};
};
// Compatibility with the UniformRandomBitGenerator concept
typedef uint64_t result_type;
static constexpr uint64_t min() { return 0; }
static constexpr uint64_t max() { return std::numeric_limits<uint64_t>::max(); }
inline uint64_t operator()() noexcept { return rand64(); }
};
/** More efficient than using std::shuffle on a FastRandomContext.
*
* This is more efficient as std::shuffle will consume entropy in groups of
* 64 bits at the time and throw away most.
*
* This also works around a bug in libstdc++ std::shuffle that may cause
* type::operator=(type&&) to be invoked on itself, which the library's
* debug mode detects and panics on. This is a known issue, see
* https://stackoverflow.com/questions/22915325/avoiding-self-assignment-in-stdshuffle
*/
template <typename I, typename R>
void Shuffle(I first, I last, R&& rng)
{
while (first != last) {
size_t j = rng.randrange(last - first);
if (j) {
using std::swap;
swap(*first, *(first + j));
}
++first;
}
}
/* Number of random bytes returned by GetOSRand.
* When changing this constant make sure to change all call sites, and make
* sure that the underlying OS APIs for all platforms support the number.
* (many cap out at 256 bytes).
*/
static const int NUM_OS_RANDOM_BYTES = 32;
/** Get 32 bytes of system entropy. Do not use this in application code: use
* GetStrongRandBytes instead.
*/
void GetOSRand(unsigned char* ent32);
/** Check that OS randomness is available and returning the requested number
* of bytes.
*/
bool Random_SanityCheck();
/**
* Initialize global RNG state and log any CPU features that are used.
*
* Calling this function is optional. RNG state will be initialized when first
* needed if it is not called.
*/
void RandomInit();
#endif // BITCOIN_RANDOM_H

1187
src/i2p/serialize.h Normal file

File diff suppressed because it is too large Load Diff

354
src/i2p/span.h Normal file
View File

@ -0,0 +1,354 @@
// Copyright (c) 2018-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_SPAN_H
#define BITCOIN_SPAN_H
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <span>
#include <type_traits>
#ifdef DEBUG
#define CONSTEXPR_IF_NOT_DEBUG
#define ASSERT_IF_DEBUG(x) assert((x))
#else
#define CONSTEXPR_IF_NOT_DEBUG constexpr
#define ASSERT_IF_DEBUG(x)
#endif
#if defined(__clang__)
#if __has_attribute(lifetimebound)
#define SPAN_ATTR_LIFETIMEBOUND [[clang::lifetimebound]]
#else
#define SPAN_ATTR_LIFETIMEBOUND
#endif
#else
#define SPAN_ATTR_LIFETIMEBOUND
#endif
/** A Span is an object that can refer to a contiguous sequence of objects.
*
* This file implements a subset of C++20's std::span. It can be considered
* temporary compatibility code until C++20 and is designed to be a
* self-contained abstraction without depending on other project files. For this
* reason, Clang lifetimebound is defined here instead of including
* <attributes.h>, which also defines it.
*
* Things to be aware of when writing code that deals with Spans:
*
* - Similar to references themselves, Spans are subject to reference lifetime
* issues. The user is responsible for making sure the objects pointed to by
* a Span live as long as the Span is used. For example:
*
* std::vector<int> vec{1,2,3,4};
* Span<int> sp(vec);
* vec.push_back(5);
* printf("%i\n", sp.front()); // UB!
*
* may exhibit undefined behavior, as increasing the size of a vector may
* invalidate references.
*
* - One particular pitfall is that Spans can be constructed from temporaries,
* but this is unsafe when the Span is stored in a variable, outliving the
* temporary. For example, this will compile, but exhibits undefined behavior:
*
* Span<const int> sp(std::vector<int>{1, 2, 3});
* printf("%i\n", sp.front()); // UB!
*
* The lifetime of the vector ends when the statement it is created in ends.
* Thus the Span is left with a dangling reference, and using it is undefined.
*
* - Due to Span's automatic creation from range-like objects (arrays, and data
* types that expose a data() and size() member function), functions that
* accept a Span as input parameter can be called with any compatible
* range-like object. For example, this works:
*
* void Foo(Span<const int> arg);
*
* Foo(std::vector<int>{1, 2, 3}); // Works
*
* This is very useful in cases where a function truly does not care about the
* container, and only about having exactly a range of elements. However it
* may also be surprising to see automatic conversions in this case.
*
* When a function accepts a Span with a mutable element type, it will not
* accept temporaries; only variables or other references. For example:
*
* void FooMut(Span<int> arg);
*
* FooMut(std::vector<int>{1, 2, 3}); // Does not compile
* std::vector<int> baz{1, 2, 3};
* FooMut(baz); // Works
*
* This is similar to how functions that take (non-const) lvalue references
* as input cannot accept temporaries. This does not work either:
*
* void FooVec(std::vector<int>& arg);
* FooVec(std::vector<int>{1, 2, 3}); // Does not compile
*
* The idea is that if a function accepts a mutable reference, a meaningful
* result will be present in that variable after the call. Passing a temporary
* is useless in that context.
*/
template <typename C> class Span {
C *m_data;
std::size_t m_size{0};
template <class T> struct is_Span_int : public std::false_type {};
template <class T> struct is_Span_int<Span<T>> : public std::true_type {};
template <class T>
struct is_Span : public is_Span_int<typename std::remove_cv<T>::type> {};
public:
constexpr Span() noexcept : m_data(nullptr) {}
/** Construct a span from a begin pointer and a size.
*
* This implements a subset of the iterator-based std::span constructor in
* C++20, which is hard to implement without std::address_of.
*/
template <typename T,
typename std::enable_if<
std::is_convertible<T (*)[], C (*)[]>::value, int>::type = 0>
constexpr Span(T *begin, std::size_t size) noexcept
: m_data(begin), m_size(size) {}
/** Construct a span from a begin and end pointer.
*
* This implements a subset of the iterator-based std::span constructor in
* C++20, which is hard to implement without std::address_of.
*/
template <typename T,
typename std::enable_if<
std::is_convertible<T (*)[], C (*)[]>::value, int>::type = 0>
CONSTEXPR_IF_NOT_DEBUG Span(T *begin, T *end) noexcept
: m_data(begin), m_size(end - begin) {
ASSERT_IF_DEBUG(end >= begin);
}
/** Implicit conversion of spans between compatible types.
*
* Specifically, if a pointer to an array of type O can be implicitly
* converted to a pointer to an array of type C, then permit implicit
* conversion of Span<O> to Span<C>. This matches the behavior of the
* corresponding C++20 std::span constructor.
*
* For example this means that a Span<T> can be converted into a Span<const
* T>.
*/
template <typename O,
typename std::enable_if<
std::is_convertible<O (*)[], C (*)[]>::value, int>::type = 0>
constexpr Span(const Span<O> &other) noexcept
: m_data(other.m_data), m_size(other.m_size) {}
/** Default copy constructor. */
constexpr Span(const Span &) noexcept = default;
/** Default assignment operator. */
Span &operator=(const Span &other) noexcept = default;
/** Construct a Span from an array. This matches the corresponding C++20
* std::span constructor. */
template <int N> constexpr Span(C (&a)[N]) noexcept : m_data(a), m_size(N) {}
/** Construct a Span for objects with .data() and .size() (std::string,
* std::array, std::vector, ...).
*
* This implements a subset of the functionality provided by the C++20
* std::span range-based constructor.
*
* To prevent surprises, only Spans for constant value types are supported
* when passing in temporaries. Note that this restriction does not exist when
* converting arrays or other Spans (see above).
*/
template <typename V>
constexpr Span(
V &other SPAN_ATTR_LIFETIMEBOUND,
typename std::enable_if<
!is_Span<V>::value &&
std::is_convertible<
typename std::remove_pointer<
decltype(std::declval<V &>().data())>::type (*)[],
C (*)[]>::value &&
std::is_convertible<decltype(std::declval<V &>().size()),
std::size_t>::value,
std::nullptr_t>::type = nullptr)
: m_data(other.data()), m_size(other.size()) {}
template <typename V>
constexpr Span(
const V &other SPAN_ATTR_LIFETIMEBOUND,
typename std::enable_if<
!is_Span<V>::value &&
std::is_convertible<
typename std::remove_pointer<
decltype(std::declval<const V &>().data())>::type (*)[],
C (*)[]>::value &&
std::is_convertible<decltype(std::declval<const V &>().size()),
std::size_t>::value,
std::nullptr_t>::type = nullptr)
: m_data(other.data()), m_size(other.size()) {}
constexpr C *data() const noexcept { return m_data; }
constexpr C *begin() const noexcept { return m_data; }
constexpr C *end() const noexcept { return m_data + m_size; }
CONSTEXPR_IF_NOT_DEBUG C &front() const noexcept {
ASSERT_IF_DEBUG(size() > 0);
return m_data[0];
}
CONSTEXPR_IF_NOT_DEBUG C &back() const noexcept {
ASSERT_IF_DEBUG(size() > 0);
return m_data[m_size - 1];
}
constexpr std::size_t size() const noexcept { return m_size; }
constexpr std::size_t size_bytes() const noexcept {
return sizeof(C) * m_size;
}
constexpr bool empty() const noexcept { return size() == 0; }
CONSTEXPR_IF_NOT_DEBUG C &operator[](std::size_t pos) const noexcept {
ASSERT_IF_DEBUG(size() > pos);
return m_data[pos];
}
CONSTEXPR_IF_NOT_DEBUG Span<C> subspan(std::size_t offset) const noexcept {
ASSERT_IF_DEBUG(size() >= offset);
return Span<C>(m_data + offset, m_size - offset);
}
CONSTEXPR_IF_NOT_DEBUG Span<C> subspan(std::size_t offset,
std::size_t count) const noexcept {
ASSERT_IF_DEBUG(size() >= offset + count);
return Span<C>(m_data + offset, count);
}
CONSTEXPR_IF_NOT_DEBUG Span<C> first(std::size_t count) const noexcept {
ASSERT_IF_DEBUG(size() >= count);
return Span<C>(m_data, count);
}
CONSTEXPR_IF_NOT_DEBUG Span<C> last(std::size_t count) const noexcept {
ASSERT_IF_DEBUG(size() >= count);
return Span<C>(m_data + m_size - count, count);
}
friend constexpr bool operator==(const Span &a, const Span &b) noexcept {
return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
}
friend constexpr bool operator!=(const Span &a, const Span &b) noexcept {
return !(a == b);
}
friend constexpr bool operator<(const Span &a, const Span &b) noexcept {
return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
}
friend constexpr bool operator<=(const Span &a, const Span &b) noexcept {
return !(b < a);
}
friend constexpr bool operator>(const Span &a, const Span &b) noexcept {
return (b < a);
}
friend constexpr bool operator>=(const Span &a, const Span &b) noexcept {
return !(a < b);
}
template <typename O> friend class Span;
};
// Return result of calling .data() method on type T. This is used to be able to
// write template deduction guides for the single-parameter Span constructor
// below that will work if the value that is passed has a .data() method, and if
// the data method does not return a void pointer.
//
// It is important to check for the void type specifically below, so the
// deduction guides can be used in SFINAE contexts to check whether objects can
// be converted to spans. If the deduction guides did not explicitly check for
// void, and an object was passed that returned void* from data (like
// std::vector<bool>), the template deduction would succeed, but the Span<void>
// object instantiation would fail, resulting in a hard error, rather than a
// SFINAE error.
// https://stackoverflow.com/questions/68759148/sfinae-to-detect-the-explicitness-of-a-ctad-deduction-guide
// https://stackoverflow.com/questions/16568986/what-happens-when-you-call-data-on-a-stdvectorbool
template <typename T>
using DataResult = std::remove_pointer_t<decltype(std::declval<T &>().data())>;
// Deduction guides for Span
// For the pointer/size based and iterator based constructor:
template <typename T, typename EndOrSize> Span(T *, EndOrSize) -> Span<T>;
// For the array constructor:
template <typename T, std::size_t N> Span(T (&)[N]) -> Span<T>;
// For the temporaries/rvalue references constructor, only supporting const
// output.
template <typename T>
Span(T &&) -> Span<std::enable_if_t<!std::is_lvalue_reference_v<T> &&
!std::is_void_v<DataResult<T &&>>,
const DataResult<T &&>>>;
// For (lvalue) references, supporting mutable output.
template <typename T>
Span(T &) -> Span<
std::enable_if_t<!std::is_void_v<DataResult<T &>>, DataResult<T &>>>;
/** Pop the last element off a span, and return a reference to that element. */
template <typename T> T &SpanPopBack(Span<T> &span) {
size_t size = span.size();
ASSERT_IF_DEBUG(size > 0);
T &back = span[size - 1];
span = Span<T>(span.data(), size - 1);
return back;
}
// From C++20 as_bytes and as_writeable_bytes
template <typename T> Span<const std::byte> AsBytes(Span<T> s) noexcept {
return {reinterpret_cast<const std::byte *>(s.data()), s.size_bytes()};
}
template <typename T> Span<std::byte> AsWritableBytes(Span<T> s) noexcept {
return {reinterpret_cast<std::byte *>(s.data()), s.size_bytes()};
}
template <typename V> Span<const std::byte> MakeByteSpan(V &&v) noexcept {
return AsBytes(Span{std::forward<V>(v)});
}
template <typename V> Span<std::byte> MakeWritableByteSpan(V &&v) noexcept {
return AsWritableBytes(Span{std::forward<V>(v)});
}
// Helper functions to safely cast basic byte pointers to unsigned char
// pointers.
inline unsigned char *UCharCast(char *c) {
return reinterpret_cast<unsigned char *>(c);
}
inline unsigned char *UCharCast(unsigned char *c) { return c; }
inline unsigned char *UCharCast(signed char *c) {
return reinterpret_cast<unsigned char *>(c);
}
inline unsigned char *UCharCast(std::byte *c) {
return reinterpret_cast<unsigned char *>(c);
}
inline const unsigned char *UCharCast(const char *c) {
return reinterpret_cast<const unsigned char *>(c);
}
inline const unsigned char *UCharCast(const unsigned char *c) { return c; }
inline const unsigned char *UCharCast(const signed char *c) {
return reinterpret_cast<const unsigned char *>(c);
}
inline const unsigned char *UCharCast(const std::byte *c) {
return reinterpret_cast<const unsigned char *>(c);
}
// Helper concept for the basic byte types.
template <typename B>
concept BasicByte = requires { UCharCast(std::span<B>{}.data()); };
// Helper function to safely convert a Span to a Span<[const] unsigned char>.
template <typename T>
constexpr auto UCharSpanCast(Span<T> s)
-> Span<typename std::remove_pointer<decltype(UCharCast(s.data()))>::type> {
return {UCharCast(s.data()), s.size()};
}
/** Like the Span constructor, but for (const) unsigned char member types only.
* Only works for (un)signed char containers. */
template <typename V>
constexpr auto MakeUCharSpan(V &&v)
-> decltype(UCharSpanCast(Span{std::forward<V>(v)})) {
return UCharSpanCast(Span{std::forward<V>(v)});
}
#endif // BITCOIN_SPAN_H

331
src/i2p/sync.cpp Normal file
View File

@ -0,0 +1,331 @@
// Copyright (c) 2011-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "sync.h"
#include "logging.h"
#include "tinyformat.h"
#include "util/strencodings.h"
// #include "util/threadnames.h"
#include <map>
#include <mutex>
#include <set>
#include <system_error>
#include <thread>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
#ifdef DEBUG_LOCKORDER
//
// Early deadlock detection.
// Problem being solved:
// Thread 1 locks A, then B, then C
// Thread 2 locks D, then C, then A
// --> may result in deadlock between the two threads, depending on when
// they run.
// Solution implemented here:
// Keep track of pairs of locks: (A before B), (A before C), etc.
// Complain if any thread tries to lock in a different order.
//
struct CLockLocation {
CLockLocation(const char *pszName, const char *pszFile, int nLine,
bool fTryIn, const std::string &thread_name)
: fTry(fTryIn), mutexName(pszName), sourceFile(pszFile),
m_thread_name(thread_name), sourceLine(nLine) {}
std::string ToString() const {
return strprintf("'%s' in %s:%s%s (in thread '%s')", mutexName, sourceFile,
sourceLine, (fTry ? " (TRY)" : ""), m_thread_name);
}
std::string Name() const { return mutexName; }
private:
bool fTry;
std::string mutexName;
std::string sourceFile;
const std::string &m_thread_name;
int sourceLine;
};
using LockStackItem = std::pair<void *, CLockLocation>;
using LockStack = std::vector<LockStackItem>;
using LockStacks = std::unordered_map<std::thread::id, LockStack>;
using LockPair = std::pair<void *, void *>;
using LockOrders = std::map<LockPair, LockStack>;
using InvLockOrders = std::set<LockPair>;
struct LockData {
LockStacks m_lock_stacks;
LockOrders lockorders;
InvLockOrders invlockorders;
std::mutex dd_mutex;
};
LockData &GetLockData() {
// This approach guarantees that the object is not destroyed until after its
// last use. The operating system automatically reclaims all the memory in a
// program's heap when that program exits. Since the ~LockData() destructor is
// never called, the LockData class and all its subclasses must have
// implicitly-defined destructors.
static LockData &lock_data = *new LockData();
return lock_data;
}
static void potential_deadlock_detected(const LockPair &mismatch,
const LockStack &s1,
const LockStack &s2) {
LogPrintf("POTENTIAL DEADLOCK DETECTED\n");
LogPrintf("Previous lock order was:\n");
for (const LockStackItem &i : s1) {
std::string prefix{};
if (i.first == mismatch.first) {
prefix = " (1)";
}
if (i.first == mismatch.second) {
prefix = " (2)";
}
LogPrintf("%s %s\n", prefix, i.second.ToString());
}
std::string mutex_a, mutex_b;
LogPrintf("Current lock order is:\n");
for (const LockStackItem &i : s2) {
std::string prefix{};
if (i.first == mismatch.first) {
prefix = " (1)";
mutex_a = i.second.Name();
}
if (i.first == mismatch.second) {
prefix = " (2)";
mutex_b = i.second.Name();
}
LogPrintf("%s %s\n", prefix, i.second.ToString());
}
if (g_debug_lockorder_abort) {
tfm::format(std::cerr,
"Assertion failed: detected inconsistent lock order for %s, "
"details in debug log.\n",
s2.back().second.ToString());
abort();
}
throw std::logic_error(
strprintf("potential deadlock detected: %s -> %s -> %s", mutex_b, mutex_a,
mutex_b));
}
static void double_lock_detected(const void *mutex,
const LockStack &lock_stack) {
LogPrintf("DOUBLE LOCK DETECTED\n");
LogPrintf("Lock order:\n");
for (const LockStackItem &i : lock_stack) {
std::string prefix{};
if (i.first == mutex) {
prefix = " (*)";
}
LogPrintf("%s %s\n", prefix, i.second.ToString());
}
if (g_debug_lockorder_abort) {
tfm::format(std::cerr,
"Assertion failed: detected double lock for %s, details in "
"debug log.\n",
lock_stack.back().second.ToString());
abort();
}
throw std::logic_error("double lock detected");
}
template <typename MutexType>
static void push_lock(MutexType *c, const CLockLocation &locklocation) {
constexpr bool is_recursive_mutex =
std::is_base_of<RecursiveMutex, MutexType>::value ||
std::is_base_of<std::recursive_mutex, MutexType>::value;
LockData &lockdata = GetLockData();
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
lock_stack.emplace_back(c, locklocation);
for (size_t j = 0; j < lock_stack.size() - 1; ++j) {
const LockStackItem &i = lock_stack[j];
if (i.first == c) {
if (is_recursive_mutex) {
break;
}
// It is not a recursive mutex and it appears in the stack two times:
// at position `j` and at the end (which we added just before this loop).
// Can't allow locking the same (non-recursive) mutex two times from the
// same thread as that results in an undefined behavior.
auto lock_stack_copy = lock_stack;
lock_stack.pop_back();
double_lock_detected(c, lock_stack_copy);
// double_lock_detected() does not return.
}
const LockPair p1 = std::make_pair(i.first, c);
if (lockdata.lockorders.count(p1))
continue;
const LockPair p2 = std::make_pair(c, i.first);
if (lockdata.lockorders.count(p2)) {
auto lock_stack_copy = lock_stack;
lock_stack.pop_back();
potential_deadlock_detected(p1, lockdata.lockorders[p2], lock_stack_copy);
// potential_deadlock_detected() does not return.
}
lockdata.lockorders.emplace(p1, lock_stack);
lockdata.invlockorders.insert(p2);
}
}
static void pop_lock() {
LockData &lockdata = GetLockData();
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
lock_stack.pop_back();
if (lock_stack.empty()) {
lockdata.m_lock_stacks.erase(std::this_thread::get_id());
}
}
template <typename MutexType>
void EnterCritical(const char *pszName, const char *pszFile, int nLine,
MutexType *cs, bool fTry) {
push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry,
util::ThreadGetInternalName()));
}
template void EnterCritical(const char *, const char *, int, Mutex *, bool);
template void EnterCritical(const char *, const char *, int, RecursiveMutex *,
bool);
template void EnterCritical(const char *, const char *, int, std::mutex *,
bool);
template void EnterCritical(const char *, const char *, int,
std::recursive_mutex *, bool);
void CheckLastCritical(void *cs, std::string &lockname, const char *guardname,
const char *file, int line) {
LockData &lockdata = GetLockData();
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
const LockStack &lock_stack =
lockdata.m_lock_stacks[std::this_thread::get_id()];
if (!lock_stack.empty()) {
const auto &lastlock = lock_stack.back();
if (lastlock.first == cs) {
lockname = lastlock.second.Name();
return;
}
}
LogPrintf("INCONSISTENT LOCK ORDER DETECTED\n");
LogPrintf("Current lock order (least recent first) is:\n");
for (const LockStackItem &i : lock_stack) {
LogPrintf(" %s\n", i.second.ToString());
}
if (g_debug_lockorder_abort) {
tfm::format(std::cerr,
"%s:%s %s was not most recent critical section locked, details "
"in debug log.\n",
file, line, guardname);
abort();
}
throw std::logic_error(
strprintf("%s was not most recent critical section locked", guardname));
}
void LeaveCritical() { pop_lock(); }
static std::string LocksHeld() {
LockData &lockdata = GetLockData();
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
const LockStack &lock_stack =
lockdata.m_lock_stacks[std::this_thread::get_id()];
std::string result;
for (const LockStackItem &i : lock_stack)
result += i.second.ToString() + std::string("\n");
return result;
}
static bool LockHeld(void *mutex) {
LockData &lockdata = GetLockData();
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
const LockStack &lock_stack =
lockdata.m_lock_stacks[std::this_thread::get_id()];
for (const LockStackItem &i : lock_stack) {
if (i.first == mutex)
return true;
}
return false;
}
template <typename MutexType>
void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine,
MutexType *cs) {
if (LockHeld(cs))
return;
tfm::format(std::cerr,
"Assertion failed: lock %s not held in %s:%i; locks held:\n%s",
pszName, pszFile, nLine, LocksHeld());
abort();
}
template void AssertLockHeldInternal(const char *, const char *, int, Mutex *);
template void AssertLockHeldInternal(const char *, const char *, int,
RecursiveMutex *);
template <typename MutexType>
void AssertLockNotHeldInternal(const char *pszName, const char *pszFile,
int nLine, MutexType *cs) {
if (!LockHeld(cs))
return;
tfm::format(std::cerr,
"Assertion failed: lock %s held in %s:%i; locks held:\n%s",
pszName, pszFile, nLine, LocksHeld());
abort();
}
template void AssertLockNotHeldInternal(const char *, const char *, int,
Mutex *);
template void AssertLockNotHeldInternal(const char *, const char *, int,
RecursiveMutex *);
void DeleteLock(void *cs) {
LockData &lockdata = GetLockData();
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
const LockPair item = std::make_pair(cs, nullptr);
LockOrders::iterator it = lockdata.lockorders.lower_bound(item);
while (it != lockdata.lockorders.end() && it->first.first == cs) {
const LockPair invitem = std::make_pair(it->first.second, it->first.first);
lockdata.invlockorders.erase(invitem);
lockdata.lockorders.erase(it++);
}
InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item);
while (invit != lockdata.invlockorders.end() && invit->first == cs) {
const LockPair invinvitem = std::make_pair(invit->second, invit->first);
lockdata.lockorders.erase(invinvitem);
lockdata.invlockorders.erase(invit++);
}
}
bool LockStackEmpty() {
LockData &lockdata = GetLockData();
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
const auto it = lockdata.m_lock_stacks.find(std::this_thread::get_id());
if (it == lockdata.m_lock_stacks.end()) {
return true;
}
return it->second.empty();
}
bool g_debug_lockorder_abort = true;
#endif /* DEBUG_LOCKORDER */

430
src/i2p/sync.h Normal file
View File

@ -0,0 +1,430 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_SYNC_H
#define BITCOIN_SYNC_H
#ifdef DEBUG_LOCKCONTENTION
//#include <logging.h>
//#include <logging/timer.h>
#endif
#include "threadsafety.h" // IWYU pragma: export
#include "util/macros.h"
#include <condition_variable>
#include <mutex>
#include <string>
#include <thread>
////////////////////////////////////////////////
// //
// THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE //
// //
////////////////////////////////////////////////
/*
RecursiveMutex mutex;
std::recursive_mutex mutex;
LOCK(mutex);
std::unique_lock<std::recursive_mutex> criticalblock(mutex);
LOCK2(mutex1, mutex2);
std::unique_lock<std::recursive_mutex> criticalblock1(mutex1);
std::unique_lock<std::recursive_mutex> criticalblock2(mutex2);
TRY_LOCK(mutex, name);
std::unique_lock<std::recursive_mutex> name(mutex, std::try_to_lock_t);
ENTER_CRITICAL_SECTION(mutex); // no RAII
mutex.lock();
LEAVE_CRITICAL_SECTION(mutex); // no RAII
mutex.unlock();
*/
///////////////////////////////
// //
// THE ACTUAL IMPLEMENTATION //
// //
///////////////////////////////
#ifdef DEBUG_LOCKORDER
template <typename MutexType>
void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false);
void LeaveCritical();
void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
template <typename MutexType>
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs);
template <typename MutexType>
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs);
void DeleteLock(void* cs);
bool LockStackEmpty();
/**
* Call abort() if a potential lock order deadlock bug is detected, instead of
* just logging information and throwing a logic_error. Defaults to true, and
* set to false in DEBUG_LOCKORDER unit tests.
*/
extern bool g_debug_lockorder_abort;
#else
template <typename MutexType>
inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false) {}
inline void LeaveCritical() {}
inline void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
template <typename MutexType>
inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs) {}
template <typename MutexType>
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs) {}
inline void DeleteLock(void* cs) {}
inline bool LockStackEmpty() { return true; }
#endif
/**
* Template mixin that adds -Wthread-safety locking annotations and lock order
* checking to a subset of the mutex API.
*/
template <typename PARENT>
class LOCKABLE AnnotatedMixin : public PARENT
{
public:
~AnnotatedMixin() {
DeleteLock((void*)this);
}
void lock() EXCLUSIVE_LOCK_FUNCTION()
{
PARENT::lock();
}
void unlock() UNLOCK_FUNCTION()
{
PARENT::unlock();
}
bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true)
{
return PARENT::try_lock();
}
using unique_lock = std::unique_lock<PARENT>;
#ifdef __clang__
//! For negative capabilities in the Clang Thread Safety Analysis.
//! A negative requirement uses the EXCLUSIVE_LOCKS_REQUIRED attribute, in conjunction
//! with the ! operator, to indicate that a mutex should not be held.
const AnnotatedMixin& operator!() const { return *this; }
#endif // __clang__
};
/**
* Wrapped mutex: supports recursive locking, but no waiting
* TODO: We should move away from using the recursive lock by default.
*/
using RecursiveMutex = AnnotatedMixin<std::recursive_mutex>;
/** Wrapped mutex: supports waiting but not recursive locking */
using Mutex = AnnotatedMixin<std::mutex>;
/** Different type to mark Mutex at global scope
*
* Thread safety analysis can't handle negative assertions about mutexes
* with global scope well, so mark them with a separate type, and
* eventually move all the mutexes into classes so they are not globally
* visible.
*
* See: https://github.com/bitcoin/bitcoin/pull/20272#issuecomment-720755781
*/
class GlobalMutex : public Mutex { };
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
inline void AssertLockNotHeldInline(const char* name, const char* file, int line, Mutex* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) { AssertLockNotHeldInternal(name, file, line, cs); }
inline void AssertLockNotHeldInline(const char* name, const char* file, int line, RecursiveMutex* cs) LOCKS_EXCLUDED(cs) { AssertLockNotHeldInternal(name, file, line, cs); }
inline void AssertLockNotHeldInline(const char* name, const char* file, int line, GlobalMutex* cs) LOCKS_EXCLUDED(cs) { AssertLockNotHeldInternal(name, file, line, cs); }
#define AssertLockNotHeld(cs) AssertLockNotHeldInline(#cs, __FILE__, __LINE__, &cs)
/** Wrapper around std::unique_lock style lock for MutexType. */
template <typename MutexType>
class SCOPED_LOCKABLE UniqueLock : public MutexType::unique_lock
{
private:
using Base = typename MutexType::unique_lock;
void Enter(const char* pszName, const char* pszFile, int nLine)
{
EnterCritical(pszName, pszFile, nLine, Base::mutex());
#ifdef DEBUG_LOCKCONTENTION
if (Base::try_lock()) return;
LOG_TIME_MICROS_WITH_CATEGORY(strprintf("lock contention %s, %s:%d", pszName, pszFile, nLine), BCLog::LOCK);
#endif
Base::lock();
}
bool TryEnter(const char* pszName, const char* pszFile, int nLine)
{
EnterCritical(pszName, pszFile, nLine, Base::mutex(), true);
if (Base::try_lock()) {
return true;
}
LeaveCritical();
return false;
}
public:
UniqueLock(MutexType& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock)
{
if (fTry)
TryEnter(pszName, pszFile, nLine);
else
Enter(pszName, pszFile, nLine);
}
UniqueLock(MutexType* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
{
if (!pmutexIn) return;
*static_cast<Base*>(this) = Base(*pmutexIn, std::defer_lock);
if (fTry)
TryEnter(pszName, pszFile, nLine);
else
Enter(pszName, pszFile, nLine);
}
~UniqueLock() UNLOCK_FUNCTION()
{
if (Base::owns_lock())
LeaveCritical();
}
operator bool()
{
return Base::owns_lock();
}
protected:
// needed for reverse_lock
UniqueLock() { }
public:
/**
* An RAII-style reverse lock. Unlocks on construction and locks on destruction.
*/
class reverse_lock {
public:
explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) {
CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line);
lock.unlock();
LeaveCritical();
lock.swap(templock);
}
~reverse_lock() {
templock.swap(lock);
EnterCritical(lockname.c_str(), file.c_str(), line, lock.mutex());
lock.lock();
}
private:
reverse_lock(reverse_lock const&);
reverse_lock& operator=(reverse_lock const&);
UniqueLock& lock;
UniqueLock templock;
std::string lockname;
const std::string file;
const int line;
};
friend class reverse_lock;
};
#define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock UNIQUE_NAME(revlock)(g, #g, __FILE__, __LINE__)
// When locking a Mutex, require negative capability to ensure the lock
// is not already held
inline Mutex& MaybeCheckNotHeld(Mutex& cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) LOCK_RETURNED(cs) { return cs; }
inline Mutex* MaybeCheckNotHeld(Mutex* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) LOCK_RETURNED(cs) { return cs; }
// When locking a GlobalMutex or RecursiveMutex, just check it is not
// locked in the surrounding scope.
template <typename MutexType>
inline MutexType& MaybeCheckNotHeld(MutexType& m) LOCKS_EXCLUDED(m) LOCK_RETURNED(m) { return m; }
template <typename MutexType>
inline MutexType* MaybeCheckNotHeld(MutexType* m) LOCKS_EXCLUDED(m) LOCK_RETURNED(m) { return m; }
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
#define LOCK2(cs1, cs2) \
UniqueLock criticalblock1(MaybeCheckNotHeld(cs1), #cs1, __FILE__, __LINE__); \
UniqueLock criticalblock2(MaybeCheckNotHeld(cs2), #cs2, __FILE__, __LINE__)
#define TRY_LOCK(cs, name) UniqueLock name(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__, true)
#define WAIT_LOCK(cs, name) UniqueLock name(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
#define ENTER_CRITICAL_SECTION(cs) \
{ \
EnterCritical(#cs, __FILE__, __LINE__, &cs); \
(cs).lock(); \
}
#define LEAVE_CRITICAL_SECTION(cs) \
{ \
std::string lockname; \
CheckLastCritical((void*)(&cs), lockname, #cs, __FILE__, __LINE__); \
(cs).unlock(); \
LeaveCritical(); \
}
//! Run code while locking a mutex.
//!
//! Examples:
//!
//! WITH_LOCK(cs, shared_val = shared_val + 1);
//!
//! int val = WITH_LOCK(cs, return shared_val);
//!
//! Note:
//!
//! Since the return type deduction follows that of decltype(auto), while the
//! deduced type of:
//!
//! WITH_LOCK(cs, return {int i = 1; return i;});
//!
//! is int, the deduced type of:
//!
//! WITH_LOCK(cs, return {int j = 1; return (j);});
//!
//! is &int, a reference to a local variable
//!
//! The above is detectable at compile-time with the -Wreturn-local-addr flag in
//! gcc and the -Wreturn-stack-address flag in clang, both enabled by default.
#define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }())
/** An implementation of a semaphore.
*
* See https://en.wikipedia.org/wiki/Semaphore_(programming)
*/
class CSemaphore
{
private:
std::condition_variable condition;
std::mutex mutex;
int value;
public:
explicit CSemaphore(int init) noexcept : value(init) {}
// Disallow default construct, copy, move.
CSemaphore() = delete;
CSemaphore(const CSemaphore&) = delete;
CSemaphore(CSemaphore&&) = delete;
CSemaphore& operator=(const CSemaphore&) = delete;
CSemaphore& operator=(CSemaphore&&) = delete;
void wait() noexcept
{
std::unique_lock<std::mutex> lock(mutex);
condition.wait(lock, [&]() { return value >= 1; });
value--;
}
bool try_wait() noexcept
{
std::lock_guard<std::mutex> lock(mutex);
if (value < 1) {
return false;
}
value--;
return true;
}
void post() noexcept
{
{
std::lock_guard<std::mutex> lock(mutex);
value++;
}
condition.notify_one();
}
};
/** RAII-style semaphore lock */
class CSemaphoreGrant
{
private:
CSemaphore* sem;
bool fHaveGrant;
public:
void Acquire() noexcept
{
if (fHaveGrant) {
return;
}
sem->wait();
fHaveGrant = true;
}
void Release() noexcept
{
if (!fHaveGrant) {
return;
}
sem->post();
fHaveGrant = false;
}
bool TryAcquire() noexcept
{
if (!fHaveGrant && sem->try_wait()) {
fHaveGrant = true;
}
return fHaveGrant;
}
// Disallow copy.
CSemaphoreGrant(const CSemaphoreGrant&) = delete;
CSemaphoreGrant& operator=(const CSemaphoreGrant&) = delete;
// Allow move.
CSemaphoreGrant(CSemaphoreGrant&& other) noexcept
{
sem = other.sem;
fHaveGrant = other.fHaveGrant;
other.fHaveGrant = false;
other.sem = nullptr;
}
CSemaphoreGrant& operator=(CSemaphoreGrant&& other) noexcept
{
Release();
sem = other.sem;
fHaveGrant = other.fHaveGrant;
other.fHaveGrant = false;
other.sem = nullptr;
return *this;
}
CSemaphoreGrant() noexcept : sem(nullptr), fHaveGrant(false) {}
explicit CSemaphoreGrant(CSemaphore& sema, bool fTry = false) noexcept : sem(&sema), fHaveGrant(false)
{
if (fTry) {
TryAcquire();
} else {
Acquire();
}
}
~CSemaphoreGrant()
{
Release();
}
explicit operator bool() const noexcept
{
return fHaveGrant;
}
};
#endif // BITCOIN_SYNC_H

77
src/i2p/threadsafety.h Normal file
View File

@ -0,0 +1,77 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_THREADSAFETY_H
#define BITCOIN_THREADSAFETY_H
#include <mutex>
#ifdef __clang__
// TL;DR Add GUARDED_BY(mutex) to member variables. The others are
// rarely necessary. Ex: int nFoo GUARDED_BY(cs_foo);
//
// See https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
// for documentation. The clang compiler can do advanced static analysis
// of locking when given the -Wthread-safety option.
#define LOCKABLE __attribute__((lockable))
#define SCOPED_LOCKABLE __attribute__((scoped_lockable))
#define GUARDED_BY(x) __attribute__((guarded_by(x)))
#define PT_GUARDED_BY(x) __attribute__((pt_guarded_by(x)))
#define ACQUIRED_AFTER(...) __attribute__((acquired_after(__VA_ARGS__)))
#define ACQUIRED_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__)))
#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__((exclusive_lock_function(__VA_ARGS__)))
#define SHARED_LOCK_FUNCTION(...) __attribute__((shared_lock_function(__VA_ARGS__)))
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) __attribute__((exclusive_trylock_function(__VA_ARGS__)))
#define SHARED_TRYLOCK_FUNCTION(...) __attribute__((shared_trylock_function(__VA_ARGS__)))
#define UNLOCK_FUNCTION(...) __attribute__((unlock_function(__VA_ARGS__)))
#define LOCK_RETURNED(x) __attribute__((lock_returned(x)))
#define LOCKS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__)))
#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((exclusive_locks_required(__VA_ARGS__)))
#define SHARED_LOCKS_REQUIRED(...) __attribute__((shared_locks_required(__VA_ARGS__)))
#define NO_THREAD_SAFETY_ANALYSIS __attribute__((no_thread_safety_analysis))
#define ASSERT_EXCLUSIVE_LOCK(...) __attribute__((assert_exclusive_lock(__VA_ARGS__)))
#else
#define LOCKABLE
#define SCOPED_LOCKABLE
#define GUARDED_BY(x)
#define PT_GUARDED_BY(x)
#define ACQUIRED_AFTER(...)
#define ACQUIRED_BEFORE(...)
#define EXCLUSIVE_LOCK_FUNCTION(...)
#define SHARED_LOCK_FUNCTION(...)
#define EXCLUSIVE_TRYLOCK_FUNCTION(...)
#define SHARED_TRYLOCK_FUNCTION(...)
#define UNLOCK_FUNCTION(...)
#define LOCK_RETURNED(x)
#define LOCKS_EXCLUDED(...)
#define EXCLUSIVE_LOCKS_REQUIRED(...)
#define SHARED_LOCKS_REQUIRED(...)
#define NO_THREAD_SAFETY_ANALYSIS
#define ASSERT_EXCLUSIVE_LOCK(...)
#endif // __GNUC__
// StdMutex provides an annotated version of std::mutex for us,
// and should only be used when sync.h Mutex/LOCK/etc are not usable.
class LOCKABLE StdMutex : public std::mutex
{
public:
#ifdef __clang__
//! For negative capabilities in the Clang Thread Safety Analysis.
//! A negative requirement uses the EXCLUSIVE_LOCKS_REQUIRED attribute, in conjunction
//! with the ! operator, to indicate that a mutex should not be held.
const StdMutex& operator!() const { return *this; }
#endif // __clang__
};
// StdLockGuard provides an annotated version of std::lock_guard for us,
// and should only be used when sync.h Mutex/LOCK/etc are not usable.
class SCOPED_LOCKABLE StdLockGuard : public std::lock_guard<StdMutex>
{
public:
explicit StdLockGuard(StdMutex& cs) EXCLUSIVE_LOCK_FUNCTION(cs) : std::lock_guard<StdMutex>(cs) {}
~StdLockGuard() UNLOCK_FUNCTION() {}
};
#endif // BITCOIN_THREADSAFETY_H

1184
src/i2p/tinyformat.h Normal file

File diff suppressed because it is too large Load Diff

136
src/i2p/uint256.h Normal file
View File

@ -0,0 +1,136 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UINT256_H
#define BITCOIN_UINT256_H
#include "crypto/common.h"
#include "span.h"
#include <algorithm>
#include <array>
#include <cassert>
#include <cstring>
#include <stdint.h>
#include <string>
/** Template base class for fixed-sized opaque blobs. */
template<unsigned int BITS>
class base_blob
{
protected:
static constexpr int WIDTH = BITS / 8;
static_assert(BITS % 8 == 0, "base_blob currently only supports whole bytes.");
std::array<uint8_t, WIDTH> m_data;
static_assert(WIDTH == sizeof(m_data), "Sanity check");
public:
/* construct 0 value by default */
constexpr base_blob() : m_data() {}
/* constructor for constants between 1 and 255 */
constexpr explicit base_blob(uint8_t v) : m_data{v} {}
constexpr explicit base_blob(Span<const unsigned char> vch)
{
assert(vch.size() == WIDTH);
std::copy(vch.begin(), vch.end(), m_data.begin());
}
constexpr bool IsNull() const
{
return std::all_of(m_data.begin(), m_data.end(), [](uint8_t val) {
return val == 0;
});
}
constexpr void SetNull()
{
std::fill(m_data.begin(), m_data.end(), 0);
}
constexpr int Compare(const base_blob& other) const { return std::memcmp(m_data.data(), other.m_data.data(), WIDTH); }
friend constexpr bool operator==(const base_blob& a, const base_blob& b) { return a.Compare(b) == 0; }
friend constexpr bool operator!=(const base_blob& a, const base_blob& b) { return a.Compare(b) != 0; }
friend constexpr bool operator<(const base_blob& a, const base_blob& b) { return a.Compare(b) < 0; }
std::string GetHex() const;
void SetHex(const char* psz);
void SetHex(const std::string& str);
std::string ToString() const;
constexpr const unsigned char* data() const { return m_data.data(); }
constexpr unsigned char* data() { return m_data.data(); }
constexpr unsigned char* begin() { return m_data.data(); }
constexpr unsigned char* end() { return m_data.data() + WIDTH; }
constexpr const unsigned char* begin() const { return m_data.data(); }
constexpr const unsigned char* end() const { return m_data.data() + WIDTH; }
static constexpr unsigned int size() { return WIDTH; }
constexpr uint64_t GetUint64(int pos) const { return ReadLE64(m_data.data() + pos * 8); }
template<typename Stream>
void Serialize(Stream& s) const
{
s << Span(m_data);
}
template<typename Stream>
void Unserialize(Stream& s)
{
s.read(MakeWritableByteSpan(m_data));
}
};
/** 160-bit opaque blob.
* @note This type is called uint160 for historical reasons only. It is an opaque
* blob of 160 bits and has no integer operations.
*/
class uint160 : public base_blob<160> {
public:
constexpr uint160() = default;
constexpr explicit uint160(Span<const unsigned char> vch) : base_blob<160>(vch) {}
};
/** 256-bit opaque blob.
* @note This type is called uint256 for historical reasons only. It is an
* opaque blob of 256 bits and has no integer operations. Use arith_uint256 if
* those are required.
*/
class uint256 : public base_blob<256> {
public:
constexpr uint256() = default;
constexpr explicit uint256(uint8_t v) : base_blob<256>(v) {}
constexpr explicit uint256(Span<const unsigned char> vch) : base_blob<256>(vch) {}
static const uint256 ZERO;
static const uint256 ONE;
};
/* uint256 from const char *.
* This is a separate function because the constructor uint256(const char*) can result
* in dangerously catching uint256(0).
*/
inline uint256 uint256S(const char *str)
{
uint256 rv;
rv.SetHex(str);
return rv;
}
/* uint256 from std::string.
* This is a separate function because the constructor uint256(const std::string &str) can result
* in dangerously catching uint256(0) via std::string(const char*).
*/
inline uint256 uint256S(const std::string& str)
{
uint256 rv;
rv.SetHex(str);
return rv;
}
#endif // BITCOIN_UINT256_H

22
src/i2p/util/chaintype.h Normal file
View File

@ -0,0 +1,22 @@
// Copyright (c) 2023 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_CHAINTYPE_H
#define BITCOIN_UTIL_CHAINTYPE_H
#include <optional>
#include <string>
enum class ChainType {
MAIN,
TESTNET,
SIGNET,
REGTEST,
};
std::string ChainTypeToString(ChainType chain);
std::optional<ChainType> ChainTypeFromString(std::string_view chain);
#endif // BITCOIN_UTIL_CHAINTYPE_H

126
src/i2p/util/fs.cpp Normal file
View File

@ -0,0 +1,126 @@
// Copyright (c) 2017-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "../util/fs.h"
#include "../util/syserror.h"
#ifndef WIN32
#include <cstring>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/utsname.h>
#include <unistd.h>
#else
#include <codecvt>
#include <limits>
#include <windows.h>
#endif
#include <cassert>
#include <cerrno>
#include <string>
namespace fsbridge {
FILE *fopen(const fs::path &p, const char *mode) {
#ifndef WIN32
return ::fopen(p.c_str(), mode);
#else
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
return ::_wfopen(p.wstring().c_str(), utf8_cvt.from_bytes(mode).c_str());
#endif
}
fs::path AbsPathJoin(const fs::path &base, const fs::path &path) {
assert(base.is_absolute());
return path.empty() ? base : fs::path(base / path);
}
#ifndef WIN32
static std::string GetErrorReason() { return SysErrorString(errno); }
FileLock::FileLock(const fs::path &file) {
fd = open(file.c_str(), O_RDWR);
if (fd == -1) {
reason = GetErrorReason();
}
}
FileLock::~FileLock() {
if (fd != -1) {
close(fd);
}
}
bool FileLock::TryLock() {
if (fd == -1) {
return false;
}
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
if (fcntl(fd, F_SETLK, &lock) == -1) {
reason = GetErrorReason();
return false;
}
return true;
}
#else
static std::string GetErrorReason() { return Win32ErrorString(GetLastError()); }
FileLock::FileLock(const fs::path &file) {
hFile = CreateFileW(file.wstring().c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE) {
reason = GetErrorReason();
}
}
FileLock::~FileLock() {
if (hFile != INVALID_HANDLE_VALUE) {
CloseHandle(hFile);
}
}
bool FileLock::TryLock() {
if (hFile == INVALID_HANDLE_VALUE) {
return false;
}
_OVERLAPPED overlapped = {};
if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0,
std::numeric_limits<DWORD>::max(),
std::numeric_limits<DWORD>::max(), &overlapped)) {
reason = GetErrorReason();
return false;
}
return true;
}
#endif
std::string get_filesystem_error_message(const fs::filesystem_error &e) {
#ifndef WIN32
return e.what();
#else
// Convert from Multi Byte to utf-16
std::string mb_string(e.what());
int size = MultiByteToWideChar(CP_ACP, 0, mb_string.data(), mb_string.size(),
nullptr, 0);
std::wstring utf16_string(size, L'\0');
MultiByteToWideChar(CP_ACP, 0, mb_string.data(), mb_string.size(),
&*utf16_string.begin(), size);
// Convert from utf-16 to utf-8
return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>()
.to_bytes(utf16_string);
#endif
}
} // namespace fsbridge

253
src/i2p/util/fs.h Normal file
View File

@ -0,0 +1,253 @@
// Copyright (c) 2017-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_FS_H
#define BITCOIN_UTIL_FS_H
#include "../tinyformat.h"
#include <cstdio>
#include <filesystem> // IWYU pragma: export
#include <functional>
#include <iomanip>
#include <ios>
#include <ostream>
#include <string>
#include <system_error>
#include <type_traits>
#include <utility>
/** Filesystem operations and types */
namespace fs {
using namespace std::filesystem;
/**
* Path class wrapper to block calls to the fs::path(std::string) implicit
* constructor and the fs::path::string() method, which have unsafe and
* unpredictable behavior on Windows (see implementation note in
* \ref PathToString for details)
*/
class path : public std::filesystem::path
{
public:
using std::filesystem::path::path;
// Allow path objects arguments for compatibility.
path(std::filesystem::path path) : std::filesystem::path::path(std::move(path)) {}
path& operator=(std::filesystem::path path) { std::filesystem::path::operator=(std::move(path)); return *this; }
path& operator/=(const std::filesystem::path& path) { std::filesystem::path::operator/=(path); return *this; }
// Allow literal string arguments, which are safe as long as the literals are ASCII.
path(const char* c) : std::filesystem::path(c) {}
path& operator=(const char* c) { std::filesystem::path::operator=(c); return *this; }
path& operator/=(const char* c) { std::filesystem::path::operator/=(c); return *this; }
path& append(const char* c) { std::filesystem::path::append(c); return *this; }
// Disallow std::string arguments to avoid locale-dependent decoding on windows.
path(std::string) = delete;
path& operator=(std::string) = delete;
path& operator/=(std::string) = delete;
path& append(std::string) = delete;
// Disallow std::string conversion method to avoid locale-dependent encoding on windows.
std::string string() const = delete;
/**
* Return a UTF-8 representation of the path as a std::string, for
* compatibility with code using std::string. For code using the newer
* std::u8string type, it is more efficient to call the inherited
* std::filesystem::path::u8string method instead.
*/
std::string utf8string() const
{
const std::u8string& utf8_str{std::filesystem::path::u8string()};
return std::string{utf8_str.begin(), utf8_str.end()};
}
// Required for path overloads in <fstream>.
// See https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=96e0367ead5d8dcac3bec2865582e76e2fbab190
path& make_preferred() { std::filesystem::path::make_preferred(); return *this; }
path filename() const { return std::filesystem::path::filename(); }
};
static inline path u8path(const std::string& utf8_str)
{
return std::filesystem::path(std::u8string{utf8_str.begin(), utf8_str.end()});
}
// Disallow implicit std::string conversion for absolute to avoid
// locale-dependent encoding on windows.
static inline path absolute(const path& p)
{
return std::filesystem::absolute(p);
}
// Disallow implicit std::string conversion for exists to avoid
// locale-dependent encoding on windows.
static inline bool exists(const path& p)
{
return std::filesystem::exists(p);
}
// Allow explicit quoted stream I/O.
static inline auto quoted(const std::string& s)
{
return std::quoted(s, '"', '&');
}
// Allow safe path append operations.
static inline path operator/(path p1, const path& p2)
{
p1 /= p2;
return p1;
}
static inline path operator/(path p1, const char* p2)
{
p1 /= p2;
return p1;
}
static inline path operator+(path p1, const char* p2)
{
p1 += p2;
return p1;
}
static inline path operator+(path p1, path::value_type p2)
{
p1 += p2;
return p1;
}
// Disallow unsafe path append operations.
template<typename T> static inline path operator/(path p1, T p2) = delete;
template<typename T> static inline path operator+(path p1, T p2) = delete;
// Disallow implicit std::string conversion for copy_file
// to avoid locale-dependent encoding on Windows.
static inline bool copy_file(const path& from, const path& to, copy_options options)
{
return std::filesystem::copy_file(from, to, options);
}
/**
* Convert path object to a byte string. On POSIX, paths natively are byte
* strings, so this is trivial. On Windows, paths natively are Unicode, so an
* encoding step is necessary. The inverse of \ref PathToString is \ref
* PathFromString. The strings returned and parsed by these functions can be
* used to call POSIX APIs, and for roundtrip conversion, logging, and
* debugging.
*
* Because \ref PathToString and \ref PathFromString functions don't specify an
* encoding, they are meant to be used internally, not externally. They are not
* appropriate to use in applications requiring UTF-8, where
* fs::path::u8string() / fs::path::utf8string() and fs::u8path() methods should be used instead. Other
* applications could require still different encodings. For example, JSON, XML,
* or URI applications might prefer to use higher-level escapes (\uXXXX or
* &XXXX; or %XX) instead of multibyte encoding. Rust, Python, Java applications
* may require encoding paths with their respective UTF-8 derivatives WTF-8,
* PEP-383, and CESU-8 (see https://en.wikipedia.org/wiki/UTF-8#Derivatives).
*/
static inline std::string PathToString(const path& path)
{
// Implementation note: On Windows, the std::filesystem::path(string)
// constructor and std::filesystem::path::string() method are not safe to
// use here, because these methods encode the path using C++'s narrow
// multibyte encoding, which on Windows corresponds to the current "code
// page", which is unpredictable and typically not able to represent all
// valid paths. So fs::path::utf8string() and
// fs::u8path() functions are used instead on Windows. On
// POSIX, u8string/utf8string/u8path functions are not safe to use because paths are
// not always valid UTF-8, so plain string methods which do not transform
// the path there are used.
#ifdef WIN32
return path.utf8string();
#else
static_assert(std::is_same<path::string_type, std::string>::value, "PathToString not implemented on this platform");
return path.std::filesystem::path::string();
#endif
}
/**
* Convert byte string to path object. Inverse of \ref PathToString.
*/
static inline path PathFromString(const std::string& string)
{
#ifdef WIN32
return u8path(string);
#else
return std::filesystem::path(string);
#endif
}
/**
* Create directory (and if necessary its parents), unless the leaf directory
* already exists or is a symlink to an existing directory.
* This is a temporary workaround for an issue in libstdc++ that has been fixed
* upstream [PR101510].
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101510
*/
static inline bool create_directories(const std::filesystem::path& p)
{
if (std::filesystem::is_symlink(p) && std::filesystem::is_directory(p)) {
return false;
}
return std::filesystem::create_directories(p);
}
/**
* This variant is not used. Delete it to prevent it from accidentally working
* around the workaround. If it is needed, add a workaround in the same pattern
* as above.
*/
bool create_directories(const std::filesystem::path& p, std::error_code& ec) = delete;
} // namespace fs
/** Bridge operations to C stdio */
namespace fsbridge {
using FopenFn = std::function<FILE*(const fs::path&, const char*)>;
FILE *fopen(const fs::path& p, const char *mode);
/**
* Helper function for joining two paths
*
* @param[in] base Base path
* @param[in] path Path to combine with base
* @returns path unchanged if it is an absolute path, otherwise returns base joined with path. Returns base unchanged if path is empty.
* @pre Base path must be absolute
* @post Returned path will always be absolute
*/
fs::path AbsPathJoin(const fs::path& base, const fs::path& path);
class FileLock
{
public:
FileLock() = delete;
FileLock(const FileLock&) = delete;
FileLock(FileLock&&) = delete;
explicit FileLock(const fs::path& file);
~FileLock();
bool TryLock();
std::string GetReason() { return reason; }
private:
std::string reason;
#ifndef WIN32
int fd = -1;
#else
void* hFile = (void*)-1; // INVALID_HANDLE_VALUE
#endif
};
std::string get_filesystem_error_message(const fs::filesystem_error& e);
};
// Disallow path operator<< formatting in tinyformat to avoid locale-dependent
// encoding on windows.
namespace tinyformat {
template<> inline void formatValue(std::ostream&, const char*, const char*, int, const std::filesystem::path&) = delete;
template<> inline void formatValue(std::ostream&, const char*, const char*, int, const fs::path&) = delete;
} // namespace tinyformat
#endif // BITCOIN_UTIL_FS_H

48
src/i2p/util/hash_type.h Normal file
View File

@ -0,0 +1,48 @@
// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_HASH_TYPE_H
#define BITCOIN_UTIL_HASH_TYPE_H
template <typename HashType> class BaseHash {
protected:
HashType m_hash;
public:
BaseHash() : m_hash() {}
explicit BaseHash(const HashType &in) : m_hash(in) {}
unsigned char *begin() { return m_hash.begin(); }
const unsigned char *begin() const { return m_hash.begin(); }
unsigned char *end() { return m_hash.end(); }
const unsigned char *end() const { return m_hash.end(); }
operator std::vector<unsigned char>() const {
return std::vector<unsigned char>{m_hash.begin(), m_hash.end()};
}
std::string ToString() const { return m_hash.ToString(); }
bool operator==(const BaseHash<HashType> &other) const noexcept {
return m_hash == other.m_hash;
}
bool operator!=(const BaseHash<HashType> &other) const noexcept {
return !(m_hash == other.m_hash);
}
bool operator<(const BaseHash<HashType> &other) const noexcept {
return m_hash < other.m_hash;
}
size_t size() const { return m_hash.size(); }
unsigned char *data() { return m_hash.data(); }
const unsigned char *data() const { return m_hash.data(); }
};
#endif // BITCOIN_UTIL_HASH_TYPE_H

20
src/i2p/util/macros.h Normal file
View File

@ -0,0 +1,20 @@
// Copyright (c) 2019-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_MACROS_H
#define BITCOIN_UTIL_MACROS_H
#define PASTE(x, y) x ## y
#define PASTE2(x, y) PASTE(x, y)
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
/**
* Converts the parameter X to a string after macro replacement on X has been performed.
* Don't merge these into one macro!
*/
#define STRINGIZE(X) DO_STRINGIZE(X)
#define DO_STRINGIZE(X) #X
#endif // BITCOIN_UTIL_MACROS_H

View File

@ -0,0 +1,28 @@
// Copyright (c) 2015-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_READWRITEFILE_H
#define BITCOIN_UTIL_READWRITEFILE_H
#include "fs.h"
#include <limits>
#include <string>
#include <utility>
/** Read full contents of a file and return them in a std::string.
* Returns a pair <status, string>.
* If an error occurred, status will be false, otherwise status will be true and the data will be returned in string.
*
* @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data
* (with len > maxsize) will be returned.
*/
std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits<size_t>::max());
/** Write contents of std::string to a file.
* @return true on success.
*/
bool WriteBinaryFile(const fs::path &filename, const std::string &data);
#endif // BITCOIN_UTIL_READWRITEFILE_H

429
src/i2p/util/sock.cpp Normal file
View File

@ -0,0 +1,429 @@
// Copyright (c) 2020-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "i2p/util/sock.h"
// #include "../common/system.h"
// #include "easylogging++.h"
// #include "i2p/compat/compat.h"
// #include "../logging.h"
#include "i2p/tinyformat.h"
#include "i2p/util/syserror.h"
// #include "i2p/util/threadinterrupt.h"
// #include "i2p/util/time.h"
#include "misc_log_ex.h"
#include <memory>
#include <stdexcept>
#include <string>
#ifdef USE_POLL
#include <poll.h>
#endif
static inline bool IOErrorIsPermanent(int err) {
return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK &&
err != WSAEINPROGRESS;
}
Sock::Sock(SOCKET s) : m_socket(s) {}
Sock::Sock(Sock &&other) {
m_socket = other.m_socket;
other.m_socket = INVALID_SOCKET;
}
Sock::~Sock() { Close(); }
Sock &Sock::operator=(Sock &&other) {
Close();
m_socket = other.m_socket;
other.m_socket = INVALID_SOCKET;
return *this;
}
ssize_t Sock::Send(const void *data, size_t len, int flags) const {
return send(m_socket, static_cast<const char *>(data), len, flags);
}
ssize_t Sock::Recv(void *buf, size_t len, int flags) const {
return recv(m_socket, static_cast<char *>(buf), len, flags);
}
int Sock::Connect(const sockaddr *addr, socklen_t addr_len) const {
return connect(m_socket, addr, addr_len);
}
int Sock::Bind(const sockaddr *addr, socklen_t addr_len) const {
return bind(m_socket, addr, addr_len);
}
int Sock::Listen(int backlog) const { return listen(m_socket, backlog); }
std::unique_ptr<Sock> Sock::Accept(sockaddr *addr, socklen_t *addr_len) const {
#ifdef WIN32
static constexpr auto ERR = INVALID_SOCKET;
#else
static constexpr auto ERR = SOCKET_ERROR;
#endif
std::unique_ptr<Sock> sock;
const auto socket = accept(m_socket, addr, addr_len);
if (socket != ERR) {
try {
sock = std::make_unique<Sock>(socket);
} catch (const std::exception &) {
#ifdef WIN32
closesocket(socket);
#else
close(socket);
#endif
}
}
return sock;
}
int Sock::GetSockOpt(int level, int opt_name, void *opt_val,
socklen_t *opt_len) const {
return getsockopt(m_socket, level, opt_name, static_cast<char *>(opt_val),
opt_len);
}
int Sock::SetSockOpt(int level, int opt_name, const void *opt_val,
socklen_t opt_len) const {
return setsockopt(m_socket, level, opt_name,
static_cast<const char *>(opt_val), opt_len);
}
int Sock::GetSockName(sockaddr *name, socklen_t *name_len) const {
return getsockname(m_socket, name, name_len);
}
bool Sock::SetNonBlocking() const {
#ifdef WIN32
u_long on{1};
if (ioctlsocket(m_socket, FIONBIO, &on) == SOCKET_ERROR) {
return false;
}
#else
const int flags{fcntl(m_socket, F_GETFL, 0)};
if (flags == SOCKET_ERROR) {
return false;
}
if (fcntl(m_socket, F_SETFL, flags | O_NONBLOCK) == SOCKET_ERROR) {
return false;
}
#endif
return true;
}
bool Sock::IsSelectable() const {
#if defined(USE_POLL) || defined(WIN32)
return true;
#else
return m_socket < FD_SETSIZE;
#endif
}
bool Sock::Wait(std::chrono::milliseconds timeout, Event requested,
Event *occurred) const {
// We need a `shared_ptr` owning `this` for `WaitMany()`, but don't want
// `this` to be destroyed when the `shared_ptr` goes out of scope at the
// end of this function. Create it with a custom noop deleter.
std::shared_ptr<const Sock> shared{this, [](const Sock *) {}};
EventsPerSock events_per_sock{std::make_pair(shared, Events{requested})};
if (!WaitMany(timeout, events_per_sock)) {
return false;
}
if (occurred != nullptr) {
*occurred = events_per_sock.begin()->second.occurred;
}
return true;
}
bool Sock::WaitMany(std::chrono::milliseconds timeout,
EventsPerSock &events_per_sock) const {
#ifdef USE_POLL
std::vector<pollfd> pfds;
for (const auto &[sock, events] : events_per_sock) {
pfds.emplace_back();
auto &pfd = pfds.back();
pfd.fd = sock->m_socket;
if (events.requested & RECV) {
pfd.events |= POLLIN;
}
if (events.requested & SEND) {
pfd.events |= POLLOUT;
}
}
if (poll(pfds.data(), pfds.size(), count_milliseconds(timeout)) ==
SOCKET_ERROR) {
return false;
}
assert(pfds.size() == events_per_sock.size());
size_t i{0};
for (auto &[sock, events] : events_per_sock) {
assert(sock->m_socket == static_cast<SOCKET>(pfds[i].fd));
events.occurred = 0;
if (pfds[i].revents & POLLIN) {
events.occurred |= RECV;
}
if (pfds[i].revents & POLLOUT) {
events.occurred |= SEND;
}
if (pfds[i].revents & (POLLERR | POLLHUP)) {
events.occurred |= ERR;
}
++i;
}
return true;
#else
fd_set recv;
fd_set send;
fd_set err;
FD_ZERO(&recv);
FD_ZERO(&send);
FD_ZERO(&err);
SOCKET socket_max{0};
for (const auto &[sock, events] : events_per_sock) {
if (!sock->IsSelectable()) {
return false;
}
const auto &s = sock->m_socket;
if (events.requested & RECV) {
FD_SET(s, &recv);
}
if (events.requested & SEND) {
FD_SET(s, &send);
}
FD_SET(s, &err);
socket_max = std::max(socket_max, s);
}
timeval tv = MillisToTimeval(timeout);
if (select(socket_max + 1, &recv, &send, &err, &tv) == SOCKET_ERROR) {
return false;
}
for (auto &[sock, events] : events_per_sock) {
const auto &s = sock->m_socket;
events.occurred = 0;
if (FD_ISSET(s, &recv)) {
events.occurred |= RECV;
}
if (FD_ISSET(s, &send)) {
events.occurred |= SEND;
}
if (FD_ISSET(s, &err)) {
events.occurred |= ERR;
}
}
return true;
#endif /* USE_POLL */
}
void Sock::SendComplete(Span<const unsigned char> data,
std::chrono::milliseconds timeout,
CThreadInterrupt &interrupt) const {
const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
size_t sent{0};
for (;;) {
const ssize_t ret{
Send(data.data() + sent, data.size() - sent, MSG_NOSIGNAL)};
if (ret > 0) {
sent += static_cast<size_t>(ret);
if (sent == data.size()) {
break;
}
} else {
const int err{WSAGetLastError()};
if (IOErrorIsPermanent(err)) {
throw std::runtime_error(
tfm::format("send(): %s", NetworkErrorString(err)));
}
}
const auto now = GetTime<std::chrono::milliseconds>();
if (now >= deadline) {
throw std::runtime_error(
tfm::format("Send timeout (sent only %u of %u bytes before that)",
sent, data.size()));
}
if (interrupt) {
throw std::runtime_error(
tfm::format("Send interrupted (sent only %u of %u bytes before that)",
sent, data.size()));
}
// Wait for a short while (or the socket to become ready for sending) before
// retrying if nothing was sent.
const auto wait_time =
std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
(void)Wait(wait_time, SEND);
}
}
void Sock::SendComplete(Span<const char> data,
std::chrono::milliseconds timeout,
CThreadInterrupt &interrupt) const {
SendComplete(MakeUCharSpan(data), timeout, interrupt);
}
std::string Sock::RecvUntilTerminator(uint8_t terminator,
std::chrono::milliseconds timeout,
CThreadInterrupt &interrupt,
size_t max_data) const {
const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
std::string data;
bool terminator_found{false};
// We must not consume any bytes past the terminator from the socket.
// One option is to read one byte at a time and check if we have read a
// terminator. However that is very slow. Instead, we peek at what is in the
// socket and only read as many bytes as possible without crossing the
// terminator. Reading 64 MiB of random data with 262526 terminator chars
// takes 37 seconds to read one byte at a time VS 0.71 seconds with the "peek"
// solution below. Reading one byte at a time is about 50 times slower.
for (;;) {
if (data.size() >= max_data) {
throw std::runtime_error(tfm::format(
"Received too many bytes without a terminator (%u)", data.size()));
}
char buf[512];
const ssize_t peek_ret{
Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)};
switch (peek_ret) {
case -1: {
const int err{WSAGetLastError()};
if (IOErrorIsPermanent(err)) {
throw std::runtime_error(
tfm::format("recv(): %s", NetworkErrorString(err)));
}
break;
}
case 0:
throw std::runtime_error("Connection unexpectedly closed by peer");
default:
auto end = buf + peek_ret;
auto terminator_pos = std::find(buf, end, terminator);
terminator_found = terminator_pos != end;
const size_t try_len{terminator_found ? terminator_pos - buf + 1
: static_cast<size_t>(peek_ret)};
const ssize_t read_ret{Recv(buf, try_len, 0)};
if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
throw std::runtime_error(tfm::format(
"recv() returned %u bytes on attempt to read %u bytes but previous "
"peek claimed %u bytes are available",
read_ret, try_len, peek_ret));
}
// Don't include the terminator in the output.
const size_t append_len{terminator_found ? try_len - 1 : try_len};
data.append(buf, buf + append_len);
if (terminator_found) {
return data;
}
}
const auto now = GetTime<std::chrono::milliseconds>();
if (now >= deadline) {
throw std::runtime_error(tfm::format(
"Receive timeout (received %u bytes without terminator before that)",
data.size()));
}
if (interrupt) {
throw std::runtime_error(tfm::format(
"Receive interrupted (received %u bytes without terminator "
"before that)",
data.size()));
}
// Wait for a short while (or the socket to become ready for reading) before
// retrying.
const auto wait_time =
std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
(void)Wait(wait_time, RECV);
}
}
bool Sock::IsConnected(std::string &errmsg) const {
if (m_socket == INVALID_SOCKET) {
errmsg = "not connected";
return false;
}
char c;
switch (Recv(&c, sizeof(c), MSG_PEEK)) {
case -1: {
const int err = WSAGetLastError();
if (IOErrorIsPermanent(err)) {
errmsg = NetworkErrorString(err);
return false;
}
return true;
}
case 0:
errmsg = "closed";
return false;
default:
return true;
}
}
void Sock::Close() {
if (m_socket == INVALID_SOCKET) {
return;
}
#ifdef WIN32
int ret = closesocket(m_socket);
#else
int ret = close(m_socket);
#endif
if (ret) {
// LogPrintf("Error closing socket %d: %s\n", m_socket,
// NetworkErrorString(WSAGetLastError()));
MLOG_GREEN(el::Level::Error, "Error closing socket %d: %s\n"
<< m_socket
<< NetworkErrorString(WSAGetLastError()));
}
m_socket = INVALID_SOCKET;
}
bool Sock::operator==(SOCKET s) const { return m_socket == s; };
std::string NetworkErrorString(int err) {
#if defined(WIN32)
return Win32ErrorString(err);
#else
// On BSD sockets implementations, NetworkErrorString is the same as
// SysErrorString.
return SysErrorString(err);
#endif
}

311
src/i2p/util/sock.h Normal file
View File

@ -0,0 +1,311 @@
// Copyright (c) 2020-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_SOCK_H
#define BITCOIN_UTIL_SOCK_H
// #include "../compat/compat.h"
#include "../span.h"
#include "threadinterrupt.h"
#include "time.h"
#include <chrono>
#include <memory>
#include <string>
#include <unordered_map>
/**
* Maximum time to wait for I/O readiness.
* It will take up until this time to break off in case of an interruption.
*/
static constexpr auto MAX_WAIT_FOR_IO = 1s;
/**
* RAII helper class that manages a socket and closes it automatically when it
* goes out of scope.
*/
class Sock {
public:
Sock() = delete;
/**
* Take ownership of an existent socket.
*/
explicit Sock(SOCKET s);
/**
* Copy constructor, disabled because closing the same socket twice is
* undesirable.
*/
Sock(const Sock &) = delete;
/**
* Move constructor, grab the socket from another object and close ours (if
* set).
*/
Sock(Sock &&other);
/**
* Destructor, close the socket or do nothing if empty.
*/
virtual ~Sock();
/**
* Copy assignment operator, disabled because closing the same socket twice is
* undesirable.
*/
Sock &operator=(const Sock &) = delete;
/**
* Move assignment operator, grab the socket from another object and close
* ours (if set).
*/
virtual Sock &operator=(Sock &&other);
/**
* send(2) wrapper. Equivalent to `send(m_socket, data, len, flags);`. Code
* that uses this wrapper can be unit tested if this method is overridden by a
* mock Sock implementation.
*/
[[nodiscard]] virtual ssize_t Send(const void *data, size_t len,
int flags) const;
/**
* recv(2) wrapper. Equivalent to `recv(m_socket, buf, len, flags);`. Code
* that uses this wrapper can be unit tested if this method is overridden by a
* mock Sock implementation.
*/
[[nodiscard]] virtual ssize_t Recv(void *buf, size_t len, int flags) const;
/**
* connect(2) wrapper. Equivalent to `connect(m_socket, addr, addrlen)`. Code
* that uses this wrapper can be unit tested if this method is overridden by a
* mock Sock implementation.
*/
[[nodiscard]] virtual int Connect(const sockaddr *addr,
socklen_t addr_len) const;
/**
* bind(2) wrapper. Equivalent to `bind(m_socket, addr, addr_len)`. Code that
* uses this wrapper can be unit tested if this method is overridden by a mock
* Sock implementation.
*/
[[nodiscard]] virtual int Bind(const sockaddr *addr,
socklen_t addr_len) const;
/**
* listen(2) wrapper. Equivalent to `listen(m_socket, backlog)`. Code that
* uses this wrapper can be unit tested if this method is overridden by a mock
* Sock implementation.
*/
[[nodiscard]] virtual int Listen(int backlog) const;
/**
* accept(2) wrapper. Equivalent to `std::make_unique<Sock>(accept(m_socket,
* addr, addr_len))`. Code that uses this wrapper can be unit tested if this
* method is overridden by a mock Sock implementation. The returned unique_ptr
* is empty if `accept()` failed in which case errno will be set.
*/
[[nodiscard]] virtual std::unique_ptr<Sock> Accept(sockaddr *addr,
socklen_t *addr_len) const;
/**
* getsockopt(2) wrapper. Equivalent to
* `getsockopt(m_socket, level, opt_name, opt_val, opt_len)`. Code that uses
* this wrapper can be unit tested if this method is overridden by a mock Sock
* implementation.
*/
[[nodiscard]] virtual int GetSockOpt(int level, int opt_name, void *opt_val,
socklen_t *opt_len) const;
/**
* setsockopt(2) wrapper. Equivalent to
* `setsockopt(m_socket, level, opt_name, opt_val, opt_len)`. Code that uses
* this wrapper can be unit tested if this method is overridden by a mock Sock
* implementation.
*/
[[nodiscard]] virtual int SetSockOpt(int level, int opt_name,
const void *opt_val,
socklen_t opt_len) const;
/**
* getsockname(2) wrapper. Equivalent to
* `getsockname(m_socket, name, name_len)`. Code that uses this
* wrapper can be unit tested if this method is overridden by a mock Sock
* implementation.
*/
[[nodiscard]] virtual int GetSockName(sockaddr *name,
socklen_t *name_len) const;
/**
* Set the non-blocking option on the socket.
* @return true if set successfully
*/
[[nodiscard]] virtual bool SetNonBlocking() const;
/**
* Check if the underlying socket can be used for `select(2)` (or the `Wait()`
* method).
* @return true if selectable
*/
[[nodiscard]] virtual bool IsSelectable() const;
using Event = uint8_t;
/**
* If passed to `Wait()`, then it will wait for readiness to read from the
* socket.
*/
static constexpr Event RECV = 0b001;
/**
* If passed to `Wait()`, then it will wait for readiness to send to the
* socket.
*/
static constexpr Event SEND = 0b010;
/**
* Ignored if passed to `Wait()`, but could be set in the occurred events if
* an exceptional condition has occurred on the socket or if it has been
* disconnected.
*/
static constexpr Event ERR = 0b100;
/**
* Wait for readiness for input (recv) or output (send).
* @param[in] timeout Wait this much for at least one of the requested events
* to occur.
* @param[in] requested Wait for those events, bitwise-or of `RECV` and
* `SEND`.
* @param[out] occurred If not nullptr and the function returns `true`, then
* this indicates which of the requested events occurred (`ERR` will be added,
* even if not requested, if an exceptional event occurs on the socket). A
* timeout is indicated by return value of `true` and `occurred` being set to
* 0.
* @return true on success (or timeout, if `occurred` of 0 is returned), false
* otherwise
*/
[[nodiscard]] virtual bool Wait(std::chrono::milliseconds timeout,
Event requested,
Event *occurred = nullptr) const;
/**
* Auxiliary requested/occurred events to wait for in `WaitMany()`.
*/
struct Events {
explicit Events(Event req) : requested{req} {}
Event requested;
Event occurred{0};
};
struct HashSharedPtrSock {
size_t operator()(const std::shared_ptr<const Sock> &s) const {
return s ? s->m_socket : std::numeric_limits<SOCKET>::max();
}
};
struct EqualSharedPtrSock {
bool operator()(const std::shared_ptr<const Sock> &lhs,
const std::shared_ptr<const Sock> &rhs) const {
if (lhs && rhs) {
return lhs->m_socket == rhs->m_socket;
}
if (!lhs && !rhs) {
return true;
}
return false;
}
};
/**
* On which socket to wait for what events in `WaitMany()`.
* The `shared_ptr` is copied into the map to ensure that the `Sock` object
* is not destroyed (its destructor would close the underlying socket).
* If this happens shortly before or after we call `poll(2)` and a new
* socket gets created under the same file descriptor number then the report
* from `WaitMany()` will be bogus.
*/
using EventsPerSock =
std::unordered_map<std::shared_ptr<const Sock>, Events, HashSharedPtrSock,
EqualSharedPtrSock>;
/**
* Same as `Wait()`, but wait on many sockets within the same timeout.
* @param[in] timeout Wait this long for at least one of the requested events
* to occur.
* @param[in,out] events_per_sock Wait for the requested events on these
* sockets and set `occurred` for the events that actually occurred.
* @return true on success (or timeout, if all `what[].occurred` are returned
* as 0), false otherwise
*/
[[nodiscard]] virtual bool WaitMany(std::chrono::milliseconds timeout,
EventsPerSock &events_per_sock) const;
/* Higher level, convenience, methods. These may throw. */
/**
* Send the given data, retrying on transient errors.
* @param[in] data Data to send.
* @param[in] timeout Timeout for the entire operation.
* @param[in] interrupt If this is signaled then the operation is canceled.
* @throws std::runtime_error if the operation cannot be completed. In this
* case only some of the data will be written to the socket.
*/
virtual void SendComplete(Span<const unsigned char> data,
std::chrono::milliseconds timeout,
CThreadInterrupt &interrupt) const;
/**
* Convenience method, equivalent to `SendComplete(MakeUCharSpan(data),
* timeout, interrupt)`.
*/
virtual void SendComplete(Span<const char> data,
std::chrono::milliseconds timeout,
CThreadInterrupt &interrupt) const;
/**
* Read from socket until a terminator character is encountered. Will never
* consume bytes past the terminator from the socket.
* @param[in] terminator Character up to which to read from the socket.
* @param[in] timeout Timeout for the entire operation.
* @param[in] interrupt If this is signaled then the operation is canceled.
* @param[in] max_data The maximum amount of data (in bytes) to receive. If
* this many bytes are received and there is still no terminator, then this
* method will throw an exception.
* @return The data that has been read, without the terminating character.
* @throws std::runtime_error if the operation cannot be completed. In this
* case some bytes may have been consumed from the socket.
*/
[[nodiscard]] virtual std::string
RecvUntilTerminator(uint8_t terminator, std::chrono::milliseconds timeout,
CThreadInterrupt &interrupt, size_t max_data) const;
/**
* Check if still connected.
* @param[out] errmsg The error string, if the socket has been disconnected.
* @return true if connected
*/
[[nodiscard]] virtual bool IsConnected(std::string &errmsg) const;
/**
* Check if the internal socket is equal to `s`. Use only in tests.
*/
bool operator==(SOCKET s) const;
protected:
/**
* Contained socket. `INVALID_SOCKET` designates the object is empty.
*/
SOCKET m_socket;
private:
/**
* Close `m_socket` if it is not `INVALID_SOCKET`.
*/
void Close();
};
/** Return readable error string for a network error code */
std::string NetworkErrorString(int err);
#endif // BITCOIN_UTIL_SOCK_H

View File

@ -0,0 +1,79 @@
// Copyright (c) 2018-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_SPANPARSING_H
#define BITCOIN_UTIL_SPANPARSING_H
#include "../span.h"
#include <string>
#include <string_view>
#include <vector>
namespace spanparsing {
/** Parse a constant.
*
* If sp's initial part matches str, sp is updated to skip that part, and true is returned.
* Otherwise sp is unmodified and false is returned.
*/
bool Const(const std::string& str, Span<const char>& sp);
/** Parse a function call.
*
* If sp's initial part matches str + "(", and sp ends with ")", sp is updated to be the
* section between the braces, and true is returned. Otherwise sp is unmodified and false
* is returned.
*/
bool Func(const std::string& str, Span<const char>& sp);
/** Extract the expression that sp begins with.
*
* This function will return the initial part of sp, up to (but not including) the first
* comma or closing brace, skipping ones that are surrounded by braces. So for example,
* for "foo(bar(1),2),3" the initial part "foo(bar(1),2)" will be returned. sp will be
* updated to skip the initial part that is returned.
*/
Span<const char> Expr(Span<const char>& sp);
/** Split a string on any char found in separators, returning a vector.
*
* If sep does not occur in sp, a singleton with the entirety of sp is returned.
*
* Note that this function does not care about braces, so splitting
* "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}.
*/
template <typename T = Span<const char>>
std::vector<T> Split(const Span<const char>& sp, std::string_view separators)
{
std::vector<T> ret;
auto it = sp.begin();
auto start = it;
while (it != sp.end()) {
if (separators.find(*it) != std::string::npos) {
ret.emplace_back(start, it);
start = it + 1;
}
++it;
}
ret.emplace_back(start, it);
return ret;
}
/** Split a string on every instance of sep, returning a vector.
*
* If sep does not occur in sp, a singleton with the entirety of sp is returned.
*
* Note that this function does not care about braces, so splitting
* "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}.
*/
template <typename T = Span<const char>>
std::vector<T> Split(const Span<const char>& sp, char sep)
{
return Split<T>(sp, std::string_view{&sep, 1});
}
} // namespace spanparsing
#endif // BITCOIN_UTIL_SPANPARSING_H

View File

@ -0,0 +1,553 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "strencodings.h"
#include "i2p/span.h"
// #include <array>
// #include <cassert>
// #include <cstring>
// #include <limits>
#include <optional>
// #include <ostream>
#include <string>
#include <vector>
// static const std::string CHARS_ALPHA_NUM =
// "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
//
// static const std::string SAFE_CHARS[] = {
// CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_DEFAULT
// CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_UA_COMMENT
// CHARS_ALPHA_NUM + ".-_", // SAFE_CHARS_FILENAME
// CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%", // SAFE_CHARS_URI
// };
// std::string SanitizeString(std::string_view str, int rule) {
// std::string result;
// for (char c : str) {
// if (SAFE_CHARS[rule].find(c) != std::string::npos) {
// result.push_back(c);
// }
// }
// return result;
// }
// const signed char p_util_hexdigit[256] = {
// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
// -1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// -1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
// };
// signed char HexDigit(char c) { return p_util_hexdigit[(unsigned char)c]; }
//
// bool IsHex(std::string_view str) {
// for (char c : str) {
// if (HexDigit(c) < 0)
// return false;
// }
// return (str.size() > 0) && (str.size() % 2 == 0);
// }
//
// bool IsHexNumber(std::string_view str) {
// if (str.substr(0, 2) == "0x")
// str.remove_prefix(2);
// for (char c : str) {
// if (HexDigit(c) < 0)
// return false;
// }
// // Return false for empty string or "0x".
// return str.size() > 0;
// }
// template <typename Byte>
// std::optional<std::vector<Byte>> TryParseHex(std::string_view str) {
// std::vector<Byte> vch;
// vch.reserve(str.size() / 2); // two hex characters form a single byte
//
// auto it = str.begin();
// while (it != str.end()) {
// if (IsSpace(*it)) {
// ++it;
// continue;
// }
// auto c1 = HexDigit(*(it++));
// if (it == str.end())
// return std::nullopt;
// auto c2 = HexDigit(*(it++));
// if (c1 < 0 || c2 < 0)
// return std::nullopt;
// vch.push_back(Byte(c1 << 4) | Byte(c2));
// }
// return vch;
// }
// template std::optional<std::vector<std::byte>> TryParseHex(std::string_view);
// template std::optional<std::vector<uint8_t>> TryParseHex(std::string_view);
//
// bool SplitHostPort(std::string_view in, uint16_t &portOut,
// std::string &hostOut) {
// bool valid = false;
// size_t colon = in.find_last_of(':');
// // if a : is found, and it either follows a [...], or no other : is in the
// // string, treat it as port separator
// bool fHaveColon = colon != in.npos;
// bool fBracketed =
// fHaveColon &&
// (in[0] == '[' &&
// in[colon - 1] == ']'); // if there is a colon, and in[0]=='[', colon
// is
// // not 0, so in[colon-1] is safe
// bool fMultiColon{fHaveColon && colon != 0 &&
// (in.find_last_of(':', colon - 1) != in.npos)};
// if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) {
// uint16_t n;
// if (ParseUInt16(in.substr(colon + 1), &n)) {
// in = in.substr(0, colon);
// portOut = n;
// valid = (portOut != 0);
// }
// } else {
// valid = true;
// }
// if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') {
// hostOut = in.substr(1, in.size() - 2);
// } else {
// hostOut = in;
// }
//
// return valid;
// }
std::string EncodeBase64(Span<const unsigned char> input) {
static const char *pbase64 =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string str;
str.reserve(((input.size() + 2) / 3) * 4);
ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, input.begin(),
input.end());
while (str.size() % 4)
str += '=';
return str;
}
std::optional<std::vector<unsigned char>> DecodeBase64(std::string_view str) {
static const int8_t decode64_table[256]{
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57,
58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1};
if (str.size() % 4 != 0)
return {};
/* One or two = characters at the end are permitted. */
if (str.size() >= 1 && str.back() == '=')
str.remove_suffix(1);
if (str.size() >= 1 && str.back() == '=')
str.remove_suffix(1);
std::vector<unsigned char> ret;
ret.reserve((str.size() * 3) / 4);
bool valid = ConvertBits<6, 8, false>(
[&](unsigned char c) { ret.push_back(c); }, str.begin(), str.end(),
[](char c) { return decode64_table[uint8_t(c)]; });
if (!valid)
return {};
return ret;
}
std::string EncodeBase32(Span<const unsigned char> input, bool pad) {
static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
std::string str;
str.reserve(((input.size() + 4) / 5) * 8);
ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(),
input.end());
if (pad) {
while (str.size() % 8) {
str += '=';
}
}
return str;
}
std::string EncodeBase32(std::string_view str, bool pad) {
return EncodeBase32(MakeUCharSpan(str), pad);
}
std::optional<std::vector<unsigned char>> DecodeBase32(std::string_view str) {
static const int8_t decode32_table[256]{
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29,
30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1};
if (str.size() % 8 != 0)
return {};
/* 1, 3, 4, or 6 padding '=' suffix characters are permitted. */
if (str.size() >= 1 && str.back() == '=')
str.remove_suffix(1);
if (str.size() >= 2 && str.substr(str.size() - 2) == "==")
str.remove_suffix(2);
if (str.size() >= 1 && str.back() == '=')
str.remove_suffix(1);
if (str.size() >= 2 && str.substr(str.size() - 2) == "==")
str.remove_suffix(2);
std::vector<unsigned char> ret;
ret.reserve((str.size() * 5) / 8);
bool valid = ConvertBits<5, 8, false>(
[&](unsigned char c) { ret.push_back(c); }, str.begin(), str.end(),
[](char c) { return decode32_table[uint8_t(c)]; });
if (!valid)
return {};
return ret;
}
// namespace {
// template <typename T> bool ParseIntegral(std::string_view str, T *out) {
// static_assert(std::is_integral<T>::value);
// // Replicate the exact behavior of strtol/strtoll/strtoul/strtoull when
// // handling leading +/- for backwards compatibility.
// if (str.length() >= 2 && str[0] == '+' && str[1] == '-') {
// return false;
// }
// const std::optional<T> opt_int =
// ToIntegral<T>((!str.empty() && str[0] == '+') ? str.substr(1) : str);
// if (!opt_int) {
// return false;
// }
// if (out != nullptr) {
// *out = *opt_int;
// }
// return true;
// }
// }; // namespace
// bool ParseInt32(std::string_view str, int32_t *out) {
// return ParseIntegral<int32_t>(str, out);
// }
//
// bool ParseInt64(std::string_view str, int64_t *out) {
// return ParseIntegral<int64_t>(str, out);
// }
//
// bool ParseUInt8(std::string_view str, uint8_t *out) {
// return ParseIntegral<uint8_t>(str, out);
// }
//
// bool ParseUInt16(std::string_view str, uint16_t *out) {
// return ParseIntegral<uint16_t>(str, out);
// }
//
// bool ParseUInt32(std::string_view str, uint32_t *out) {
// return ParseIntegral<uint32_t>(str, out);
// }
//
// bool ParseUInt64(std::string_view str, uint64_t *out) {
// return ParseIntegral<uint64_t>(str, out);
// }
// std::string FormatParagraph(std::string_view in, size_t width, size_t indent)
// {
// assert(width >= indent);
// std::stringstream out;
// size_t ptr = 0;
// size_t indented = 0;
// while (ptr < in.size()) {
// size_t lineend = in.find_first_of('\n', ptr);
// if (lineend == std::string::npos) {
// lineend = in.size();
// }
// const size_t linelen = lineend - ptr;
// const size_t rem_width = width - indented;
// if (linelen <= rem_width) {
// out << in.substr(ptr, linelen + 1);
// ptr = lineend + 1;
// indented = 0;
// } else {
// size_t finalspace = in.find_last_of(" \n", ptr + rem_width);
// if (finalspace == std::string::npos || finalspace < ptr) {
// // No place to break; just include the entire word and move on
// finalspace = in.find_first_of("\n ", ptr);
// if (finalspace == std::string::npos) {
// // End of the string, just add it and break
// out << in.substr(ptr);
// break;
// }
// }
// out << in.substr(ptr, finalspace - ptr) << "\n";
// if (in[finalspace] == '\n') {
// indented = 0;
// } else if (indent) {
// out << std::string(indent, ' ');
// indented = indent;
// }
// ptr = finalspace + 1;
// }
// }
// return out.str();
// }
/** Upper bound for mantissa.
* 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit
* integer. Larger integers cannot consist of arbitrary combinations of 0-9:
*
* 999999999999999999 1^18-1
* 9223372036854775807 (1<<63)-1 (max int64_t)
* 9999999999999999999 1^19-1 (would overflow)
*/
// static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL;
//
// /** Helper function for ParseFixedPoint */
// static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa,
// int &mantissa_tzeros) {
// if (ch == '0')
// ++mantissa_tzeros;
// else {
// for (int i = 0; i <= mantissa_tzeros; ++i) {
// if (mantissa > (UPPER_BOUND / 10LL))
// return false; /* overflow */
// mantissa *= 10;
// }
// mantissa += ch - '0';
// mantissa_tzeros = 0;
// }
// return true;
// }
// bool ParseFixedPoint(std::string_view val, int decimals, int64_t *amount_out)
// {
// int64_t mantissa = 0;
// int64_t exponent = 0;
// int mantissa_tzeros = 0;
// bool mantissa_sign = false;
// bool exponent_sign = false;
// int ptr = 0;
// int end = val.size();
// int point_ofs = 0;
//
// if (ptr < end && val[ptr] == '-') {
// mantissa_sign = true;
// ++ptr;
// }
// if (ptr < end) {
// if (val[ptr] == '0') {
// /* pass single 0 */
// ++ptr;
// } else if (val[ptr] >= '1' && val[ptr] <= '9') {
// while (ptr < end && IsDigit(val[ptr])) {
// if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
// return false; /* overflow */
// ++ptr;
// }
// } else
// return false; /* missing expected digit */
// } else
// return false; /* empty string or loose '-' */
// if (ptr < end && val[ptr] == '.') {
// ++ptr;
// if (ptr < end && IsDigit(val[ptr])) {
// while (ptr < end && IsDigit(val[ptr])) {
// if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
// return false; /* overflow */
// ++ptr;
// ++point_ofs;
// }
// } else
// return false; /* missing expected digit */
// }
// if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E')) {
// ++ptr;
// if (ptr < end && val[ptr] == '+')
// ++ptr;
// else if (ptr < end && val[ptr] == '-') {
// exponent_sign = true;
// ++ptr;
// }
// if (ptr < end && IsDigit(val[ptr])) {
// while (ptr < end && IsDigit(val[ptr])) {
// if (exponent > (UPPER_BOUND / 10LL))
// return false; /* overflow */
// exponent = exponent * 10 + val[ptr] - '0';
// ++ptr;
// }
// } else
// return false; /* missing expected digit */
// }
// if (ptr != end)
// return false; /* trailing garbage */
//
// /* finalize exponent */
// if (exponent_sign)
// exponent = -exponent;
// exponent = exponent - point_ofs + mantissa_tzeros;
//
// /* finalize mantissa */
// if (mantissa_sign)
// mantissa = -mantissa;
//
// /* convert to one 64-bit fixed-point value */
// exponent += decimals;
// if (exponent < 0)
// return false; /* cannot represent values smaller than 10^-decimals */
// if (exponent >= 18)
// return false; /* cannot represent values larger than or equal to
// 10^(18-decimals) */
//
// for (int i = 0; i < exponent; ++i) {
// if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL))
// return false; /* overflow */
// mantissa *= 10;
// }
// if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND)
// return false; /* overflow */
//
// if (amount_out)
// *amount_out = mantissa;
//
// return true;
// }
// std::string ToLower(std::string_view str) {
// std::string r;
// r.reserve(str.size());
// for (auto ch : str)
// r += ToLower(ch);
// return r;
// }
std::string ToUpper(std::string_view str) {
std::string r;
r.reserve(str.size());
for (auto ch : str)
r += ToUpper(ch);
return r;
}
std::string Capitalize(std::string str) {
if (str.empty())
return str;
str[0] = ToUpper(str.front());
return str;
}
// namespace {
//
// using ByteAsHex = std::array<char, 2>;
//
// constexpr std::array<ByteAsHex, 256> CreateByteToHexMap() {
// constexpr char hexmap[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
// '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
//
// std::array<ByteAsHex, 256> byte_to_hex{};
// for (size_t i = 0; i < byte_to_hex.size(); ++i) {
// byte_to_hex[i][0] = hexmap[i >> 4];
// byte_to_hex[i][1] = hexmap[i & 15];
// }
// return byte_to_hex;
// }
//
// } // namespace
// std::string HexStr(const Span<const uint8_t> s) {
// std::string rv(s.size() * 2, '\0');
// static constexpr auto byte_to_hex = CreateByteToHexMap();
// static_assert(sizeof(byte_to_hex) == 512);
//
// char *it = rv.data();
// for (uint8_t v : s) {
// std::memcpy(it, byte_to_hex[v].data(), 2);
// it += 2;
// }
//
// assert(it == rv.data() + rv.size());
// return rv;
// }
// std::optional<uint64_t> ParseByteUnits(std::string_view str,
// ByteUnit default_multiplier) {
// if (str.empty()) {
// return std::nullopt;
// }
// auto multiplier = default_multiplier;
// char unit = str.back();
// switch (unit) {
// case 'k':
// multiplier = ByteUnit::k;
// break;
// case 'K':
// multiplier = ByteUnit::K;
// break;
// case 'm':
// multiplier = ByteUnit::m;
// break;
// case 'M':
// multiplier = ByteUnit::M;
// break;
// case 'g':
// multiplier = ByteUnit::g;
// break;
// case 'G':
// multiplier = ByteUnit::G;
// break;
// case 't':
// multiplier = ByteUnit::t;
// break;
// case 'T':
// multiplier = ByteUnit::T;
// break;
// default:
// unit = 0;
// break;
// }
//
// uint64_t unit_amount = static_cast<uint64_t>(multiplier);
// auto parsed_num =
// ToIntegral<uint64_t>(unit ? str.substr(0, str.size() - 1) : str);
// if (!parsed_num || parsed_num > std::numeric_limits<uint64_t>::max() /
// unit_amount) { // check overflow
// return std::nullopt;
// }
// return *parsed_num * unit_amount;
// }

405
src/i2p/util/strencodings.h Normal file
View File

@ -0,0 +1,405 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
/**
* Utilities for converting data from/to strings.
*/
#ifndef BITCOIN_UTIL_STRENCODINGS_H
#define BITCOIN_UTIL_STRENCODINGS_H
#include "i2p/span.h"
// #include "string.h"
// #include <charconv>
#include <cstddef>
// #include <cstdint>
// #include <limits>
#include <optional>
#include <string> // IWYU pragma: export
#include <string_view> // IWYU pragma: export
// #include <system_error>
// #include <type_traits>
#include <vector>
/** Used by SanitizeString() */
// enum SafeChars {
// SAFE_CHARS_DEFAULT, //!< The full set of allowed chars
// SAFE_CHARS_UA_COMMENT, //!< BIP-0014 subset
// SAFE_CHARS_FILENAME, //!< Chars allowed in filenames
// SAFE_CHARS_URI, //!< Chars allowed in URIs (RFC 3986)
// };
/**
* Used by ParseByteUnits()
* Lowercase base 1000
* Uppercase base 1024
*/
// enum class ByteUnit : uint64_t {
// NOOP = 1ULL,
// k = 1000ULL,
// K = 1024ULL,
// m = 1'000'000ULL,
// M = 1ULL << 20,
// g = 1'000'000'000ULL,
// G = 1ULL << 30,
// t = 1'000'000'000'000ULL,
// T = 1ULL << 40,
// };
/**
* Remove unsafe chars. Safe chars chosen to allow simple messages/URLs/email
* addresses, but avoid anything even possibly remotely dangerous like & or >
* @param[in] str The string to sanitize
* @param[in] rule The set of safe chars to choose (default: least
* restrictive)
* @return A new string without unsafe chars
*/
// std::string SanitizeString(std::string_view str, int rule =
// SAFE_CHARS_DEFAULT);
// /** Parse the hex string into bytes (uint8_t or std::byte). Ignores
// whitespace.
// * Returns nullopt on invalid input. */
// template <typename Byte = std::byte>
// std::optional<std::vector<Byte>> TryParseHex(std::string_view str);
// /** Like TryParseHex, but returns an empty vector on invalid input. */
// template <typename Byte = uint8_t>
// std::vector<Byte> ParseHex(std::string_view hex_str) {
// return TryParseHex<Byte>(hex_str).value_or(std::vector<Byte>{});
// }
// signed char HexDigit(char c);
// /* Returns true if each character in str is a hex character, and has an even
// * number of hex digits.*/
// bool IsHex(std::string_view str);
/**
* Return true if the string is a hex number, optionally prefixed with "0x"
*/
// bool IsHexNumber(std::string_view str);
std::optional<std::vector<unsigned char>> DecodeBase64(std::string_view str);
std::string EncodeBase64(Span<const unsigned char> input);
inline std::string EncodeBase64(Span<const std::byte> input) {
return EncodeBase64(MakeUCharSpan(input));
}
inline std::string EncodeBase64(std::string_view str) {
return EncodeBase64(MakeUCharSpan(str));
}
std::optional<std::vector<unsigned char>> DecodeBase32(std::string_view str);
/**
* Base32 encode.
* If `pad` is true, then the output will be padded with '=' so that its length
* is a multiple of 8.
*/
std::string EncodeBase32(Span<const unsigned char> input, bool pad = true);
/**
* Base32 encode.
* If `pad` is true, then the output will be padded with '=' so that its length
* is a multiple of 8.
*/
std::string EncodeBase32(std::string_view str, bool pad = true);
/**
* Splits socket address string into host string and port value.
* Validates port value.
*
* @param[in] in The socket address string to split.
* @param[out] portOut Port-portion of the input, if found and parsable.
* @param[out] hostOut Host-portion of the input, if found.
* @return true if port-portion is absent or within its allowed
* range, otherwise false
*/
// bool SplitHostPort(std::string_view in, uint16_t &portOut,
// std::string &hostOut);
// LocaleIndependentAtoi is provided for backwards compatibility reasons.
//
// New code should use ToIntegral or the ParseInt* functions
// which provide parse error feedback.
//
// The goal of LocaleIndependentAtoi is to replicate the defined behaviour of
// std::atoi as it behaves under the "C" locale, and remove some undefined
// behavior. If the parsed value is bigger than the integer type's maximum
// value, or smaller than the integer type's minimum value, std::atoi has
// undefined behavior, while this function returns the maximum or minimum
// values, respectively.
// template <typename T> T LocaleIndependentAtoi(std::string_view str) {
// static_assert(std::is_integral<T>::value);
// T result;
// // Emulate atoi(...) handling of white space and leading +/-.
// std::string_view s = TrimStringView(str);
// if (!s.empty() && s[0] == '+') {
// if (s.length() >= 2 && s[1] == '-') {
// return 0;
// }
// s = s.substr(1);
// }
// auto [_, error_condition] =
// std::from_chars(s.data(), s.data() + s.size(), result);
// if (error_condition == std::errc::result_out_of_range) {
// if (s.length() >= 1 && s[0] == '-') {
// // Saturate underflow, per strtoll's behavior.
// return std::numeric_limits<T>::min();
// } else {
// // Saturate overflow, per strtoll's behavior.
// return std::numeric_limits<T>::max();
// }
// } else if (error_condition != std::errc{}) {
// return 0;
// }
// return result;
// }
/**
* Tests if the given character is a decimal digit.
* @param[in] c character to test
* @return true if the argument is a decimal digit; otherwise false.
*/
// constexpr bool IsDigit(char c) { return c >= '0' && c <= '9'; }
/**
* Tests if the given character is a whitespace character. The whitespace
* characters are: space, form-feed ('\f'), newline ('\n'), carriage return
* ('\r'), horizontal tab ('\t'), and vertical tab ('\v').
*
* This function is locale independent. Under the C locale this function gives
* the same result as std::isspace.
*
* @param[in] c character to test
* @return true if the argument is a whitespace character; otherwise
* false
*/
// constexpr inline bool IsSpace(char c) noexcept {
// return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' ||
// c == '\v';
// }
/**
* Convert string to integral type T. Leading whitespace, a leading +, or any
* trailing character fail the parsing. The required format expressed as regex
* is `-?[0-9]+`. The minus sign is only permitted for signed integer types.
*
* @returns std::nullopt if the entire string could not be parsed, or if the
* parsed value is not in the range representable by the type T.
*/
// template <typename T> std::optional<T> ToIntegral(std::string_view str) {
// static_assert(std::is_integral<T>::value);
// T result;
// const auto [first_nonmatching, error_condition] =
// std::from_chars(str.data(), str.data() + str.size(), result);
// if (first_nonmatching != str.data() + str.size() ||
// error_condition != std::errc{}) {
// return std::nullopt;
// }
// return result;
// }
/**
* Convert string to signed 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or
* underflow occurred.
*/
/* [[nodiscard]] bool ParseInt32(std::string_view str, int32_t *out); */
/**
* Convert string to signed 64-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or
* underflow occurred.
*/
/* [[nodiscard]] bool ParseInt64(std::string_view str, int64_t *out); */
/**
* Convert decimal string to unsigned 8-bit integer with strict parse error
* feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or
* underflow occurred.
*/
/* [[nodiscard]] bool ParseUInt8(std::string_view str, uint8_t *out); */
/**
* Convert decimal string to unsigned 16-bit integer with strict parse error
* feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if the entire string could not be parsed or if overflow or underflow
* occurred.
*/
/* [[nodiscard]] bool ParseUInt16(std::string_view str, uint16_t *out); */
/**
* Convert decimal string to unsigned 32-bit integer with strict parse error
* feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or
* underflow occurred.
*/
/* [[nodiscard]] bool ParseUInt32(std::string_view str, uint32_t *out); */
/**
* Convert decimal string to unsigned 64-bit integer with strict parse error
* feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or
* underflow occurred.
*/
/* [[nodiscard]] bool ParseUInt64(std::string_view str, uint64_t *out); */
/**
* Convert a span of bytes to a lower-case hexadecimal string.
*/
// std::string HexStr(const Span<const uint8_t> s);
// inline std::string HexStr(const Span<const char> s) {
// return HexStr(MakeUCharSpan(s));
// }
// inline std::string HexStr(const Span<const std::byte> s) {
// return HexStr(MakeUCharSpan(s));
// }
/**
* Format a paragraph of text to a fixed width, adding spaces for
* indentation to any added line.
*/
// std::string FormatParagraph(std::string_view in, size_t width = 79,
// size_t indent = 0);
/**
* Timing-attack-resistant comparison.
* Takes time proportional to length
* of first argument.
*/
// template <typename T> bool TimingResistantEqual(const T &a, const T &b) {
// if (b.size() == 0)
// return a.size() == 0;
// size_t accumulator = a.size() ^ b.size();
// for (size_t i = 0; i < a.size(); i++)
// accumulator |= size_t(a[i] ^ b[i % b.size()]);
// return accumulator == 0;
// }
/** Parse number as fixed point according to JSON number syntax.
* @returns true on success, false on error.
* @note The result must be in the range (-10^18,10^18), otherwise an overflow
* error will trigger.
*/
// [[nodiscard]] bool ParseFixedPoint(std::string_view, int decimals,
// int64_t *amount_out);
namespace {
/** Helper class for the default infn argument to ConvertBits (just returns
the
* input). */
struct IntIdentity {
[[maybe_unused]] int operator()(int x) const { return x; }
};
} // namespace
/** Convert from one power-of-2 number base to another. */
template <int frombits, int tobits, bool pad, typename O, typename It,
typename I = IntIdentity>
bool ConvertBits(O outfn, It it, It end, I infn = {}) {
size_t acc = 0;
size_t bits = 0;
constexpr size_t maxv = (1 << tobits) - 1;
constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1;
while (it != end) {
int v = infn(*it);
if (v < 0)
return false;
acc = ((acc << frombits) | v) & max_acc;
bits += frombits;
while (bits >= tobits) {
bits -= tobits;
outfn((acc >> bits) & maxv);
}
++it;
}
if (pad) {
if (bits)
outfn((acc << (tobits - bits)) & maxv);
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
return false;
}
return true;
}
/**
* Converts the given character to its lowercase equivalent.
* This function is locale independent. It only converts uppercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] c the character to convert to lowercase.
* @return the lowercase equivalent of c; or the argument
* if no conversion is possible.
*/
// constexpr char ToLower(char c) {
// return (c >= 'A' && c <= 'Z' ? (c - 'A') + 'a' : c);
// }
/**
* Returns the lowercase equivalent of the given string.
* This function is locale independent. It only converts uppercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] str the string to convert to lowercase.
* @returns lowercased equivalent of str
*/
// std::string ToLower(std::string_view str);
//
/**
* Converts the given character to its uppercase equivalent.
* This function is locale independent. It only converts lowercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] c the character to convert to uppercase.
* @return the uppercase equivalent of c; or the argument
* if no conversion is possible.
*/
constexpr char ToUpper(char c) {
return (c >= 'a' && c <= 'z' ? (c - 'a') + 'A' : c);
}
/**
* Returns the uppercase equivalent of the given string.
* This function is locale independent. It only converts lowercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] str the string to convert to uppercase.
* @returns UPPERCASED EQUIVALENT OF str
*/
// std::string ToUpper(std::string_view str);
//
/**
* Capitalizes the first character of the given string.
* This function is locale independent. It only converts lowercase
* characters in the standard 7-bit ASCII range.
* This is a feature, not a limitation.
*
* @param[in] str the string to capitalize.
* @returns string with the first letter capitalized.
*/
std::string Capitalize(std::string str);
/**
* Parse a string with suffix unit [k|K|m|M|g|G|t|T].
* Must be a whole integer, fractions not allowed (0.5t), no whitespace or +-
* Lowercase units are 1000 base. Uppercase units are 1024 base.
* Examples: 2m,27M,19g,41T
*
* @param[in] str the string to convert into bytes
* @param[in] default_multiplier if no unit is found in str use this unit
* @returns optional uint64_t bytes from str or nullopt
* if ToIntegral is false, str is empty,
* trailing whitespace or overflow
*/
// std::optional<uint64_t> ParseByteUnits(std::string_view str,
// ByteUnit default_multiplier);
#endif // BITCOIN_UTIL_STRENCODINGS_H

128
src/i2p/util/string.h Normal file
View File

@ -0,0 +1,128 @@
// Copyright (c) 2019-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_STRING_H
#define BITCOIN_UTIL_STRING_H
#include "spanparsing.h"
#include <array>
#include <cstdint>
#include <cstring>
#include <locale>
#include <sstream>
#include <string> // IWYU pragma: export
#include <string_view> // IWYU pragma: export
#include <vector>
void ReplaceAll(std::string& in_out, const std::string& search, const std::string& substitute);
[[nodiscard]] inline std::vector<std::string> SplitString(std::string_view str, char sep)
{
return spanparsing::Split<std::string>(str, sep);
}
[[nodiscard]] inline std::vector<std::string> SplitString(std::string_view str, std::string_view separators)
{
return spanparsing::Split<std::string>(str, separators);
}
[[nodiscard]] inline std::string_view TrimStringView(std::string_view str, std::string_view pattern = " \f\n\r\t\v")
{
std::string::size_type front = str.find_first_not_of(pattern);
if (front == std::string::npos) {
return {};
}
std::string::size_type end = str.find_last_not_of(pattern);
return str.substr(front, end - front + 1);
}
[[nodiscard]] inline std::string TrimString(std::string_view str, std::string_view pattern = " \f\n\r\t\v")
{
return std::string(TrimStringView(str, pattern));
}
[[nodiscard]] inline std::string_view RemovePrefixView(std::string_view str, std::string_view prefix)
{
if (str.substr(0, prefix.size()) == prefix) {
return str.substr(prefix.size());
}
return str;
}
[[nodiscard]] inline std::string RemovePrefix(std::string_view str, std::string_view prefix)
{
return std::string(RemovePrefixView(str, prefix));
}
/**
* Join all container items. Typically used to concatenate strings but accepts
* containers with elements of any type.
*
* @param container The items to join
* @param separator The separator
* @param unary_op Apply this operator to each item
*/
template <typename C, typename S, typename UnaryOp>
auto Join(const C& container, const S& separator, UnaryOp unary_op)
{
decltype(unary_op(*container.begin())) ret;
bool first{true};
for (const auto& item : container) {
if (!first) ret += separator;
ret += unary_op(item);
first = false;
}
return ret;
}
template <typename C, typename S>
auto Join(const C& container, const S& separator)
{
return Join(container, separator, [](const auto& i) { return i; });
}
/**
* Create an unordered multi-line list of items.
*/
inline std::string MakeUnorderedList(const std::vector<std::string>& items)
{
return Join(items, "\n", [](const std::string& item) { return "- " + item; });
}
/**
* Check if a string does not contain any embedded NUL (\0) characters
*/
[[nodiscard]] inline bool ContainsNoNUL(std::string_view str) noexcept
{
for (auto c : str) {
if (c == 0) return false;
}
return true;
}
/**
* Locale-independent version of std::to_string
*/
template <typename T>
std::string ToString(const T& t)
{
std::ostringstream oss;
oss.imbue(std::locale::classic());
oss << t;
return oss.str();
}
/**
* Check whether a container begins with the given prefix.
*/
template <typename T1, size_t PREFIX_LEN>
[[nodiscard]] inline bool HasPrefix(const T1& obj,
const std::array<uint8_t, PREFIX_LEN>& prefix)
{
return obj.size() >= PREFIX_LEN &&
std::equal(std::begin(prefix), std::end(prefix), std::begin(obj));
}
#endif // BITCOIN_UTIL_STRING_H

63
src/i2p/util/syserror.cpp Normal file
View File

@ -0,0 +1,63 @@
// Copyright (c) 2020-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include "../tinyformat.h"
#include "../util/syserror.h"
#include <cstring>
#include <string>
#if defined(WIN32)
#include <codecvt>
#include <locale>
#include <windows.h>
#endif
std::string SysErrorString(int err) {
char buf[1024];
/* Too bad there are three incompatible implementations of the
* thread-safe strerror. */
const char *s = nullptr;
#ifdef WIN32
if (strerror_s(buf, sizeof(buf), err) == 0)
s = buf;
#else
#ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the \
passed buffer */
s = strerror_r(err, buf, sizeof(buf));
#else /* POSIX variant always returns message in buffer */
if (strerror_r(err, buf, sizeof(buf)) == 0)
s = buf;
#endif
#endif
if (s != nullptr) {
return tfm::format("%s (%d)", s, err);
} else {
return tfm::format("Unknown error (%d)", err);
}
}
#if defined(WIN32)
std::string Win32ErrorString(int err) {
wchar_t buf[256];
buf[0] = 0;
if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buf, ARRAYSIZE(buf), nullptr)) {
return strprintf(
"%s (%d)",
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>()
.to_bytes(buf),
err);
} else {
return strprintf("Unknown error (%d)", err);
}
}
#endif

20
src/i2p/util/syserror.h Normal file
View File

@ -0,0 +1,20 @@
// Copyright (c) 2010-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_SYSERROR_H
#define BITCOIN_UTIL_SYSERROR_H
#include <string>
/** Return system error string from errno value. Use this instead of
* std::strerror, which is not thread-safe. For network errors use
* NetworkErrorString from sock.h instead.
*/
std::string SysErrorString(int err);
#if defined(WIN32)
std::string Win32ErrorString(int err);
#endif
#endif // BITCOIN_UTIL_SYSERROR_H

View File

@ -0,0 +1,31 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "threadinterrupt.h"
#include "../sync.h"
CThreadInterrupt::CThreadInterrupt() : flag(false) {}
CThreadInterrupt::operator bool() const {
return flag.load(std::memory_order_acquire);
}
void CThreadInterrupt::reset() { flag.store(false, std::memory_order_release); }
void CThreadInterrupt::operator()() {
{
LOCK(mut);
flag.store(true, std::memory_order_release);
}
cond.notify_all();
}
bool CThreadInterrupt::sleep_for(Clock::duration rel_time) {
WAIT_LOCK(mut, lock);
return !cond.wait_for(lock, rel_time, [this]() {
return flag.load(std::memory_order_acquire);
});
}

View File

@ -0,0 +1,42 @@
// Copyright (c) 2016-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_THREADINTERRUPT_H
#define BITCOIN_UTIL_THREADINTERRUPT_H
#include "../sync.h"
#include "../threadsafety.h"
#include <atomic>
#include <chrono>
#include <condition_variable>
/**
* A helper class for interruptible sleeps. Calling operator() will interrupt
* any current sleep, and after that point operator bool() will return true
* until reset.
*
* This class should not be used in a signal handler. It uses thread
* synchronization primitives that are not safe to use with signals. If sending
* an interrupt from a signal handler is necessary, the \ref SignalInterrupt
* class can be used instead.
*/
class CThreadInterrupt
{
public:
using Clock = std::chrono::steady_clock;
CThreadInterrupt();
explicit operator bool() const;
void operator()() EXCLUSIVE_LOCKS_REQUIRED(!mut);
void reset();
bool sleep_for(Clock::duration rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut);
private:
std::condition_variable cond;
Mutex mut;
std::atomic<bool> flag;
};
#endif // BITCOIN_UTIL_THREADINTERRUPT_H

122
src/i2p/util/time.h Normal file
View File

@ -0,0 +1,122 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_TIME_H
#define BITCOIN_UTIL_TIME_H
#include <chrono> // IWYU pragma: export
#include <cstdint>
#include <string>
using namespace std::chrono_literals;
/** Mockable clock in the context of tests, otherwise the system clock */
struct NodeClock : public std::chrono::system_clock {
using time_point = std::chrono::time_point<NodeClock>;
/** Return current system time or mocked time, if set */
static time_point now() noexcept;
static std::time_t to_time_t(const time_point&) = delete; // unused
static time_point from_time_t(std::time_t) = delete; // unused
};
using NodeSeconds = std::chrono::time_point<NodeClock, std::chrono::seconds>;
using SteadyClock = std::chrono::steady_clock;
using SteadySeconds = std::chrono::time_point<std::chrono::steady_clock, std::chrono::seconds>;
using SteadyMilliseconds = std::chrono::time_point<std::chrono::steady_clock, std::chrono::milliseconds>;
using SteadyMicroseconds = std::chrono::time_point<std::chrono::steady_clock, std::chrono::microseconds>;
using SystemClock = std::chrono::system_clock;
void UninterruptibleSleep(const std::chrono::microseconds& n);
/**
* Helper to count the seconds of a duration/time_point.
*
* All durations/time_points should be using std::chrono and calling this should generally
* be avoided in code. Though, it is still preferred to an inline t.count() to
* protect against a reliance on the exact type of t.
*
* This helper is used to convert durations/time_points before passing them over an
* interface that doesn't support std::chrono (e.g. RPC, debug log, or the GUI)
*/
template <typename Dur1, typename Dur2>
constexpr auto Ticks(Dur2 d)
{
return std::chrono::duration_cast<Dur1>(d).count();
}
template <typename Duration, typename Timepoint>
constexpr auto TicksSinceEpoch(Timepoint t)
{
return Ticks<Duration>(t.time_since_epoch());
}
constexpr int64_t count_seconds(std::chrono::seconds t) { return t.count(); }
constexpr int64_t count_milliseconds(std::chrono::milliseconds t) { return t.count(); }
constexpr int64_t count_microseconds(std::chrono::microseconds t) { return t.count(); }
using HoursDouble = std::chrono::duration<double, std::chrono::hours::period>;
using SecondsDouble = std::chrono::duration<double, std::chrono::seconds::period>;
using MillisecondsDouble = std::chrono::duration<double, std::chrono::milliseconds::period>;
/**
* DEPRECATED
* Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
* ClockType is
* - SteadyClock/std::chrono::steady_clock for steady time
* - SystemClock/std::chrono::system_clock for system time
* - NodeClock for mockable system time
*/
int64_t GetTime();
/**
* DEPRECATED
* Use SetMockTime with chrono type
*
* @param[in] nMockTimeIn Time in seconds.
*/
void SetMockTime(int64_t nMockTimeIn);
/** For testing. Set e.g. with the setmocktime rpc, or -mocktime argument */
void SetMockTime(std::chrono::seconds mock_time_in);
/** For testing */
std::chrono::seconds GetMockTime();
/**
* Return the current time point cast to the given precision. Only use this
* when an exact precision is needed, otherwise use T::clock::now() directly.
*/
template <typename T>
T Now()
{
return std::chrono::time_point_cast<typename T::duration>(T::clock::now());
}
/** DEPRECATED, see GetTime */
template <typename T>
T GetTime()
{
return Now<std::chrono::time_point<NodeClock, T>>().time_since_epoch();
}
/**
* ISO 8601 formatting is preferred. Use the FormatISO8601{DateTime,Date}
* helper functions if possible.
*/
std::string FormatISO8601DateTime(int64_t nTime);
std::string FormatISO8601Date(int64_t nTime);
/**
* Convert milliseconds to a struct timeval for e.g. select.
*/
struct timeval MillisToTimeval(int64_t nTimeout);
/**
* Convert milliseconds to a struct timeval for e.g. select.
*/
struct timeval MillisToTimeval(std::chrono::milliseconds ms);
/** Sanity check epoch match normal Unix epoch */
bool ChronoSanityCheck();
#endif // BITCOIN_UTIL_TIME_H

78
src/i2p/util/vector.h Normal file
View File

@ -0,0 +1,78 @@
// Copyright (c) 2019-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_VECTOR_H
#define BITCOIN_UTIL_VECTOR_H
#include <functional>
#include <initializer_list>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>
/** Construct a vector with the specified elements.
*
* This is preferable over the list initializing constructor of std::vector:
* - It automatically infers the element type from its arguments.
* - If any arguments are rvalue references, they will be moved into the vector
* (list initialization always copies).
*/
template <typename... Args>
inline std::vector<typename std::common_type<Args...>::type>
Vector(Args &&...args) {
std::vector<typename std::common_type<Args...>::type> ret;
ret.reserve(sizeof...(args));
// The line below uses the trick from
// https://www.experts-exchange.com/articles/32502/None-recursive-variadic-templates-with-std-initializer-list.html
(void)std::initializer_list<int>{
(ret.emplace_back(std::forward<Args>(args)), 0)...};
return ret;
}
/** Concatenate two vectors, moving elements. */
template <typename V> inline V Cat(V v1, V &&v2) {
v1.reserve(v1.size() + v2.size());
for (auto &arg : v2) {
v1.push_back(std::move(arg));
}
return v1;
}
/** Concatenate two vectors. */
template <typename V> inline V Cat(V v1, const V &v2) {
v1.reserve(v1.size() + v2.size());
for (const auto &arg : v2) {
v1.push_back(arg);
}
return v1;
}
/** Clear a vector (or std::deque) and release its allocated memory. */
template <typename V> inline void ClearShrink(V &v) noexcept {
// There are various ways to clear a vector and release its memory:
//
// 1. V{}.swap(v)
// 2. v = V{}
// 3. v = {}; v.shrink_to_fit();
// 4. v.clear(); v.shrink_to_fit();
//
// (2) does not appear to release memory in glibc debug mode, even if
// v.shrink_to_fit() follows. (3) and (4) rely on std::vector::shrink_to_fit,
// which is only a non-binding request. Therefore, we use method (1).
V{}.swap(v);
}
template <typename V, typename L>
inline std::optional<V> FindFirst(const std::vector<V> &vec, const L fnc) {
for (const auto &el : vec) {
if (fnc(el)) {
return el;
}
}
return std::nullopt;
}
#endif // BITCOIN_UTIL_VECTOR_H