mirror of
https://github.com/monero-project/monero.git
synced 2024-10-01 11:49:47 -04:00
"copy-pasting only 500 lines of code"
This commit is contained in:
parent
83e920a5b7
commit
f67b0578a9
5
.gitignore
vendored
5
.gitignore
vendored
@ -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 ###
|
||||||
*~
|
*~
|
||||||
|
|
||||||
|
@ -2,86 +2,83 @@
|
|||||||
#
|
#
|
||||||
# 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)
|
||||||
# miniupnp changed their static define
|
# miniupnp changed their static define
|
||||||
add_definitions(-DMINIUPNP_STATICLIB)
|
add_definitions(-DMINIUPNP_STATICLIB)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
function (monero_private_headers group)
|
function(monero_private_headers group)
|
||||||
source_group("${group}\\Private"
|
source_group("${group}\\Private" FILES ${ARGN})
|
||||||
FILES
|
endfunction()
|
||||||
${ARGN})
|
|
||||||
endfunction ()
|
|
||||||
|
|
||||||
function (monero_install_headers subdir)
|
function(monero_install_headers subdir)
|
||||||
install(
|
install(
|
||||||
FILES ${ARGN}
|
FILES ${ARGN}
|
||||||
DESTINATION "include/${subdir}"
|
DESTINATION "include/${subdir}"
|
||||||
COMPONENT development)
|
COMPONENT development)
|
||||||
endfunction ()
|
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}
|
||||||
if (STATIC)
|
APPEND
|
||||||
set_property(TARGET "${target}"
|
PROPERTY COMPILE_DEFINITIONS "STACK_TRACE")
|
||||||
APPEND PROPERTY LINK_FLAGS "-Wl,--wrap=__cxa_throw")
|
if(STATIC)
|
||||||
|
set_property(
|
||||||
|
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}")
|
||||||
monero_set_target_strip ("${name}")
|
monero_set_target_strip("${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
282
src/i2p/.logging.h
Normal 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
45
src/i2p/CMakeLists.txt
Normal 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
27
src/i2p/attributes.h
Normal 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
31
src/i2p/chainparams.h
Normal 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
510
src/i2p/common/args.h
Normal 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 §ion,
|
||||||
|
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
117
src/i2p/common/settings.h
Normal 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 §ion,
|
||||||
|
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 §ion,
|
||||||
|
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 §ion,
|
||||||
|
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
|
44
src/i2p/compat/assumptions.h
Normal file
44
src/i2p/compat/assumptions.h
Normal 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
79
src/i2p/compat/byteswap.h
Normal 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
115
src/i2p/compat/compat.h
Normal 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
74
src/i2p/compat/endian.h
Normal 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
|
29
src/i2p/consensus/amount.h
Normal file
29
src/i2p/consensus/amount.h
Normal 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
151
src/i2p/consensus/params.h
Normal 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
159
src/i2p/crypto/chacha20.h
Normal 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
85
src/i2p/crypto/common.h
Normal 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
|
32
src/i2p/crypto/hmac_sha512.cpp
Normal file
32
src/i2p/crypto/hmac_sha512.cpp
Normal 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);
|
||||||
|
}
|
30
src/i2p/crypto/hmac_sha512.h
Normal file
30
src/i2p/crypto/hmac_sha512.h
Normal 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
|
320
src/i2p/crypto/ripemd160.cpp
Normal file
320
src/i2p/crypto/ripemd160.cpp
Normal 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;
|
||||||
|
}
|
27
src/i2p/crypto/ripemd160.h
Normal file
27
src/i2p/crypto/ripemd160.h
Normal 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
55
src/i2p/crypto/sha256.h
Normal 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
245
src/i2p/crypto/sha3.cpp
Normal 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
41
src/i2p/crypto/sha3.h
Normal 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
289
src/i2p/crypto/sha512.cpp
Normal 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
28
src/i2p/crypto/sha512.h
Normal 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
48
src/i2p/crypto/siphash.h
Normal 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
98
src/i2p/hash.cpp
Normal 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
221
src/i2p/hash.h
Normal 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
529
src/i2p/i2p.cpp
Normal 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
314
src/i2p/i2p.h
Normal 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
43
src/i2p/kernel/chain.h
Normal 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
|
198
src/i2p/kernel/chainparams.h
Normal file
198
src/i2p/kernel/chainparams.h
Normal 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
|
13
src/i2p/kernel/messagestartchars.h
Normal file
13
src/i2p/kernel/messagestartchars.h
Normal 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
1081
src/i2p/netaddress.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1181
src/i2p/netaddress.h
Normal file
1181
src/i2p/netaddress.h
Normal file
File diff suppressed because it is too large
Load Diff
942
src/i2p/netbase.cpp
Normal file
942
src/i2p/netbase.cpp
Normal 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
345
src/i2p/netbase.h
Normal 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
541
src/i2p/prevector.h
Normal 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
158
src/i2p/primitives/block.h
Normal 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
|
442
src/i2p/primitives/transaction.h
Normal file
442
src/i2p/primitives/transaction.h
Normal 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
302
src/i2p/random.h
Normal 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
1187
src/i2p/serialize.h
Normal file
File diff suppressed because it is too large
Load Diff
354
src/i2p/span.h
Normal file
354
src/i2p/span.h
Normal 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
331
src/i2p/sync.cpp
Normal 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
430
src/i2p/sync.h
Normal 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
77
src/i2p/threadsafety.h
Normal 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
1184
src/i2p/tinyformat.h
Normal file
File diff suppressed because it is too large
Load Diff
136
src/i2p/uint256.h
Normal file
136
src/i2p/uint256.h
Normal 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
22
src/i2p/util/chaintype.h
Normal 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
126
src/i2p/util/fs.cpp
Normal 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
253
src/i2p/util/fs.h
Normal 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
48
src/i2p/util/hash_type.h
Normal 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
20
src/i2p/util/macros.h
Normal 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
|
28
src/i2p/util/readwritefile.h
Normal file
28
src/i2p/util/readwritefile.h
Normal 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
429
src/i2p/util/sock.cpp
Normal 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
311
src/i2p/util/sock.h
Normal 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
|
79
src/i2p/util/spanparsing.h
Normal file
79
src/i2p/util/spanparsing.h
Normal 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
|
553
src/i2p/util/strencodings.cpp
Normal file
553
src/i2p/util/strencodings.cpp
Normal 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
405
src/i2p/util/strencodings.h
Normal 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
128
src/i2p/util/string.h
Normal 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
63
src/i2p/util/syserror.cpp
Normal 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
20
src/i2p/util/syserror.h
Normal 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
|
31
src/i2p/util/threadinterrupt.cpp
Normal file
31
src/i2p/util/threadinterrupt.cpp
Normal 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);
|
||||||
|
});
|
||||||
|
}
|
42
src/i2p/util/threadinterrupt.h
Normal file
42
src/i2p/util/threadinterrupt.h
Normal 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
122
src/i2p/util/time.h
Normal 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
78
src/i2p/util/vector.h
Normal 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
|
Loading…
Reference in New Issue
Block a user