mirror of https://github.com/oxen-io/oxen-core.git
Compare commits
8 Commits
6b662f1f5f
...
cab17962d1
Author | SHA1 | Date |
---|---|---|
Sean | cab17962d1 | |
Jason Rhinelander | 5436df9615 | |
Jason Rhinelander | 0cef2a9491 | |
Jason Rhinelander | 6f0c74ab9b | |
Jason Rhinelander | 67818e7cbf | |
Jason Rhinelander | 13409ad00e | |
Sean Darcy | 45db02de78 | |
Sean Darcy | 97aa52ed4e |
|
@ -1,56 +1,50 @@
|
|||
|
||||
BasedOnStyle: Google
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
AlignConsecutiveAssignments: 'false'
|
||||
AlignConsecutiveDeclarations: 'false'
|
||||
AlignEscapedNewlinesLeft: 'true'
|
||||
AlignOperands: 'false'
|
||||
AlignTrailingComments: 'true'
|
||||
AllowShortBlocksOnASingleLine: 'false'
|
||||
AllowShortCaseLabelsOnASingleLine: 'false'
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: 'false'
|
||||
AllowShortLoopsOnASingleLine: 'false'
|
||||
AlwaysBreakAfterDefinitionReturnType: All
|
||||
AlwaysBreakAfterReturnType: All
|
||||
AlwaysBreakTemplateDeclarations: 'true'
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: true
|
||||
AfterClass: true
|
||||
AfterControlStatement: true
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: true
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
AfterExternBlock: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
BreakBeforeTernaryOperators: 'true'
|
||||
BreakConstructorInitializersBeforeComma: 'true'
|
||||
Cpp11BracedListStyle: 'true'
|
||||
KeepEmptyLinesAtTheStartOfBlocks: 'false'
|
||||
NamespaceIndentation: All
|
||||
PenaltyBreakString: '3'
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: AlignAfterOperator
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: AfterColon
|
||||
Cpp11BracedListStyle: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
NamespaceIndentation: Inner
|
||||
CompactNamespaces: true
|
||||
PenaltyBreakString: 3
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpacesInAngles: 'false'
|
||||
SpacesInContainerLiterals: 'false'
|
||||
SpacesInParentheses: 'false'
|
||||
SpacesInSquareBrackets: 'false'
|
||||
Standard: Cpp11
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: c++17
|
||||
UseTab: Never
|
||||
SortIncludes: false
|
||||
SortIncludes: true
|
||||
ColumnLimit: 100
|
||||
IndentWidth: 4
|
||||
AccessModifierOffset: -2
|
||||
ConstructorInitializerIndentWidth: 8
|
||||
ContinuationIndentWidth: 8
|
||||
|
||||
|
||||
# treat pointers and reference declarations as if part of the type
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Left
|
||||
|
||||
# when wrapping function calls/declarations, force each parameter to have its own line
|
||||
BinPackParameters: 'false'
|
||||
BinPackArguments: 'false'
|
||||
BinPackParameters: false
|
||||
BinPackArguments: false
|
||||
|
|
|
@ -237,6 +237,24 @@ local gui_wallet_step_darwin = {
|
|||
|
||||
|
||||
[
|
||||
{
|
||||
name: 'lint check',
|
||||
kind: 'pipeline',
|
||||
type: 'docker',
|
||||
steps: [{
|
||||
name: 'build',
|
||||
image: docker_base + 'lint',
|
||||
pull: 'always',
|
||||
commands: [
|
||||
'echo "Building on ${DRONE_STAGE_MACHINE}"',
|
||||
apt_get_quiet + ' update',
|
||||
apt_get_quiet + ' install -y eatmydata',
|
||||
'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y git clang-format-14 jsonnet',
|
||||
'./contrib/drone-format-verify.sh',
|
||||
],
|
||||
}],
|
||||
},
|
||||
|
||||
// Static build to make wallet3:
|
||||
{
|
||||
name: 'Static (wallet3)',
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
test "x$IGNORE" != "x" && exit 0
|
||||
set -e
|
||||
repo=$(readlink -e $(dirname $0)/../)
|
||||
clang-format-14 -i $(find $repo/{src,pybind} | grep -E '\.[hc](pp)?$')
|
||||
jsonnetfmt -i $repo/.drone.jsonnet
|
||||
git --no-pager diff --exit-code --color || (echo -ne '\n\n\e[31;1mLint check failed; please run ./contrib/format.sh\e[0m\n\n' ; exit 1)
|
|
@ -1,8 +1,10 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
CLANG_FORMAT_DESIRED_VERSION=11
|
||||
CLANG_FORMAT_DESIRED_VERSION=14
|
||||
|
||||
TARGET_DIRS=(src/wallet3 src/sqlitedb src/logging)
|
||||
TARGET_DIRS=(src pybind)
|
||||
|
||||
set -e
|
||||
|
||||
binary=$(which clang-format-$CLANG_FORMAT_DESIRED_VERSION 2>/dev/null)
|
||||
if [ $? -ne 0 ]; then
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 707a83609fb64d09b61ed1e56c82bf692050d2a1
|
||||
Subproject commit f6c516dce886b93435e28b4211d4e3a61245a33f
|
|
@ -9,7 +9,13 @@ pybind11_add_module(pywallet3 MODULE
|
|||
wallet/daemon_comms_config.cpp
|
||||
wallet/rpc_config.cpp
|
||||
)
|
||||
target_link_libraries(pywallet3 PUBLIC wallet3)
|
||||
target_link_libraries(pywallet3
|
||||
PUBLIC
|
||||
wallet3
|
||||
logging
|
||||
oxen::logging
|
||||
spdlog::spdlog
|
||||
fmt::fmt)
|
||||
target_include_directories(pywallet3 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
file(GENERATE
|
||||
|
|
|
@ -1,34 +1,25 @@
|
|||
#pragma once
|
||||
#include <pybind11/functional.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
namespace wallet
|
||||
{
|
||||
void
|
||||
Wallet_Init(py::module& mod);
|
||||
namespace wallet {
|
||||
void Wallet_Init(py::module& mod);
|
||||
|
||||
void
|
||||
Keyring_Init(py::module& mod);
|
||||
void Keyring_Init(py::module& mod);
|
||||
|
||||
void
|
||||
KeyringManager_Init(py::module& mod);
|
||||
void KeyringManager_Init(py::module& mod);
|
||||
|
||||
void
|
||||
WalletConfig_Init(py::module& mod);
|
||||
void WalletConfig_Init(py::module& mod);
|
||||
|
||||
void
|
||||
GeneralWalletConfig_Init(py::module& mod);
|
||||
void GeneralWalletConfig_Init(py::module& mod);
|
||||
|
||||
void
|
||||
DaemonCommsConfig_Init(py::module& mod);
|
||||
void DaemonCommsConfig_Init(py::module& mod);
|
||||
|
||||
void
|
||||
RPCConfig_Init(py::module& mod);
|
||||
void RPCConfig_Init(py::module& mod);
|
||||
|
||||
void
|
||||
LoggingConfig_Init(py::module& mod);
|
||||
void LoggingConfig_Init(py::module& mod);
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
#include "common.hpp"
|
||||
|
||||
PYBIND11_MODULE(pywallet3, m)
|
||||
{
|
||||
wallet::Wallet_Init(m);
|
||||
wallet::Keyring_Init(m);
|
||||
wallet::KeyringManager_Init(m);
|
||||
wallet::WalletConfig_Init(m);
|
||||
wallet::GeneralWalletConfig_Init(m);
|
||||
wallet::LoggingConfig_Init(m);
|
||||
wallet::DaemonCommsConfig_Init(m);
|
||||
wallet::RPCConfig_Init(m);
|
||||
PYBIND11_MODULE(pywallet3, m) {
|
||||
wallet::Wallet_Init(m);
|
||||
wallet::Keyring_Init(m);
|
||||
wallet::KeyringManager_Init(m);
|
||||
wallet::WalletConfig_Init(m);
|
||||
wallet::GeneralWalletConfig_Init(m);
|
||||
wallet::LoggingConfig_Init(m);
|
||||
wallet::DaemonCommsConfig_Init(m);
|
||||
wallet::RPCConfig_Init(m);
|
||||
}
|
||||
|
|
|
@ -9,8 +9,15 @@ pywallet3_sources = ['$<TARGET_PROPERTY:pywallet3,SOURCE_DIR>/' + src for src in
|
|||
'$<JOIN:$<TARGET_PROPERTY:pywallet3,SOURCES>,',
|
||||
'>')]
|
||||
pywallet3_include_dirs = [
|
||||
'$<JOIN:$<TARGET_PROPERTY:wallet3,INCLUDE_DIRECTORIES>,',
|
||||
'$<JOIN:$<TARGET_PROPERTY:pywallet3,INCLUDE_DIRECTORIES>,',
|
||||
'>']
|
||||
pywallet3_defs = []
|
||||
for d in [
|
||||
'$<JOIN:$<TARGET_PROPERTY:pywallet3,COMPILE_DEFINITIONS>,',
|
||||
'>']:
|
||||
parts = d.split("=", 1)
|
||||
pywallet3_defs.append((parts[0], parts[1] if len(parts) == 2 else None))
|
||||
|
||||
|
||||
# Note:
|
||||
# Sort input source files if you glob sources to ensure bit-for-bit
|
||||
|
@ -21,6 +28,7 @@ ext_modules = [Pybind11Extension(
|
|||
pywallet3_sources,
|
||||
cxx_std=17,
|
||||
include_dirs=pywallet3_include_dirs,
|
||||
define_macros=pywallet3_defs,
|
||||
extra_objects=['$<TARGET_PROPERTY:wallet3,BINARY_DIR>/libwallet3_merged.a'],
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
#include "../common.hpp"
|
||||
#include "wallet3/config/config.hpp"
|
||||
|
||||
namespace wallet
|
||||
{
|
||||
void
|
||||
DaemonCommsConfig_Init(py::module& mod)
|
||||
{
|
||||
namespace wallet {
|
||||
void DaemonCommsConfig_Init(py::module& mod) {
|
||||
py::class_<DaemonCommsConfig, std::shared_ptr<DaemonCommsConfig>>(mod, "DaemonCommsConfig")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("address", &DaemonCommsConfig::address);
|
||||
}
|
||||
.def(py::init<>())
|
||||
.def_readwrite("address", &DaemonCommsConfig::address);
|
||||
}
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
#include "../common.hpp"
|
||||
#include "wallet3/config/config.hpp"
|
||||
|
||||
namespace wallet
|
||||
{
|
||||
void
|
||||
GeneralWalletConfig_Init(py::module& mod)
|
||||
{
|
||||
py::class_<GeneralWalletConfig, std::shared_ptr<GeneralWalletConfig>>(mod, "GeneralWalletConfig")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("datadir", &GeneralWalletConfig::datadir)
|
||||
.def_readwrite("append_network_type_to_datadir", &GeneralWalletConfig::append_network_type_to_datadir);
|
||||
}
|
||||
namespace wallet {
|
||||
void GeneralWalletConfig_Init(py::module& mod) {
|
||||
py::class_<GeneralWalletConfig, std::shared_ptr<GeneralWalletConfig>>(
|
||||
mod, "GeneralWalletConfig")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("datadir", &GeneralWalletConfig::datadir)
|
||||
.def_readwrite(
|
||||
"append_network_type_to_datadir",
|
||||
&GeneralWalletConfig::append_network_type_to_datadir);
|
||||
}
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -1,31 +1,36 @@
|
|||
#include "../common.hpp"
|
||||
#include "wallet3/keyring.hpp"
|
||||
|
||||
#include <crypto/crypto.h>
|
||||
#include <common/hex.h>
|
||||
#include <crypto/crypto.h>
|
||||
#include <cryptonote_basic/cryptonote_basic.h>
|
||||
|
||||
namespace wallet
|
||||
{
|
||||
void
|
||||
Keyring_Init(py::module& mod)
|
||||
{
|
||||
py::class_<Keyring, std::shared_ptr<Keyring>>(mod, "Keyring")
|
||||
.def(py::init([]( std::string ssk, std::string spk, std::string vsk, std::string vpk, std::string nettype) {
|
||||
auto type = cryptonote::network_type::MAINNET;
|
||||
if (nettype == "testnet") type = cryptonote::network_type::TESTNET;
|
||||
else if (nettype == "devnet") type = cryptonote::network_type::DEVNET;
|
||||
crypto::secret_key spend_priv;
|
||||
crypto::public_key spend_pub;
|
||||
crypto::secret_key view_priv;
|
||||
crypto::public_key view_pub;
|
||||
tools::hex_to_type<crypto::secret_key>(ssk, spend_priv);
|
||||
tools::hex_to_type<crypto::public_key>(spk, spend_pub);
|
||||
tools::hex_to_type<crypto::secret_key>(vsk, view_priv);
|
||||
tools::hex_to_type<crypto::public_key>(vpk, view_pub);
|
||||
return Keyring(spend_priv, spend_pub, view_priv, view_pub, std::move(type)); }))
|
||||
#include "../common.hpp"
|
||||
|
||||
.def("get_main_address", &Keyring::get_main_address);
|
||||
}
|
||||
namespace wallet {
|
||||
void Keyring_Init(py::module& mod) {
|
||||
py::class_<Keyring, std::shared_ptr<Keyring>>(mod, "Keyring")
|
||||
.def(py::init([](std::string ssk,
|
||||
std::string spk,
|
||||
std::string vsk,
|
||||
std::string vpk,
|
||||
std::string nettype) {
|
||||
auto type = cryptonote::network_type::MAINNET;
|
||||
if (nettype == "testnet")
|
||||
type = cryptonote::network_type::TESTNET;
|
||||
else if (nettype == "devnet")
|
||||
type = cryptonote::network_type::DEVNET;
|
||||
crypto::secret_key spend_priv;
|
||||
crypto::public_key spend_pub;
|
||||
crypto::secret_key view_priv;
|
||||
crypto::public_key view_pub;
|
||||
tools::hex_to_type<crypto::secret_key>(ssk, spend_priv);
|
||||
tools::hex_to_type<crypto::public_key>(spk, spend_pub);
|
||||
tools::hex_to_type<crypto::secret_key>(vsk, view_priv);
|
||||
tools::hex_to_type<crypto::public_key>(vpk, view_pub);
|
||||
return Keyring(spend_priv, spend_pub, view_priv, view_pub, std::move(type));
|
||||
}))
|
||||
|
||||
.def("get_main_address", &Keyring::get_main_address);
|
||||
}
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
#include "../common.hpp"
|
||||
#include "wallet3/keyring_manager.hpp"
|
||||
|
||||
#include <crypto/crypto.h>
|
||||
#include <cryptonote_basic/cryptonote_basic.h>
|
||||
|
||||
namespace wallet
|
||||
{
|
||||
void
|
||||
KeyringManager_Init(py::module& mod)
|
||||
{
|
||||
py::class_<KeyringManager, std::shared_ptr<KeyringManager>>(mod, "KeyringManager")
|
||||
.def(py::init([](std::string nettype) {
|
||||
auto type = cryptonote::network_type::MAINNET;
|
||||
if (nettype == "testnet") type = cryptonote::network_type::TESTNET;
|
||||
else if (nettype == "devnet") type = cryptonote::network_type::DEVNET;
|
||||
return KeyringManager(std::move(type)); }))
|
||||
#include "../common.hpp"
|
||||
|
||||
.def("generate_keyring_from_electrum_seed", &KeyringManager::generate_keyring_from_electrum_seed);
|
||||
}
|
||||
namespace wallet {
|
||||
void KeyringManager_Init(py::module& mod) {
|
||||
py::class_<KeyringManager, std::shared_ptr<KeyringManager>>(mod, "KeyringManager")
|
||||
.def(py::init([](std::string nettype) {
|
||||
auto type = cryptonote::network_type::MAINNET;
|
||||
if (nettype == "testnet")
|
||||
type = cryptonote::network_type::TESTNET;
|
||||
else if (nettype == "devnet")
|
||||
type = cryptonote::network_type::DEVNET;
|
||||
return KeyringManager(std::move(type));
|
||||
}))
|
||||
|
||||
.def("generate_keyring_from_electrum_seed",
|
||||
&KeyringManager::generate_keyring_from_electrum_seed);
|
||||
}
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
#include "../common.hpp"
|
||||
#include "wallet3/config/config.hpp"
|
||||
|
||||
namespace wallet
|
||||
{
|
||||
void
|
||||
LoggingConfig_Init(py::module& mod)
|
||||
{
|
||||
namespace wallet {
|
||||
void LoggingConfig_Init(py::module& mod) {
|
||||
py::class_<LoggingConfig, std::shared_ptr<LoggingConfig>>(mod, "LoggingConfig")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("level", &LoggingConfig::level)
|
||||
.def_readwrite("save_logs_in_subdirectory", &LoggingConfig::save_logs_in_subdirectory)
|
||||
.def_readwrite("logdir", &LoggingConfig::logdir)
|
||||
.def_readwrite("log_filename", &LoggingConfig::log_filename);
|
||||
}
|
||||
.def(py::init<>())
|
||||
.def_readwrite("level", &LoggingConfig::level)
|
||||
.def_readwrite("save_logs_in_subdirectory", &LoggingConfig::save_logs_in_subdirectory)
|
||||
.def_readwrite("logdir", &LoggingConfig::logdir)
|
||||
.def_readwrite("log_filename", &LoggingConfig::log_filename);
|
||||
}
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
#include "../common.hpp"
|
||||
#include "wallet3/config/config.hpp"
|
||||
|
||||
namespace wallet
|
||||
{
|
||||
void
|
||||
RPCConfig_Init(py::module& mod)
|
||||
{
|
||||
namespace wallet {
|
||||
void RPCConfig_Init(py::module& mod) {
|
||||
py::class_<rpc::Config, std::shared_ptr<rpc::Config>>(mod, "RPCConfig")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("sockname", &rpc::Config::sockname);
|
||||
}
|
||||
.def(py::init<>())
|
||||
.def_readwrite("sockname", &rpc::Config::sockname);
|
||||
}
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -1,41 +1,50 @@
|
|||
#include "../common.hpp"
|
||||
#include <oxenmq/oxenmq.h>
|
||||
|
||||
#include <wallet3/wallet.hpp>
|
||||
#include <oxen/log.hpp>
|
||||
#include <wallet3/config/config.hpp>
|
||||
#include <wallet3/default_daemon_comms.hpp>
|
||||
#include <wallet3/keyring.hpp>
|
||||
#include <wallet3/config/config.hpp>
|
||||
#include <oxenmq/oxenmq.h>
|
||||
#include <oxen/log.hpp>
|
||||
#include <wallet3/wallet.hpp>
|
||||
|
||||
#include "../common.hpp"
|
||||
|
||||
static auto logcat = oxen::log::Cat("omq");
|
||||
|
||||
void omq_logger(oxenmq::LogLevel level, const char* file, int line, std::string message) {
|
||||
constexpr std::string_view format = "[{}:{}]: {}";
|
||||
switch (level) {
|
||||
case oxenmq::LogLevel::fatal: oxen::log::critical(logcat, format, file, line, message); break;
|
||||
case oxenmq::LogLevel::error: oxen::log::error(logcat, format, file, line, message); break;
|
||||
case oxenmq::LogLevel::warn: oxen::log::warning(logcat, format, file, line, message); break;
|
||||
case oxenmq::LogLevel::info: oxen::log::info(logcat, format, file, line, message); break;
|
||||
case oxenmq::LogLevel::debug: oxen::log::debug(logcat, format, file, line, message); break;
|
||||
case oxenmq::LogLevel::trace: oxen::log::trace(logcat, format, file, line, message); break;
|
||||
case oxenmq::LogLevel::fatal:
|
||||
oxen::log::critical(logcat, format, file, line, message);
|
||||
break;
|
||||
case oxenmq::LogLevel::error: oxen::log::error(logcat, format, file, line, message); break;
|
||||
case oxenmq::LogLevel::warn: oxen::log::warning(logcat, format, file, line, message); break;
|
||||
case oxenmq::LogLevel::info: oxen::log::info(logcat, format, file, line, message); break;
|
||||
case oxenmq::LogLevel::debug: oxen::log::debug(logcat, format, file, line, message); break;
|
||||
case oxenmq::LogLevel::trace: oxen::log::trace(logcat, format, file, line, message); break;
|
||||
}
|
||||
}
|
||||
|
||||
namespace wallet
|
||||
{
|
||||
void
|
||||
Wallet_Init(py::module& mod)
|
||||
{
|
||||
namespace wallet {
|
||||
void Wallet_Init(py::module& mod) {
|
||||
py::class_<Wallet, std::shared_ptr<Wallet>>(mod, "Wallet")
|
||||
.def(py::init([](const std::string& wallet_name, std::shared_ptr<Keyring> keyring, Config config) {
|
||||
auto& comms_config = config.daemon;
|
||||
auto& omq_rpc_config = config.omq_rpc;
|
||||
auto oxenmq = std::make_shared<oxenmq::OxenMQ>(omq_logger, oxenmq::LogLevel::info);
|
||||
auto comms = std::make_shared<DefaultDaemonComms>(oxenmq, comms_config);
|
||||
return Wallet::create(std::move(oxenmq), std::move(keyring), nullptr, std::move(comms), wallet_name + ".sqlite", "", std::move(config));
|
||||
}))
|
||||
.def("get_balance", &Wallet::get_balance)
|
||||
.def("deregister", &Wallet::deregister);
|
||||
}
|
||||
.def(py::init([](const std::string& wallet_name,
|
||||
std::shared_ptr<Keyring> keyring,
|
||||
Config config) {
|
||||
auto& comms_config = config.daemon;
|
||||
auto& omq_rpc_config = config.omq_rpc;
|
||||
auto oxenmq = std::make_shared<oxenmq::OxenMQ>(omq_logger, oxenmq::LogLevel::info);
|
||||
auto comms = std::make_shared<DefaultDaemonComms>(oxenmq, comms_config);
|
||||
return Wallet::create(
|
||||
std::move(oxenmq),
|
||||
std::move(keyring),
|
||||
nullptr,
|
||||
std::move(comms),
|
||||
wallet_name + ".sqlite",
|
||||
"",
|
||||
std::move(config));
|
||||
}))
|
||||
.def("get_balance", &Wallet::get_balance)
|
||||
.def("deregister", &Wallet::deregister);
|
||||
}
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
#include "../common.hpp"
|
||||
#include "wallet3/config/config.hpp"
|
||||
|
||||
namespace wallet
|
||||
{
|
||||
void
|
||||
WalletConfig_Init(py::module& mod)
|
||||
{
|
||||
namespace wallet {
|
||||
void WalletConfig_Init(py::module& mod) {
|
||||
py::class_<Config, std::shared_ptr<Config>>(mod, "WalletConfig")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("general", &Config::general)
|
||||
.def_readwrite("logging", &Config::logging)
|
||||
.def_readwrite("daemon", &Config::daemon)
|
||||
.def_readwrite("omq_rpc", &Config::omq_rpc);
|
||||
}
|
||||
.def(py::init<>())
|
||||
.def_readwrite("general", &Config::general)
|
||||
.def_readwrite("logging", &Config::logging)
|
||||
.def_readwrite("daemon", &Config::daemon)
|
||||
.def_readwrite("omq_rpc", &Config::omq_rpc);
|
||||
}
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright (c) 2014-2019, The Monero Project
|
||||
// Copyright (c) 2018, The Loki Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -27,309 +27,288 @@
|
|||
// 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.
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "cryptonote_basic/hardfork.h"
|
||||
#include "cryptonote_core/service_node_rules.h"
|
||||
#include "checkpoints/checkpoints.h"
|
||||
#include "epee/string_tools.h"
|
||||
#include "blockchain_db.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "ringct/rctOps.h"
|
||||
#include "common/hex.h"
|
||||
|
||||
#include "lmdb/db_lmdb.h"
|
||||
#include <chrono>
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
#include "checkpoints/checkpoints.h"
|
||||
#include "common/hex.h"
|
||||
#include "common/string_util.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "cryptonote_basic/hardfork.h"
|
||||
#include "cryptonote_core/service_node_rules.h"
|
||||
#include "epee/string_tools.h"
|
||||
#include "lmdb/db_lmdb.h"
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
static auto logcat = log::Cat("blockchain.db");
|
||||
namespace cryptonote {
|
||||
|
||||
static auto logcat = log::Cat("blockchain.db");
|
||||
|
||||
const command_line::arg_descriptor<std::string> arg_db_sync_mode = {
|
||||
"db-sync-mode"
|
||||
, "Specify sync option, using format [safe|fast|fastest]:[sync|async]:[<nblocks_per_sync>[blocks]|<nbytes_per_sync>[bytes]]."
|
||||
, "fast:async:250000000bytes"
|
||||
};
|
||||
const command_line::arg_descriptor<bool> arg_db_salvage = {
|
||||
"db-salvage"
|
||||
, "Try to salvage a blockchain database if it seems corrupted"
|
||||
, false
|
||||
};
|
||||
"db-sync-mode",
|
||||
"Specify sync option, using format "
|
||||
"[safe|fast|fastest]:[sync|async]:[<nblocks_per_sync>[blocks]|<nbytes_per_sync>[bytes]].",
|
||||
"fast:async:250000000bytes"};
|
||||
const command_line::arg_descriptor<bool> arg_db_salvage = {
|
||||
"db-salvage", "Try to salvage a blockchain database if it seems corrupted", false};
|
||||
|
||||
BlockchainDB *new_db()
|
||||
{
|
||||
return new BlockchainLMDB();
|
||||
BlockchainDB* new_db() {
|
||||
return new BlockchainLMDB();
|
||||
}
|
||||
|
||||
void BlockchainDB::init_options(boost::program_options::options_description& desc)
|
||||
{
|
||||
command_line::add_arg(desc, arg_db_sync_mode);
|
||||
command_line::add_arg(desc, arg_db_salvage);
|
||||
void BlockchainDB::init_options(boost::program_options::options_description& desc) {
|
||||
command_line::add_arg(desc, arg_db_sync_mode);
|
||||
command_line::add_arg(desc, arg_db_salvage);
|
||||
}
|
||||
|
||||
void BlockchainDB::pop_block()
|
||||
{
|
||||
block blk;
|
||||
std::vector<transaction> txs;
|
||||
pop_block(blk, txs);
|
||||
void BlockchainDB::pop_block() {
|
||||
block blk;
|
||||
std::vector<transaction> txs;
|
||||
pop_block(blk, txs);
|
||||
}
|
||||
|
||||
void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair<transaction, std::string>& txp, const crypto::hash* tx_hash_ptr, const crypto::hash* tx_prunable_hash_ptr)
|
||||
{
|
||||
const transaction &tx = txp.first;
|
||||
void BlockchainDB::add_transaction(
|
||||
const crypto::hash& blk_hash,
|
||||
const std::pair<transaction, std::string>& txp,
|
||||
const crypto::hash* tx_hash_ptr,
|
||||
const crypto::hash* tx_prunable_hash_ptr) {
|
||||
const transaction& tx = txp.first;
|
||||
|
||||
bool miner_tx = false;
|
||||
crypto::hash tx_hash, tx_prunable_hash;
|
||||
if (!tx_hash_ptr)
|
||||
{
|
||||
// should only need to compute hash for miner transactions
|
||||
tx_hash = get_transaction_hash(tx);
|
||||
log::trace(logcat, "null tx_hash_ptr - needed to compute: {}", tx_hash);
|
||||
}
|
||||
else
|
||||
{
|
||||
tx_hash = *tx_hash_ptr;
|
||||
}
|
||||
|
||||
bool has_blacklisted_outputs = false;
|
||||
if (tx.version >= cryptonote::txversion::v2_ringct)
|
||||
{
|
||||
if (!tx_prunable_hash_ptr)
|
||||
tx_prunable_hash = get_transaction_prunable_hash(tx, &txp.second);
|
||||
else
|
||||
tx_prunable_hash = *tx_prunable_hash_ptr;
|
||||
|
||||
crypto::secret_key secret_tx_key;
|
||||
cryptonote::account_public_address address;
|
||||
if (get_tx_secret_key_from_tx_extra(tx.extra, secret_tx_key) && get_service_node_contributor_from_tx_extra(tx.extra, address))
|
||||
has_blacklisted_outputs = true;
|
||||
}
|
||||
|
||||
for (const txin_v& tx_input : tx.vin)
|
||||
{
|
||||
if (std::holds_alternative<txin_to_key>(tx_input))
|
||||
{
|
||||
add_spent_key(var::get<txin_to_key>(tx_input).k_image);
|
||||
bool miner_tx = false;
|
||||
crypto::hash tx_hash, tx_prunable_hash;
|
||||
if (!tx_hash_ptr) {
|
||||
// should only need to compute hash for miner transactions
|
||||
tx_hash = get_transaction_hash(tx);
|
||||
log::trace(logcat, "null tx_hash_ptr - needed to compute: {}", tx_hash);
|
||||
} else {
|
||||
tx_hash = *tx_hash_ptr;
|
||||
}
|
||||
else if (std::holds_alternative<txin_gen>(tx_input))
|
||||
{
|
||||
/* nothing to do here */
|
||||
miner_tx = true;
|
||||
|
||||
bool has_blacklisted_outputs = false;
|
||||
if (tx.version >= cryptonote::txversion::v2_ringct) {
|
||||
if (!tx_prunable_hash_ptr)
|
||||
tx_prunable_hash = get_transaction_prunable_hash(tx, &txp.second);
|
||||
else
|
||||
tx_prunable_hash = *tx_prunable_hash_ptr;
|
||||
|
||||
crypto::secret_key secret_tx_key;
|
||||
cryptonote::account_public_address address;
|
||||
if (get_tx_secret_key_from_tx_extra(tx.extra, secret_tx_key) &&
|
||||
get_service_node_contributor_from_tx_extra(tx.extra, address))
|
||||
has_blacklisted_outputs = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
log::info(logcat, "Unsupported input type, removing key images and aborting transaction addition");
|
||||
for (const txin_v& tx_input : tx.vin)
|
||||
{
|
||||
if (std::holds_alternative<txin_to_key>(tx_input))
|
||||
{
|
||||
remove_spent_key(var::get<txin_to_key>(tx_input).k_image);
|
||||
|
||||
for (const txin_v& tx_input : tx.vin) {
|
||||
if (std::holds_alternative<txin_to_key>(tx_input)) {
|
||||
add_spent_key(var::get<txin_to_key>(tx_input).k_image);
|
||||
} else if (std::holds_alternative<txin_gen>(tx_input)) {
|
||||
/* nothing to do here */
|
||||
miner_tx = true;
|
||||
} else {
|
||||
log::info(
|
||||
logcat,
|
||||
"Unsupported input type, removing key images and aborting transaction "
|
||||
"addition");
|
||||
for (const txin_v& tx_input : tx.vin) {
|
||||
if (std::holds_alternative<txin_to_key>(tx_input)) {
|
||||
remove_spent_key(var::get<txin_to_key>(tx_input).k_image);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t tx_id = add_transaction_data(blk_hash, txp, tx_hash, tx_prunable_hash);
|
||||
|
||||
std::vector<uint64_t> amount_output_indices(tx.vout.size());
|
||||
|
||||
// iterate tx.vout using indices instead of C++11 foreach syntax because
|
||||
// we need the index
|
||||
for (uint64_t i = 0; i < tx.vout.size(); ++i)
|
||||
{
|
||||
uint64_t unlock_time = 0;
|
||||
if (tx.version >= cryptonote::txversion::v3_per_output_unlock_times)
|
||||
{
|
||||
unlock_time = tx.output_unlock_times[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
unlock_time = tx.unlock_time;
|
||||
}
|
||||
|
||||
// miner v2 txes have their coinbase output in one single out to save space,
|
||||
// and we store them as rct outputs with an identity mask
|
||||
if (miner_tx && tx.version >= cryptonote::txversion::v2_ringct)
|
||||
{
|
||||
cryptonote::tx_out vout = tx.vout[i];
|
||||
const rct::key commitment = rct::zeroCommit(vout.amount);
|
||||
vout.amount = 0;
|
||||
amount_output_indices[i] = add_output(tx_hash, vout, i, unlock_time,
|
||||
&commitment);
|
||||
uint64_t tx_id = add_transaction_data(blk_hash, txp, tx_hash, tx_prunable_hash);
|
||||
|
||||
std::vector<uint64_t> amount_output_indices(tx.vout.size());
|
||||
|
||||
// iterate tx.vout using indices instead of C++11 foreach syntax because
|
||||
// we need the index
|
||||
for (uint64_t i = 0; i < tx.vout.size(); ++i) {
|
||||
uint64_t unlock_time = 0;
|
||||
if (tx.version >= cryptonote::txversion::v3_per_output_unlock_times) {
|
||||
unlock_time = tx.output_unlock_times[i];
|
||||
} else {
|
||||
unlock_time = tx.unlock_time;
|
||||
}
|
||||
|
||||
// miner v2 txes have their coinbase output in one single out to save space,
|
||||
// and we store them as rct outputs with an identity mask
|
||||
if (miner_tx && tx.version >= cryptonote::txversion::v2_ringct) {
|
||||
cryptonote::tx_out vout = tx.vout[i];
|
||||
const rct::key commitment = rct::zeroCommit(vout.amount);
|
||||
vout.amount = 0;
|
||||
amount_output_indices[i] = add_output(tx_hash, vout, i, unlock_time, &commitment);
|
||||
} else {
|
||||
amount_output_indices[i] = add_output(
|
||||
tx_hash,
|
||||
tx.vout[i],
|
||||
i,
|
||||
unlock_time,
|
||||
tx.version >= cryptonote::txversion::v2_ringct
|
||||
? &tx.rct_signatures.outPk[i].mask
|
||||
: NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
amount_output_indices[i] = add_output(tx_hash, tx.vout[i], i, unlock_time,
|
||||
tx.version >= cryptonote::txversion::v2_ringct ? &tx.rct_signatures.outPk[i].mask : NULL);
|
||||
|
||||
if (has_blacklisted_outputs)
|
||||
add_output_blacklist(amount_output_indices);
|
||||
|
||||
add_tx_amount_output_indices(tx_id, amount_output_indices);
|
||||
}
|
||||
|
||||
uint64_t BlockchainDB::add_block(
|
||||
const std::pair<block, std::string>& blck,
|
||||
size_t block_weight,
|
||||
uint64_t long_term_block_weight,
|
||||
const difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
const std::vector<std::pair<transaction, std::string>>& txs) {
|
||||
const block& blk = blck.first;
|
||||
|
||||
// sanity
|
||||
if (blk.tx_hashes.size() != txs.size())
|
||||
throw std::runtime_error("Inconsistent tx/hashes sizes");
|
||||
|
||||
auto started = std::chrono::steady_clock::now();
|
||||
crypto::hash blk_hash = get_block_hash(blk);
|
||||
time_blk_hash += std::chrono::steady_clock::now() - started;
|
||||
|
||||
uint64_t prev_height = height();
|
||||
|
||||
// call out to add the transactions
|
||||
|
||||
started = std::chrono::steady_clock::now();
|
||||
|
||||
uint64_t num_rct_outs = 0;
|
||||
add_transaction(blk_hash, std::make_pair(blk.miner_tx, tx_to_blob(blk.miner_tx)));
|
||||
if (blk.miner_tx.version >= cryptonote::txversion::v2_ringct)
|
||||
num_rct_outs += blk.miner_tx.vout.size();
|
||||
|
||||
int tx_i = 0;
|
||||
crypto::hash tx_hash{};
|
||||
for (const std::pair<transaction, std::string>& tx : txs) {
|
||||
tx_hash = blk.tx_hashes[tx_i];
|
||||
add_transaction(blk_hash, tx, &tx_hash);
|
||||
for (const auto& vout : tx.first.vout) {
|
||||
if (vout.amount == 0)
|
||||
++num_rct_outs;
|
||||
}
|
||||
++tx_i;
|
||||
}
|
||||
}
|
||||
time_add_transaction += std::chrono::steady_clock::now() - started;
|
||||
|
||||
if (has_blacklisted_outputs)
|
||||
add_output_blacklist(amount_output_indices);
|
||||
// call out to subclass implementation to add the block & metadata
|
||||
started = std::chrono::steady_clock::now();
|
||||
add_block(
|
||||
blk,
|
||||
block_weight,
|
||||
long_term_block_weight,
|
||||
cumulative_difficulty,
|
||||
coins_generated,
|
||||
num_rct_outs,
|
||||
blk_hash);
|
||||
time_add_block1 += std::chrono::steady_clock::now() - started;
|
||||
|
||||
add_tx_amount_output_indices(tx_id, amount_output_indices);
|
||||
++num_calls;
|
||||
|
||||
return prev_height;
|
||||
}
|
||||
|
||||
uint64_t BlockchainDB::add_block( const std::pair<block, std::string>& blck
|
||||
, size_t block_weight
|
||||
, uint64_t long_term_block_weight
|
||||
, const difficulty_type& cumulative_difficulty
|
||||
, const uint64_t& coins_generated
|
||||
, const std::vector<std::pair<transaction, std::string>>& txs
|
||||
)
|
||||
{
|
||||
const block &blk = blck.first;
|
||||
void BlockchainDB::pop_block(block& blk, std::vector<transaction>& txs) {
|
||||
blk = get_top_block();
|
||||
|
||||
// sanity
|
||||
if (blk.tx_hashes.size() != txs.size())
|
||||
throw std::runtime_error("Inconsistent tx/hashes sizes");
|
||||
remove_block();
|
||||
|
||||
auto started = std::chrono::steady_clock::now();
|
||||
crypto::hash blk_hash = get_block_hash(blk);
|
||||
time_blk_hash += std::chrono::steady_clock::now() - started;
|
||||
|
||||
uint64_t prev_height = height();
|
||||
|
||||
// call out to add the transactions
|
||||
|
||||
started = std::chrono::steady_clock::now();
|
||||
|
||||
uint64_t num_rct_outs = 0;
|
||||
add_transaction(blk_hash, std::make_pair(blk.miner_tx, tx_to_blob(blk.miner_tx)));
|
||||
if (blk.miner_tx.version >= cryptonote::txversion::v2_ringct)
|
||||
num_rct_outs += blk.miner_tx.vout.size();
|
||||
|
||||
int tx_i = 0;
|
||||
crypto::hash tx_hash{};
|
||||
for (const std::pair<transaction, std::string>& tx : txs)
|
||||
{
|
||||
tx_hash = blk.tx_hashes[tx_i];
|
||||
add_transaction(blk_hash, tx, &tx_hash);
|
||||
for (const auto &vout: tx.first.vout)
|
||||
{
|
||||
if (vout.amount == 0)
|
||||
++num_rct_outs;
|
||||
for (auto it = blk.tx_hashes.rbegin(); it != blk.tx_hashes.rend(); ++it) {
|
||||
auto& h = *it;
|
||||
cryptonote::transaction tx;
|
||||
if (!get_tx(h, tx) && !get_pruned_tx(h, tx))
|
||||
throw DB_ERROR("Failed to get pruned or unpruned transaction from the db");
|
||||
txs.push_back(std::move(tx));
|
||||
remove_transaction(h);
|
||||
}
|
||||
++tx_i;
|
||||
}
|
||||
time_add_transaction += std::chrono::steady_clock::now() - started;
|
||||
|
||||
// call out to subclass implementation to add the block & metadata
|
||||
started = std::chrono::steady_clock::now();
|
||||
add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash);
|
||||
time_add_block1 += std::chrono::steady_clock::now() - started;
|
||||
|
||||
++num_calls;
|
||||
|
||||
return prev_height;
|
||||
remove_transaction(get_transaction_hash(blk.miner_tx));
|
||||
}
|
||||
|
||||
void BlockchainDB::pop_block(block& blk, std::vector<transaction>& txs)
|
||||
{
|
||||
blk = get_top_block();
|
||||
void BlockchainDB::remove_transaction(const crypto::hash& tx_hash) {
|
||||
transaction tx = get_pruned_tx(tx_hash);
|
||||
|
||||
remove_block();
|
||||
|
||||
for (auto it = blk.tx_hashes.rbegin(); it != blk.tx_hashes.rend(); ++it)
|
||||
{
|
||||
auto& h = *it;
|
||||
cryptonote::transaction tx;
|
||||
if (!get_tx(h, tx) && !get_pruned_tx(h, tx))
|
||||
throw DB_ERROR("Failed to get pruned or unpruned transaction from the db");
|
||||
txs.push_back(std::move(tx));
|
||||
remove_transaction(h);
|
||||
}
|
||||
remove_transaction(get_transaction_hash(blk.miner_tx));
|
||||
}
|
||||
|
||||
void BlockchainDB::remove_transaction(const crypto::hash& tx_hash)
|
||||
{
|
||||
transaction tx = get_pruned_tx(tx_hash);
|
||||
|
||||
for (const txin_v& tx_input : tx.vin)
|
||||
{
|
||||
if (std::holds_alternative<txin_to_key>(tx_input))
|
||||
{
|
||||
remove_spent_key(var::get<txin_to_key>(tx_input).k_image);
|
||||
for (const txin_v& tx_input : tx.vin) {
|
||||
if (std::holds_alternative<txin_to_key>(tx_input)) {
|
||||
remove_spent_key(var::get<txin_to_key>(tx_input).k_image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// need tx as tx.vout has the tx outputs, and the output amounts are needed
|
||||
remove_transaction_data(tx_hash, tx);
|
||||
// need tx as tx.vout has the tx outputs, and the output amounts are needed
|
||||
remove_transaction_data(tx_hash, tx);
|
||||
}
|
||||
|
||||
block_header BlockchainDB::get_block_header(const crypto::hash& h) const
|
||||
{
|
||||
block_header b = get_block_header_from_height(get_block_height(h));
|
||||
return b;
|
||||
block_header BlockchainDB::get_block_header(const crypto::hash& h) const {
|
||||
block_header b = get_block_header_from_height(get_block_height(h));
|
||||
return b;
|
||||
}
|
||||
|
||||
block BlockchainDB::get_block(const crypto::hash& h) const
|
||||
{
|
||||
block b = get_block_from_height(get_block_height(h));
|
||||
return b;
|
||||
block BlockchainDB::get_block(const crypto::hash& h) const {
|
||||
block b = get_block_from_height(get_block_height(h));
|
||||
return b;
|
||||
}
|
||||
|
||||
bool BlockchainDB::get_tx(const crypto::hash& h, cryptonote::transaction &tx) const
|
||||
{
|
||||
std::string bd;
|
||||
if (!get_tx_blob(h, bd))
|
||||
return false;
|
||||
if (!parse_and_validate_tx_from_blob(bd, tx))
|
||||
throw DB_ERROR("Failed to parse transaction from blob retrieved from the db");
|
||||
bool BlockchainDB::get_tx(const crypto::hash& h, cryptonote::transaction& tx) const {
|
||||
std::string bd;
|
||||
if (!get_tx_blob(h, bd))
|
||||
return false;
|
||||
if (!parse_and_validate_tx_from_blob(bd, tx))
|
||||
throw DB_ERROR("Failed to parse transaction from blob retrieved from the db");
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlockchainDB::get_pruned_tx(const crypto::hash& h, cryptonote::transaction &tx) const
|
||||
{
|
||||
std::string bd;
|
||||
if (!get_pruned_tx_blob(h, bd))
|
||||
return false;
|
||||
if (!parse_and_validate_tx_base_from_blob(bd, tx))
|
||||
{
|
||||
throw DB_ERROR("Failed to parse transaction base from blob retrieved from the db");
|
||||
}
|
||||
bool BlockchainDB::get_pruned_tx(const crypto::hash& h, cryptonote::transaction& tx) const {
|
||||
std::string bd;
|
||||
if (!get_pruned_tx_blob(h, bd))
|
||||
return false;
|
||||
if (!parse_and_validate_tx_base_from_blob(bd, tx)) {
|
||||
throw DB_ERROR("Failed to parse transaction base from blob retrieved from the db");
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
transaction BlockchainDB::get_tx(const crypto::hash& h) const
|
||||
{
|
||||
transaction tx;
|
||||
if (!get_tx(h, tx))
|
||||
throw TX_DNE("tx with hash " + tools::type_to_hex(h) + " not found in db");
|
||||
return tx;
|
||||
transaction BlockchainDB::get_tx(const crypto::hash& h) const {
|
||||
transaction tx;
|
||||
if (!get_tx(h, tx))
|
||||
throw TX_DNE("tx with hash " + tools::type_to_hex(h) + " not found in db");
|
||||
return tx;
|
||||
}
|
||||
|
||||
uint64_t BlockchainDB::get_output_unlock_time(const uint64_t amount, const uint64_t amount_index) const
|
||||
{
|
||||
output_data_t odata = get_output_key(amount, amount_index);
|
||||
return odata.unlock_time;
|
||||
uint64_t BlockchainDB::get_output_unlock_time(
|
||||
const uint64_t amount, const uint64_t amount_index) const {
|
||||
output_data_t odata = get_output_key(amount, amount_index);
|
||||
return odata.unlock_time;
|
||||
}
|
||||
|
||||
transaction BlockchainDB::get_pruned_tx(const crypto::hash& h) const
|
||||
{
|
||||
transaction tx;
|
||||
if (!get_pruned_tx(h, tx))
|
||||
throw TX_DNE("pruned tx with hash " + tools::type_to_hex(h) + " not found in db");
|
||||
return tx;
|
||||
transaction BlockchainDB::get_pruned_tx(const crypto::hash& h) const {
|
||||
transaction tx;
|
||||
if (!get_pruned_tx(h, tx))
|
||||
throw TX_DNE("pruned tx with hash " + tools::type_to_hex(h) + " not found in db");
|
||||
return tx;
|
||||
}
|
||||
|
||||
void BlockchainDB::reset_stats()
|
||||
{
|
||||
num_calls = 0;
|
||||
time_blk_hash = 0ns;
|
||||
time_tx_exists = 0ns;
|
||||
time_add_block1 = 0ns;
|
||||
time_add_transaction = 0ns;
|
||||
time_commit1 = 0ns;
|
||||
void BlockchainDB::reset_stats() {
|
||||
num_calls = 0;
|
||||
time_blk_hash = 0ns;
|
||||
time_tx_exists = 0ns;
|
||||
time_add_block1 = 0ns;
|
||||
time_add_transaction = 0ns;
|
||||
time_commit1 = 0ns;
|
||||
}
|
||||
|
||||
void BlockchainDB::show_stats()
|
||||
{
|
||||
log::info(logcat, "\n*********************************\n \
|
||||
void BlockchainDB::show_stats() {
|
||||
log::info(
|
||||
logcat,
|
||||
"\n*********************************\n \
|
||||
num_calls: {}\n \
|
||||
time_blk_hash: {}\n \
|
||||
time_tx_exists: {}\n \
|
||||
|
@ -337,134 +316,133 @@ void BlockchainDB::show_stats()
|
|||
time_add_transaction: {}\n \
|
||||
time_commit1: {}\n \
|
||||
*********************************\n",
|
||||
num_calls, tools::friendly_duration(time_blk_hash), tools::friendly_duration(time_tx_exists), tools::friendly_duration(time_add_block1), tools::friendly_duration(time_add_transaction), tools::friendly_duration(time_commit1));
|
||||
num_calls,
|
||||
tools::friendly_duration(time_blk_hash),
|
||||
tools::friendly_duration(time_tx_exists),
|
||||
tools::friendly_duration(time_add_block1),
|
||||
tools::friendly_duration(time_add_transaction),
|
||||
tools::friendly_duration(time_commit1));
|
||||
}
|
||||
|
||||
void BlockchainDB::fixup(cryptonote::network_type)
|
||||
{
|
||||
if (is_read_only()) {
|
||||
log::info(logcat, "Database is opened read only - skipping fixup check");
|
||||
return;
|
||||
}
|
||||
|
||||
set_batch_transactions(true);
|
||||
}
|
||||
|
||||
bool BlockchainDB::get_immutable_checkpoint(checkpoint_t *immutable_checkpoint, uint64_t block_height) const
|
||||
{
|
||||
size_t constexpr NUM_CHECKPOINTS = service_nodes::CHECKPOINT_NUM_CHECKPOINTS_FOR_CHAIN_FINALITY;
|
||||
static_assert(NUM_CHECKPOINTS == 2,
|
||||
"Expect checkpoint finality to be 2, otherwise the immutable logic needs to check for any hardcoded "
|
||||
"checkpoints inbetween");
|
||||
|
||||
std::vector<checkpoint_t> checkpoints = get_checkpoints_range(block_height, 0, NUM_CHECKPOINTS);
|
||||
|
||||
if (checkpoints.empty())
|
||||
return false;
|
||||
|
||||
checkpoint_t *checkpoint_ptr = nullptr;
|
||||
if (checkpoints[0].type != checkpoint_type::service_node) // checkpoint[0] is the first closest checkpoint that is <= my height
|
||||
{
|
||||
checkpoint_ptr = &checkpoints[0]; // Must be hard-coded then, always immutable
|
||||
}
|
||||
else if (checkpoints.size() == NUM_CHECKPOINTS)
|
||||
{
|
||||
// NOTE: The first checkpoint is a service node checkpoint. Go back
|
||||
// 1 checkpoint, which will either be another service node checkpoint or
|
||||
// a predefined one.
|
||||
checkpoint_ptr = &checkpoints[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
return false; // NOTE: Only one service node checkpoint recorded, we can override this checkpoint.
|
||||
}
|
||||
|
||||
if (immutable_checkpoint)
|
||||
*immutable_checkpoint = std::move(*checkpoint_ptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t BlockchainDB::get_tx_block_height(const crypto::hash &h) const
|
||||
{
|
||||
auto result = get_tx_block_heights({{h}}).front();
|
||||
if (result == std::numeric_limits<uint64_t>::max())
|
||||
{
|
||||
std::string err = "tx_data_t with hash " + tools::type_to_hex(h) + " not found in db";
|
||||
log::info(logcat, "{}", err);
|
||||
throw TX_DNE(std::move(err));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BlockchainDB::get_alt_block_header(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::block_header *header, std::string *checkpoint) const
|
||||
{
|
||||
std::string blob;
|
||||
if (!get_alt_block(blkid, data, &blob, checkpoint))
|
||||
{
|
||||
throw BLOCK_DNE("Alt-block with hash " + tools::type_to_hex(blkid) + " not found in db");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
serialization::binary_string_unarchiver ba{blob};
|
||||
serialization::value(ba, *header);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BlockchainDB::fill_timestamps_and_difficulties_for_pow(cryptonote::network_type nettype,
|
||||
std::vector<uint64_t> ×tamps,
|
||||
std::vector<uint64_t> &difficulties,
|
||||
uint64_t chain_height,
|
||||
uint64_t timestamps_difficulty_height) const
|
||||
{
|
||||
constexpr uint64_t MIN_CHAIN_HEIGHT = 2;
|
||||
if (chain_height < MIN_CHAIN_HEIGHT)
|
||||
return;
|
||||
|
||||
uint64_t const top_block_height = chain_height - 1;
|
||||
bool const before_hf16 = !is_hard_fork_at_least(nettype, hf::hf16_pulse, chain_height);
|
||||
uint64_t const block_count = old::DIFFICULTY_BLOCKS_COUNT(before_hf16);
|
||||
|
||||
timestamps.reserve(block_count);
|
||||
difficulties.reserve(block_count);
|
||||
|
||||
if (timestamps_difficulty_height == 0 ||
|
||||
(chain_height - timestamps_difficulty_height) != 1 ||
|
||||
timestamps.size() > block_count ||
|
||||
difficulties.size() > block_count)
|
||||
{
|
||||
// Cache invalidated.
|
||||
timestamps.clear();
|
||||
difficulties.clear();
|
||||
|
||||
// Fill missing timestamps/difficulties, up to one before the latest (latest is added below).
|
||||
uint64_t start_height = chain_height - std::min<size_t>(chain_height, block_count);
|
||||
start_height = std::max<uint64_t>(start_height, 1);
|
||||
|
||||
for (uint64_t block_height = start_height; block_height < (chain_height - 1) /*skip latest block*/; block_height++)
|
||||
{
|
||||
timestamps.push_back(get_block_timestamp(block_height));
|
||||
difficulties.push_back(get_block_cumulative_difficulty(block_height));
|
||||
void BlockchainDB::fixup(cryptonote::network_type) {
|
||||
if (is_read_only()) {
|
||||
log::info(logcat, "Database is opened read only - skipping fixup check");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add latest timestamp/difficulty
|
||||
add_timestamp_and_difficulty(nettype,
|
||||
chain_height,
|
||||
timestamps,
|
||||
difficulties,
|
||||
get_block_timestamp(top_block_height),
|
||||
get_block_cumulative_difficulty(top_block_height));
|
||||
|
||||
set_batch_transactions(true);
|
||||
}
|
||||
|
||||
bool BlockchainDB::get_immutable_checkpoint(
|
||||
checkpoint_t* immutable_checkpoint, uint64_t block_height) const {
|
||||
size_t constexpr NUM_CHECKPOINTS = service_nodes::CHECKPOINT_NUM_CHECKPOINTS_FOR_CHAIN_FINALITY;
|
||||
static_assert(
|
||||
NUM_CHECKPOINTS == 2,
|
||||
"Expect checkpoint finality to be 2, otherwise the immutable logic needs to check for "
|
||||
"any hardcoded "
|
||||
"checkpoints inbetween");
|
||||
|
||||
std::vector<checkpoint_t> checkpoints = get_checkpoints_range(block_height, 0, NUM_CHECKPOINTS);
|
||||
|
||||
if (checkpoints.empty())
|
||||
return false;
|
||||
|
||||
checkpoint_t* checkpoint_ptr = nullptr;
|
||||
if (checkpoints[0].type != checkpoint_type::service_node) // checkpoint[0] is the first closest
|
||||
// checkpoint that is <= my height
|
||||
{
|
||||
checkpoint_ptr = &checkpoints[0]; // Must be hard-coded then, always immutable
|
||||
} else if (checkpoints.size() == NUM_CHECKPOINTS) {
|
||||
// NOTE: The first checkpoint is a service node checkpoint. Go back
|
||||
// 1 checkpoint, which will either be another service node checkpoint or
|
||||
// a predefined one.
|
||||
checkpoint_ptr = &checkpoints[1];
|
||||
} else {
|
||||
return false; // NOTE: Only one service node checkpoint recorded, we can override this
|
||||
// checkpoint.
|
||||
}
|
||||
|
||||
if (immutable_checkpoint)
|
||||
*immutable_checkpoint = std::move(*checkpoint_ptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t BlockchainDB::get_tx_block_height(const crypto::hash& h) const {
|
||||
auto result = get_tx_block_heights({{h}}).front();
|
||||
if (result == std::numeric_limits<uint64_t>::max()) {
|
||||
std::string err = "tx_data_t with hash " + tools::type_to_hex(h) + " not found in db";
|
||||
log::info(logcat, "{}", err);
|
||||
throw TX_DNE(std::move(err));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BlockchainDB::get_alt_block_header(
|
||||
const crypto::hash& blkid,
|
||||
alt_block_data_t* data,
|
||||
cryptonote::block_header* header,
|
||||
std::string* checkpoint) const {
|
||||
std::string blob;
|
||||
if (!get_alt_block(blkid, data, &blob, checkpoint)) {
|
||||
throw BLOCK_DNE("Alt-block with hash " + tools::type_to_hex(blkid) + " not found in db");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
serialization::binary_string_unarchiver ba{blob};
|
||||
serialization::value(ba, *header);
|
||||
} catch (std::exception& e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BlockchainDB::fill_timestamps_and_difficulties_for_pow(
|
||||
cryptonote::network_type nettype,
|
||||
std::vector<uint64_t>& timestamps,
|
||||
std::vector<uint64_t>& difficulties,
|
||||
uint64_t chain_height,
|
||||
uint64_t timestamps_difficulty_height) const {
|
||||
constexpr uint64_t MIN_CHAIN_HEIGHT = 2;
|
||||
if (chain_height < MIN_CHAIN_HEIGHT)
|
||||
return;
|
||||
|
||||
uint64_t const top_block_height = chain_height - 1;
|
||||
bool const before_hf16 = !is_hard_fork_at_least(nettype, hf::hf16_pulse, chain_height);
|
||||
uint64_t const block_count = old::DIFFICULTY_BLOCKS_COUNT(before_hf16);
|
||||
|
||||
timestamps.reserve(block_count);
|
||||
difficulties.reserve(block_count);
|
||||
|
||||
if (timestamps_difficulty_height == 0 || (chain_height - timestamps_difficulty_height) != 1 ||
|
||||
timestamps.size() > block_count || difficulties.size() > block_count) {
|
||||
// Cache invalidated.
|
||||
timestamps.clear();
|
||||
difficulties.clear();
|
||||
|
||||
// Fill missing timestamps/difficulties, up to one before the latest (latest is added
|
||||
// below).
|
||||
uint64_t start_height = chain_height - std::min<size_t>(chain_height, block_count);
|
||||
start_height = std::max<uint64_t>(start_height, 1);
|
||||
|
||||
for (uint64_t block_height = start_height;
|
||||
block_height < (chain_height - 1) /*skip latest block*/;
|
||||
block_height++) {
|
||||
timestamps.push_back(get_block_timestamp(block_height));
|
||||
difficulties.push_back(get_block_cumulative_difficulty(block_height));
|
||||
}
|
||||
}
|
||||
|
||||
// Add latest timestamp/difficulty
|
||||
add_timestamp_and_difficulty(
|
||||
nettype,
|
||||
chain_height,
|
||||
timestamps,
|
||||
difficulties,
|
||||
get_block_timestamp(top_block_height),
|
||||
get_block_cumulative_difficulty(top_block_height));
|
||||
}
|
||||
|
||||
} // namespace cryptonote
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -26,20 +26,19 @@
|
|||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <lmdb.h>
|
||||
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
#include "common/fs.h"
|
||||
#include <atomic>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/thread/tss.hpp>
|
||||
|
||||
#include <lmdb.h>
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "common/fs.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
|
||||
#define ENABLE_AUTO_RESIZE
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
namespace cryptonote {
|
||||
|
||||
struct mdb_block_info;
|
||||
|
||||
|
@ -48,116 +47,105 @@ struct txindex {
|
|||
tx_data_t data;
|
||||
};
|
||||
|
||||
struct mdb_txn_cursors
|
||||
{
|
||||
MDB_cursor *blocks;
|
||||
MDB_cursor *block_heights;
|
||||
MDB_cursor *block_info;
|
||||
MDB_cursor *block_checkpoints;
|
||||
struct mdb_txn_cursors {
|
||||
MDB_cursor* blocks;
|
||||
MDB_cursor* block_heights;
|
||||
MDB_cursor* block_info;
|
||||
MDB_cursor* block_checkpoints;
|
||||
|
||||
MDB_cursor *output_txs;
|
||||
MDB_cursor *output_amounts;
|
||||
MDB_cursor* output_txs;
|
||||
MDB_cursor* output_amounts;
|
||||
|
||||
MDB_cursor *txs;
|
||||
MDB_cursor *txs_pruned;
|
||||
MDB_cursor *txs_prunable;
|
||||
MDB_cursor *txs_prunable_hash;
|
||||
MDB_cursor *txs_prunable_tip;
|
||||
MDB_cursor *tx_indices;
|
||||
MDB_cursor *tx_outputs;
|
||||
MDB_cursor* txs;
|
||||
MDB_cursor* txs_pruned;
|
||||
MDB_cursor* txs_prunable;
|
||||
MDB_cursor* txs_prunable_hash;
|
||||
MDB_cursor* txs_prunable_tip;
|
||||
MDB_cursor* tx_indices;
|
||||
MDB_cursor* tx_outputs;
|
||||
|
||||
MDB_cursor *spent_keys;
|
||||
MDB_cursor* spent_keys;
|
||||
|
||||
MDB_cursor *txpool_meta;
|
||||
MDB_cursor *txpool_blob;
|
||||
MDB_cursor* txpool_meta;
|
||||
MDB_cursor* txpool_blob;
|
||||
|
||||
MDB_cursor *alt_blocks;
|
||||
MDB_cursor* alt_blocks;
|
||||
|
||||
MDB_cursor *hf_versions;
|
||||
MDB_cursor* hf_versions;
|
||||
|
||||
MDB_cursor *service_node_data;
|
||||
MDB_cursor *service_node_proofs;
|
||||
MDB_cursor *output_blacklist;
|
||||
MDB_cursor *properties;
|
||||
MDB_cursor* service_node_data;
|
||||
MDB_cursor* service_node_proofs;
|
||||
MDB_cursor* output_blacklist;
|
||||
MDB_cursor* properties;
|
||||
};
|
||||
|
||||
struct mdb_rflags
|
||||
{
|
||||
bool m_rf_txn;
|
||||
bool m_rf_blocks;
|
||||
bool m_rf_block_heights;
|
||||
bool m_rf_block_info;
|
||||
bool m_rf_block_checkpoints;
|
||||
bool m_rf_output_txs;
|
||||
bool m_rf_output_amounts;
|
||||
bool m_rf_output_blacklist;
|
||||
bool m_rf_txs;
|
||||
bool m_rf_txs_pruned;
|
||||
bool m_rf_txs_prunable;
|
||||
bool m_rf_txs_prunable_hash;
|
||||
bool m_rf_txs_prunable_tip;
|
||||
bool m_rf_tx_indices;
|
||||
bool m_rf_tx_outputs;
|
||||
bool m_rf_spent_keys;
|
||||
bool m_rf_txpool_meta;
|
||||
bool m_rf_txpool_blob;
|
||||
bool m_rf_alt_blocks;
|
||||
bool m_rf_hf_versions;
|
||||
bool m_rf_service_node_data;
|
||||
bool m_rf_service_node_proofs;
|
||||
bool m_rf_properties;
|
||||
struct mdb_rflags {
|
||||
bool m_rf_txn;
|
||||
bool m_rf_blocks;
|
||||
bool m_rf_block_heights;
|
||||
bool m_rf_block_info;
|
||||
bool m_rf_block_checkpoints;
|
||||
bool m_rf_output_txs;
|
||||
bool m_rf_output_amounts;
|
||||
bool m_rf_output_blacklist;
|
||||
bool m_rf_txs;
|
||||
bool m_rf_txs_pruned;
|
||||
bool m_rf_txs_prunable;
|
||||
bool m_rf_txs_prunable_hash;
|
||||
bool m_rf_txs_prunable_tip;
|
||||
bool m_rf_tx_indices;
|
||||
bool m_rf_tx_outputs;
|
||||
bool m_rf_spent_keys;
|
||||
bool m_rf_txpool_meta;
|
||||
bool m_rf_txpool_blob;
|
||||
bool m_rf_alt_blocks;
|
||||
bool m_rf_hf_versions;
|
||||
bool m_rf_service_node_data;
|
||||
bool m_rf_service_node_proofs;
|
||||
bool m_rf_properties;
|
||||
};
|
||||
|
||||
struct mdb_threadinfo
|
||||
{
|
||||
MDB_txn *m_ti_rtxn; // per-thread read txn
|
||||
mdb_txn_cursors m_ti_rcursors; // per-thread read cursors
|
||||
mdb_rflags m_ti_rflags; // per-thread read state
|
||||
struct mdb_threadinfo {
|
||||
MDB_txn* m_ti_rtxn; // per-thread read txn
|
||||
mdb_txn_cursors m_ti_rcursors; // per-thread read cursors
|
||||
mdb_rflags m_ti_rflags; // per-thread read state
|
||||
|
||||
~mdb_threadinfo();
|
||||
~mdb_threadinfo();
|
||||
};
|
||||
|
||||
struct mdb_txn_safe
|
||||
{
|
||||
mdb_txn_safe(const bool check=true);
|
||||
~mdb_txn_safe();
|
||||
struct mdb_txn_safe {
|
||||
mdb_txn_safe(const bool check = true);
|
||||
~mdb_txn_safe();
|
||||
|
||||
void commit(std::string message = "");
|
||||
void commit(std::string message = "");
|
||||
|
||||
// This should only be needed for batch transaction which must be ensured to
|
||||
// be aborted before mdb_env_close, not after. So we can't rely on
|
||||
// BlockchainLMDB destructor to call mdb_txn_safe destructor, as that's too late
|
||||
// to properly abort, since mdb_env_close would have been called earlier.
|
||||
void abort();
|
||||
void uncheck();
|
||||
// This should only be needed for batch transaction which must be ensured to
|
||||
// be aborted before mdb_env_close, not after. So we can't rely on
|
||||
// BlockchainLMDB destructor to call mdb_txn_safe destructor, as that's too late
|
||||
// to properly abort, since mdb_env_close would have been called earlier.
|
||||
void abort();
|
||||
void uncheck();
|
||||
|
||||
operator MDB_txn*()
|
||||
{
|
||||
return m_txn;
|
||||
}
|
||||
operator MDB_txn*() { return m_txn; }
|
||||
|
||||
operator MDB_txn**()
|
||||
{
|
||||
return &m_txn;
|
||||
}
|
||||
operator MDB_txn**() { return &m_txn; }
|
||||
|
||||
uint64_t num_active_tx() const;
|
||||
uint64_t num_active_tx() const;
|
||||
|
||||
static void prevent_new_txns();
|
||||
static void wait_no_active_txns();
|
||||
static void allow_new_txns();
|
||||
static void prevent_new_txns();
|
||||
static void wait_no_active_txns();
|
||||
static void allow_new_txns();
|
||||
|
||||
mdb_threadinfo* m_tinfo;
|
||||
MDB_txn* m_txn;
|
||||
bool m_batch_txn = false;
|
||||
bool m_check;
|
||||
static std::atomic<uint64_t> num_active_txns;
|
||||
mdb_threadinfo* m_tinfo;
|
||||
MDB_txn* m_txn;
|
||||
bool m_batch_txn = false;
|
||||
bool m_check;
|
||||
static std::atomic<uint64_t> num_active_txns;
|
||||
|
||||
// could use a mutex here, but this should be sufficient.
|
||||
static std::atomic_flag creation_gate;
|
||||
// could use a mutex here, but this should be sufficient.
|
||||
static std::atomic_flag creation_gate;
|
||||
};
|
||||
|
||||
|
||||
// If m_batch_active is set, a batch transaction exists beyond this class, such
|
||||
// as a batch import with verification enabled, or possibly (later) a batch
|
||||
// network sync.
|
||||
|
@ -171,345 +159,415 @@ struct mdb_txn_safe
|
|||
// A regular network sync without batch writes is expected to open a new read
|
||||
// transaction, as those lookups are part of the validation done prior to the
|
||||
// write for block and tx data, so no write transaction is open at the time.
|
||||
class BlockchainLMDB : public BlockchainDB
|
||||
{
|
||||
public:
|
||||
BlockchainLMDB(bool batch_transactions=true);
|
||||
~BlockchainLMDB();
|
||||
class BlockchainLMDB : public BlockchainDB {
|
||||
public:
|
||||
BlockchainLMDB(bool batch_transactions = true);
|
||||
~BlockchainLMDB();
|
||||
|
||||
void open(const fs::path& filename, cryptonote::network_type nettype, const int mdb_flags=0) override;
|
||||
void open(const fs::path& filename, cryptonote::network_type nettype, const int mdb_flags = 0)
|
||||
override;
|
||||
|
||||
void close() override;
|
||||
void close() override;
|
||||
|
||||
void sync() override;
|
||||
void sync() override;
|
||||
|
||||
void safesyncmode(const bool onoff) override;
|
||||
void safesyncmode(const bool onoff) override;
|
||||
|
||||
void reset() override;
|
||||
void reset() override;
|
||||
|
||||
std::vector<fs::path> get_filenames() const override;
|
||||
std::vector<fs::path> get_filenames() const override;
|
||||
|
||||
bool remove_data_file(const fs::path& folder) const override;
|
||||
bool remove_data_file(const fs::path& folder) const override;
|
||||
|
||||
std::string get_db_name() const override;
|
||||
std::string get_db_name() const override;
|
||||
|
||||
void lock() override;
|
||||
void lock() override;
|
||||
|
||||
bool try_lock() override;
|
||||
bool try_lock() override;
|
||||
|
||||
void unlock() override;
|
||||
void unlock() override;
|
||||
|
||||
bool block_exists(const crypto::hash& h, uint64_t *height = NULL) const override;
|
||||
bool block_exists(const crypto::hash& h, uint64_t* height = NULL) const override;
|
||||
|
||||
uint64_t get_block_height(const crypto::hash& h) const override;
|
||||
uint64_t get_block_height(const crypto::hash& h) const override;
|
||||
|
||||
block get_block_from_height(uint64_t height) const override;
|
||||
block get_block_from_height(uint64_t height) const override;
|
||||
|
||||
block_header get_block_header_from_height(uint64_t height) const override;
|
||||
block_header get_block_header_from_height(uint64_t height) const override;
|
||||
|
||||
std::string get_block_blob(const crypto::hash& h) const override;
|
||||
std::string get_block_blob(const crypto::hash& h) const override;
|
||||
|
||||
std::string get_block_blob_from_height(uint64_t height) const override;
|
||||
std::string get_block_blob_from_height(uint64_t height) const override;
|
||||
|
||||
std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const override;
|
||||
std::vector<uint64_t> get_block_cumulative_rct_outputs(
|
||||
const std::vector<uint64_t>& heights) const override;
|
||||
|
||||
uint64_t get_block_timestamp(const uint64_t& height) const override;
|
||||
uint64_t get_block_timestamp(const uint64_t& height) const override;
|
||||
|
||||
uint64_t get_top_block_timestamp() const override;
|
||||
uint64_t get_top_block_timestamp() const override;
|
||||
|
||||
size_t get_block_weight(const uint64_t& height) const override;
|
||||
size_t get_block_weight(const uint64_t& height) const override;
|
||||
|
||||
std::vector<uint64_t> get_block_weights(uint64_t start_height, size_t count) const override;
|
||||
std::vector<uint64_t> get_block_weights(uint64_t start_height, size_t count) const override;
|
||||
|
||||
difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const override;
|
||||
difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const override;
|
||||
|
||||
difficulty_type get_block_difficulty(const uint64_t& height) const override;
|
||||
difficulty_type get_block_difficulty(const uint64_t& height) const override;
|
||||
|
||||
uint64_t get_block_already_generated_coins(const uint64_t& height) const override;
|
||||
uint64_t get_block_already_generated_coins(const uint64_t& height) const override;
|
||||
|
||||
uint64_t get_block_long_term_weight(const uint64_t& height) const override;
|
||||
uint64_t get_block_long_term_weight(const uint64_t& height) const override;
|
||||
|
||||
std::vector<uint64_t> get_long_term_block_weights(uint64_t start_height, size_t count) const override;
|
||||
std::vector<uint64_t> get_long_term_block_weights(
|
||||
uint64_t start_height, size_t count) const override;
|
||||
|
||||
crypto::hash get_block_hash_from_height(const uint64_t& height) const override;
|
||||
crypto::hash get_block_hash_from_height(const uint64_t& height) const override;
|
||||
|
||||
std::vector<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const override;
|
||||
std::vector<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const override;
|
||||
|
||||
std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const override;
|
||||
std::vector<crypto::hash> get_hashes_range(
|
||||
const uint64_t& h1, const uint64_t& h2) const override;
|
||||
|
||||
crypto::hash top_block_hash(uint64_t *block_height = NULL) const override;
|
||||
crypto::hash top_block_hash(uint64_t* block_height = NULL) const override;
|
||||
|
||||
block get_top_block() const override;
|
||||
block get_top_block() const override;
|
||||
|
||||
uint64_t height() const override;
|
||||
uint64_t height() const override;
|
||||
|
||||
bool tx_exists(const crypto::hash& h) const override;
|
||||
bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const override;
|
||||
bool tx_exists(const crypto::hash& h) const override;
|
||||
bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const override;
|
||||
|
||||
uint64_t get_tx_unlock_time(const crypto::hash& h) const override;
|
||||
uint64_t get_tx_unlock_time(const crypto::hash& h) const override;
|
||||
|
||||
bool get_tx_blob(const crypto::hash& h, std::string &tx) const override;
|
||||
bool get_pruned_tx_blob(const crypto::hash& h, std::string &tx) const override;
|
||||
bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<std::string> &bd) const override;
|
||||
bool get_prunable_tx_blob(const crypto::hash& h, std::string &tx) const override;
|
||||
bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const override;
|
||||
bool get_tx_blob(const crypto::hash& h, std::string& tx) const override;
|
||||
bool get_pruned_tx_blob(const crypto::hash& h, std::string& tx) const override;
|
||||
bool get_pruned_tx_blobs_from(
|
||||
const crypto::hash& h, size_t count, std::vector<std::string>& bd) const override;
|
||||
bool get_prunable_tx_blob(const crypto::hash& h, std::string& tx) const override;
|
||||
bool get_prunable_tx_hash(
|
||||
const crypto::hash& tx_hash, crypto::hash& prunable_hash) const override;
|
||||
|
||||
uint64_t get_tx_count() const override;
|
||||
uint64_t get_tx_count() const override;
|
||||
|
||||
std::vector<transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const override;
|
||||
std::vector<transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const override;
|
||||
|
||||
std::vector<uint64_t> get_tx_block_heights(const std::vector<crypto::hash>& hlist) const override;
|
||||
std::vector<uint64_t> get_tx_block_heights(
|
||||
const std::vector<crypto::hash>& hlist) const override;
|
||||
|
||||
uint64_t get_num_outputs(const uint64_t& amount) const override;
|
||||
uint64_t get_num_outputs(const uint64_t& amount) const override;
|
||||
|
||||
output_data_t get_output_key(const uint64_t& amount, const uint64_t& index, bool include_commitmemt) const override;
|
||||
void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) const override;
|
||||
output_data_t get_output_key(
|
||||
const uint64_t& amount, const uint64_t& index, bool include_commitmemt) const override;
|
||||
void get_output_key(
|
||||
const epee::span<const uint64_t>& amounts,
|
||||
const std::vector<uint64_t>& offsets,
|
||||
std::vector<output_data_t>& outputs,
|
||||
bool allow_partial = false) const override;
|
||||
|
||||
tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const override;
|
||||
void get_output_tx_and_index_from_global(const std::vector<uint64_t> &global_indices,
|
||||
std::vector<tx_out_index> &tx_out_indices) const;
|
||||
tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const override;
|
||||
void get_output_tx_and_index_from_global(
|
||||
const std::vector<uint64_t>& global_indices,
|
||||
std::vector<tx_out_index>& tx_out_indices) const;
|
||||
|
||||
tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const override;
|
||||
void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<tx_out_index> &indices) const override;
|
||||
tx_out_index get_output_tx_and_index(
|
||||
const uint64_t& amount, const uint64_t& index) const override;
|
||||
void get_output_tx_and_index(
|
||||
const uint64_t& amount,
|
||||
const std::vector<uint64_t>& offsets,
|
||||
std::vector<tx_out_index>& indices) const override;
|
||||
|
||||
std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(const uint64_t tx_id, size_t n_txes) const override;
|
||||
std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(
|
||||
const uint64_t tx_id, size_t n_txes) const override;
|
||||
|
||||
bool has_key_image(const crypto::key_image& img) const override;
|
||||
|
||||
void add_txpool_tx(
|
||||
const crypto::hash& txid,
|
||||
const std::string& blob,
|
||||
const txpool_tx_meta_t& meta) override;
|
||||
void update_txpool_tx(const crypto::hash& txid, const txpool_tx_meta_t& meta) override;
|
||||
uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const override;
|
||||
bool txpool_has_tx(const crypto::hash& txid) const override;
|
||||
void remove_txpool_tx(const crypto::hash& txid) override;
|
||||
bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t& meta) const override;
|
||||
bool get_txpool_tx_blob(const crypto::hash& txid, std::string& bd) const override;
|
||||
std::string get_txpool_tx_blob(const crypto::hash& txid) const override;
|
||||
uint32_t get_blockchain_pruning_seed() const override;
|
||||
bool prune_blockchain(uint32_t pruning_seed = 0) override;
|
||||
bool update_pruning() override;
|
||||
bool check_pruning() override;
|
||||
|
||||
void add_alt_block(
|
||||
const crypto::hash& blkid,
|
||||
const cryptonote::alt_block_data_t& data,
|
||||
const std::string& blob,
|
||||
const std::string* checkpoint) override;
|
||||
bool get_alt_block(
|
||||
const crypto::hash& blkid,
|
||||
alt_block_data_t* data,
|
||||
std::string* blob,
|
||||
std::string* checkpoint) const override;
|
||||
void remove_alt_block(const crypto::hash& blkid) override;
|
||||
uint64_t get_alt_block_count() override;
|
||||
void drop_alt_blocks() override;
|
||||
|
||||
bool for_all_txpool_txes(
|
||||
std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const std::string*)> f,
|
||||
bool include_blob = false,
|
||||
bool include_unrelayed_txes = true) const override;
|
||||
|
||||
bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const override;
|
||||
bool for_blocks_range(
|
||||
const uint64_t& h1,
|
||||
const uint64_t& h2,
|
||||
std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>)
|
||||
const override;
|
||||
bool for_all_transactions(
|
||||
std::function<bool(const crypto::hash&, const cryptonote::transaction&)>,
|
||||
bool pruned) const override;
|
||||
bool for_all_outputs(
|
||||
std::function<bool(
|
||||
uint64_t amount, const crypto::hash& tx_hash, uint64_t height, size_t tx_idx)>
|
||||
f) const override;
|
||||
bool for_all_outputs(
|
||||
uint64_t amount, const std::function<bool(uint64_t height)>& f) const override;
|
||||
bool for_all_alt_blocks(
|
||||
std::function<
|
||||
bool(const crypto::hash& blkid,
|
||||
const alt_block_data_t& data,
|
||||
const std::string* block_blob,
|
||||
const std::string* checkpoint_blob)> f,
|
||||
bool include_blob = false) const override;
|
||||
|
||||
uint64_t add_block(
|
||||
const std::pair<block, std::string>& blk,
|
||||
size_t block_weight,
|
||||
uint64_t long_term_block_weight,
|
||||
const difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
const std::vector<std::pair<transaction, std::string>>& txs) override;
|
||||
void update_block_checkpoint(checkpoint_t const& checkpoint) override;
|
||||
void remove_block_checkpoint(uint64_t height) override;
|
||||
bool get_block_checkpoint(uint64_t height, checkpoint_t& checkpoint) const override;
|
||||
bool get_top_checkpoint(checkpoint_t& checkpoint) const override;
|
||||
std::vector<checkpoint_t> get_checkpoints_range(
|
||||
uint64_t start,
|
||||
uint64_t end,
|
||||
size_t num_desired_checkpoints = GET_ALL_CHECKPOINTS) const override;
|
||||
|
||||
void set_batch_transactions(bool batch_transactions) override;
|
||||
bool batch_start(uint64_t batch_num_blocks = 0, uint64_t batch_bytes = 0) override;
|
||||
void batch_commit();
|
||||
void batch_stop() override;
|
||||
void batch_abort() override;
|
||||
|
||||
void block_wtxn_start() override;
|
||||
void block_wtxn_stop() override;
|
||||
void block_wtxn_abort() override;
|
||||
bool block_rtxn_start() const override;
|
||||
void block_rtxn_stop() const override;
|
||||
void block_rtxn_abort() const override;
|
||||
|
||||
bool block_rtxn_start(MDB_txn** mtxn, mdb_txn_cursors** mcur) const;
|
||||
|
||||
void pop_block(block& blk, std::vector<transaction>& txs) override;
|
||||
|
||||
bool can_thread_bulk_indices() const override { return true; }
|
||||
|
||||
/**
|
||||
* @brief return a histogram of outputs on the blockchain
|
||||
*
|
||||
* @param amounts optional set of amounts to lookup
|
||||
* @param unlocked whether to restrict count to unlocked outputs
|
||||
* @param recent_cutoff timestamp to determine which outputs are recent
|
||||
* @param min_count return only amounts with at least that many instances
|
||||
*
|
||||
* @return a set of amount/instances
|
||||
*/
|
||||
std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> get_output_histogram(
|
||||
const std::vector<uint64_t>& amounts,
|
||||
bool unlocked,
|
||||
uint64_t recent_cutoff,
|
||||
uint64_t min_count) const override;
|
||||
|
||||
bool get_output_distribution(
|
||||
uint64_t amount,
|
||||
uint64_t from_height,
|
||||
uint64_t to_height,
|
||||
std::vector<uint64_t>& distribution,
|
||||
uint64_t& base) const override;
|
||||
void get_output_blacklist(std::vector<uint64_t>& blacklist) const override;
|
||||
void add_output_blacklist(std::vector<uint64_t> const& blacklist) override;
|
||||
|
||||
// helper functions
|
||||
static int compare_uint64(const MDB_val* a, const MDB_val* b);
|
||||
static int compare_hash32(const MDB_val* a, const MDB_val* b);
|
||||
static int compare_string(const MDB_val* a, const MDB_val* b);
|
||||
|
||||
private:
|
||||
void do_resize(uint64_t size_increase = 0);
|
||||
|
||||
bool need_resize(uint64_t threshold_size = 0) const;
|
||||
void check_and_resize_for_batch(uint64_t batch_num_blocks, uint64_t batch_bytes);
|
||||
uint64_t get_estimated_batch_size(uint64_t batch_num_blocks, uint64_t batch_bytes) const;
|
||||
|
||||
void add_block(
|
||||
const block& blk,
|
||||
size_t block_weight,
|
||||
uint64_t long_term_block_weight,
|
||||
const difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
uint64_t num_rct_outs,
|
||||
const crypto::hash& block_hash) override;
|
||||
|
||||
void remove_block() override;
|
||||
|
||||
uint64_t add_transaction_data(
|
||||
const crypto::hash& blk_hash,
|
||||
const std::pair<transaction, std::string>& tx,
|
||||
const crypto::hash& tx_hash,
|
||||
const crypto::hash& tx_prunable_hash) override;
|
||||
|
||||
void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx) override;
|
||||
|
||||
uint64_t add_output(
|
||||
const crypto::hash& tx_hash,
|
||||
const tx_out& tx_output,
|
||||
const uint64_t& local_index,
|
||||
const uint64_t unlock_time,
|
||||
const rct::key* commitment) override;
|
||||
|
||||
void add_tx_amount_output_indices(
|
||||
const uint64_t tx_id, const std::vector<uint64_t>& amount_output_indices) override;
|
||||
|
||||
void remove_tx_outputs(const uint64_t tx_id, const transaction& tx);
|
||||
|
||||
void remove_output(const uint64_t amount, const uint64_t& out_index);
|
||||
|
||||
void prune_outputs(uint64_t amount) override;
|
||||
|
||||
void add_spent_key(const crypto::key_image& k_image) override;
|
||||
|
||||
void remove_spent_key(const crypto::key_image& k_image) override;
|
||||
|
||||
uint64_t num_outputs() const;
|
||||
|
||||
inline void check_open() const;
|
||||
|
||||
bool prune_worker(int mode, uint32_t pruning_seed);
|
||||
|
||||
bool is_read_only() const override;
|
||||
|
||||
uint64_t get_database_size() const override;
|
||||
|
||||
std::vector<uint64_t> get_block_info_64bit_fields(
|
||||
uint64_t start_height, size_t count, uint64_t (*extract)(const mdb_block_info*)) const;
|
||||
|
||||
uint64_t get_max_block_size() override;
|
||||
void add_max_block_size(uint64_t sz) override;
|
||||
|
||||
bool has_key_image(const crypto::key_image& img) const override;
|
||||
// fix up anything that may be wrong due to past bugs
|
||||
void fixup(cryptonote::network_type nettype) override;
|
||||
|
||||
// migrate from older DB version to current
|
||||
void migrate(const uint32_t oldversion, cryptonote::network_type nettype);
|
||||
|
||||
void migrate_0_1();
|
||||
void migrate_1_2();
|
||||
void migrate_2_3();
|
||||
void migrate_3_4();
|
||||
void migrate_4_5(cryptonote::network_type nettype);
|
||||
void migrate_5_6();
|
||||
void migrate_6_7();
|
||||
|
||||
void cleanup_batch();
|
||||
|
||||
void add_txpool_tx(const crypto::hash &txid, const std::string &blob, const txpool_tx_meta_t& meta) override;
|
||||
void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t& meta) override;
|
||||
uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const override;
|
||||
bool txpool_has_tx(const crypto::hash &txid) const override;
|
||||
void remove_txpool_tx(const crypto::hash& txid) override;
|
||||
bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const override;
|
||||
bool get_txpool_tx_blob(const crypto::hash& txid, std::string &bd) const override;
|
||||
std::string get_txpool_tx_blob(const crypto::hash& txid) const override;
|
||||
uint32_t get_blockchain_pruning_seed() const override;
|
||||
bool prune_blockchain(uint32_t pruning_seed = 0) override;
|
||||
bool update_pruning() override;
|
||||
bool check_pruning() override;
|
||||
bool get_block_checkpoint_internal(
|
||||
uint64_t height, checkpoint_t& checkpoint, MDB_cursor_op op) const;
|
||||
void set_service_node_data(const std::string& data, bool long_term) override;
|
||||
bool get_service_node_data(std::string& data, bool long_term) const override;
|
||||
void clear_service_node_data() override;
|
||||
|
||||
void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const std::string &blob, const std::string *checkpoint) override;
|
||||
bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, std::string *blob, std::string *checkpoint) const override;
|
||||
void remove_alt_block(const crypto::hash &blkid) override;
|
||||
uint64_t get_alt_block_count() override;
|
||||
void drop_alt_blocks() override;
|
||||
bool get_service_node_proof(
|
||||
const crypto::public_key& pubkey, service_nodes::proof_info& proof) const override;
|
||||
void set_service_node_proof(
|
||||
const crypto::public_key& pubkey, const service_nodes::proof_info& proof) override;
|
||||
std::unordered_map<crypto::public_key, service_nodes::proof_info> get_all_service_node_proofs()
|
||||
const override;
|
||||
bool remove_service_node_proof(const crypto::public_key& pubkey) override;
|
||||
|
||||
bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const std::string*)> f, bool include_blob = false, bool include_unrelayed_txes = true) const override;
|
||||
private:
|
||||
template <
|
||||
typename T,
|
||||
std::enable_if_t<
|
||||
std::is_same_v<T, cryptonote::block> ||
|
||||
std::is_same_v<T, cryptonote::block_header> ||
|
||||
std::is_same_v<T, std::string>,
|
||||
int> = 0>
|
||||
T get_and_convert_block_blob_from_height(uint64_t height) const;
|
||||
|
||||
bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const override;
|
||||
bool for_blocks_range(const uint64_t& h1, const uint64_t& h2, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const override;
|
||||
bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const override;
|
||||
bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const override;
|
||||
bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const override;
|
||||
bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const std::string *block_blob, const std::string *checkpoint_blob)> f, bool include_blob = false) const override;
|
||||
MDB_env* m_env;
|
||||
|
||||
uint64_t add_block( const std::pair<block, std::string>& blk
|
||||
, size_t block_weight
|
||||
, uint64_t long_term_block_weight
|
||||
, const difficulty_type& cumulative_difficulty
|
||||
, const uint64_t& coins_generated
|
||||
, const std::vector<std::pair<transaction, std::string>>& txs
|
||||
) override;
|
||||
void update_block_checkpoint(checkpoint_t const &checkpoint) override;
|
||||
void remove_block_checkpoint(uint64_t height) override;
|
||||
bool get_block_checkpoint (uint64_t height, checkpoint_t &checkpoint) const override;
|
||||
bool get_top_checkpoint (checkpoint_t &checkpoint) const override;
|
||||
std::vector<checkpoint_t> get_checkpoints_range(uint64_t start, uint64_t end, size_t num_desired_checkpoints = GET_ALL_CHECKPOINTS) const override;
|
||||
MDB_dbi m_blocks;
|
||||
MDB_dbi m_block_heights;
|
||||
MDB_dbi m_block_info;
|
||||
MDB_dbi m_block_checkpoints;
|
||||
|
||||
void set_batch_transactions(bool batch_transactions) override;
|
||||
bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) override;
|
||||
void batch_commit();
|
||||
void batch_stop() override;
|
||||
void batch_abort() override;
|
||||
MDB_dbi m_txs;
|
||||
MDB_dbi m_txs_pruned;
|
||||
MDB_dbi m_txs_prunable;
|
||||
MDB_dbi m_txs_prunable_hash;
|
||||
MDB_dbi m_txs_prunable_tip;
|
||||
MDB_dbi m_tx_indices;
|
||||
MDB_dbi m_tx_outputs;
|
||||
|
||||
void block_wtxn_start() override;
|
||||
void block_wtxn_stop() override;
|
||||
void block_wtxn_abort() override;
|
||||
bool block_rtxn_start() const override;
|
||||
void block_rtxn_stop() const override;
|
||||
void block_rtxn_abort() const override;
|
||||
MDB_dbi m_output_txs;
|
||||
MDB_dbi m_output_amounts;
|
||||
MDB_dbi m_output_blacklist;
|
||||
|
||||
bool block_rtxn_start(MDB_txn **mtxn, mdb_txn_cursors **mcur) const;
|
||||
MDB_dbi m_spent_keys;
|
||||
|
||||
void pop_block(block& blk, std::vector<transaction>& txs) override;
|
||||
MDB_dbi m_txpool_meta;
|
||||
MDB_dbi m_txpool_blob;
|
||||
|
||||
bool can_thread_bulk_indices() const override { return true; }
|
||||
MDB_dbi m_alt_blocks;
|
||||
|
||||
/**
|
||||
* @brief return a histogram of outputs on the blockchain
|
||||
*
|
||||
* @param amounts optional set of amounts to lookup
|
||||
* @param unlocked whether to restrict count to unlocked outputs
|
||||
* @param recent_cutoff timestamp to determine which outputs are recent
|
||||
* @param min_count return only amounts with at least that many instances
|
||||
*
|
||||
* @return a set of amount/instances
|
||||
*/
|
||||
std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const override;
|
||||
MDB_dbi m_hf_starting_heights;
|
||||
MDB_dbi m_hf_versions;
|
||||
|
||||
bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector<uint64_t> &distribution, uint64_t &base) const override;
|
||||
void get_output_blacklist(std::vector<uint64_t> &blacklist) const override;
|
||||
void add_output_blacklist(std::vector<uint64_t> const &blacklist) override;
|
||||
MDB_dbi m_service_node_data;
|
||||
MDB_dbi m_service_node_proofs;
|
||||
|
||||
// helper functions
|
||||
static int compare_uint64(const MDB_val *a, const MDB_val *b);
|
||||
static int compare_hash32(const MDB_val *a, const MDB_val *b);
|
||||
static int compare_string(const MDB_val *a, const MDB_val *b);
|
||||
MDB_dbi m_properties;
|
||||
|
||||
private:
|
||||
void do_resize(uint64_t size_increase=0);
|
||||
|
||||
bool need_resize(uint64_t threshold_size=0) const;
|
||||
void check_and_resize_for_batch(uint64_t batch_num_blocks, uint64_t batch_bytes);
|
||||
uint64_t get_estimated_batch_size(uint64_t batch_num_blocks, uint64_t batch_bytes) const;
|
||||
|
||||
void add_block( const block& blk
|
||||
, size_t block_weight
|
||||
, uint64_t long_term_block_weight
|
||||
, const difficulty_type& cumulative_difficulty
|
||||
, const uint64_t& coins_generated
|
||||
, uint64_t num_rct_outs
|
||||
, const crypto::hash& block_hash
|
||||
) override;
|
||||
|
||||
void remove_block() override;
|
||||
|
||||
uint64_t add_transaction_data(const crypto::hash& blk_hash, const std::pair<transaction, std::string>& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) override;
|
||||
|
||||
void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx) override;
|
||||
|
||||
uint64_t add_output(const crypto::hash& tx_hash,
|
||||
const tx_out& tx_output,
|
||||
const uint64_t& local_index,
|
||||
const uint64_t unlock_time,
|
||||
const rct::key *commitment
|
||||
) override;
|
||||
mutable uint64_t m_cum_size; // used in batch size estimation
|
||||
mutable unsigned int m_cum_count;
|
||||
fs::path m_folder;
|
||||
mdb_txn_safe* m_write_txn; // may point to either a short-lived txn or a batch txn
|
||||
mdb_txn_safe* m_write_batch_txn; // persist batch txn outside of BlockchainLMDB
|
||||
boost::thread::id m_writer;
|
||||
|
||||
void add_tx_amount_output_indices(const uint64_t tx_id,
|
||||
const std::vector<uint64_t>& amount_output_indices
|
||||
) override;
|
||||
bool m_batch_transactions; // support for batch transactions
|
||||
bool m_batch_active; // whether batch transaction is in progress
|
||||
|
||||
void remove_tx_outputs(const uint64_t tx_id, const transaction& tx);
|
||||
|
||||
void remove_output(const uint64_t amount, const uint64_t& out_index);
|
||||
|
||||
void prune_outputs(uint64_t amount) override;
|
||||
|
||||
void add_spent_key(const crypto::key_image& k_image) override;
|
||||
|
||||
void remove_spent_key(const crypto::key_image& k_image) override;
|
||||
|
||||
uint64_t num_outputs() const;
|
||||
|
||||
inline void check_open() const;
|
||||
|
||||
bool prune_worker(int mode, uint32_t pruning_seed);
|
||||
|
||||
bool is_read_only() const override;
|
||||
|
||||
uint64_t get_database_size() const override;
|
||||
|
||||
std::vector<uint64_t> get_block_info_64bit_fields(uint64_t start_height, size_t count, uint64_t (*extract)(const mdb_block_info*)) const;
|
||||
|
||||
uint64_t get_max_block_size() override;
|
||||
void add_max_block_size(uint64_t sz) override;
|
||||
|
||||
// fix up anything that may be wrong due to past bugs
|
||||
void fixup(cryptonote::network_type nettype) override;
|
||||
|
||||
// migrate from older DB version to current
|
||||
void migrate(const uint32_t oldversion, cryptonote::network_type nettype);
|
||||
|
||||
void migrate_0_1();
|
||||
void migrate_1_2();
|
||||
void migrate_2_3();
|
||||
void migrate_3_4();
|
||||
void migrate_4_5(cryptonote::network_type nettype);
|
||||
void migrate_5_6();
|
||||
void migrate_6_7();
|
||||
|
||||
void cleanup_batch();
|
||||
|
||||
bool get_block_checkpoint_internal(uint64_t height, checkpoint_t &checkpoint, MDB_cursor_op op) const;
|
||||
void set_service_node_data(const std::string& data, bool long_term) override;
|
||||
bool get_service_node_data(std::string& data, bool long_term) const override;
|
||||
void clear_service_node_data() override;
|
||||
|
||||
bool get_service_node_proof(const crypto::public_key& pubkey, service_nodes::proof_info& proof) const override;
|
||||
void set_service_node_proof(const crypto::public_key& pubkey, const service_nodes::proof_info& proof) override;
|
||||
std::unordered_map<crypto::public_key, service_nodes::proof_info> get_all_service_node_proofs() const override;
|
||||
bool remove_service_node_proof(const crypto::public_key& pubkey) override;
|
||||
|
||||
private:
|
||||
template <typename T,
|
||||
std::enable_if_t<std::is_same_v<T, cryptonote::block> ||
|
||||
std::is_same_v<T, cryptonote::block_header> ||
|
||||
std::is_same_v<T, std::string>, int> = 0>
|
||||
T get_and_convert_block_blob_from_height(uint64_t height) const;
|
||||
|
||||
MDB_env* m_env;
|
||||
|
||||
MDB_dbi m_blocks;
|
||||
MDB_dbi m_block_heights;
|
||||
MDB_dbi m_block_info;
|
||||
MDB_dbi m_block_checkpoints;
|
||||
|
||||
MDB_dbi m_txs;
|
||||
MDB_dbi m_txs_pruned;
|
||||
MDB_dbi m_txs_prunable;
|
||||
MDB_dbi m_txs_prunable_hash;
|
||||
MDB_dbi m_txs_prunable_tip;
|
||||
MDB_dbi m_tx_indices;
|
||||
MDB_dbi m_tx_outputs;
|
||||
|
||||
MDB_dbi m_output_txs;
|
||||
MDB_dbi m_output_amounts;
|
||||
MDB_dbi m_output_blacklist;
|
||||
|
||||
MDB_dbi m_spent_keys;
|
||||
|
||||
MDB_dbi m_txpool_meta;
|
||||
MDB_dbi m_txpool_blob;
|
||||
|
||||
MDB_dbi m_alt_blocks;
|
||||
|
||||
MDB_dbi m_hf_starting_heights;
|
||||
MDB_dbi m_hf_versions;
|
||||
|
||||
MDB_dbi m_service_node_data;
|
||||
MDB_dbi m_service_node_proofs;
|
||||
|
||||
MDB_dbi m_properties;
|
||||
|
||||
mutable uint64_t m_cum_size; // used in batch size estimation
|
||||
mutable unsigned int m_cum_count;
|
||||
fs::path m_folder;
|
||||
mdb_txn_safe* m_write_txn; // may point to either a short-lived txn or a batch txn
|
||||
mdb_txn_safe* m_write_batch_txn; // persist batch txn outside of BlockchainLMDB
|
||||
boost::thread::id m_writer;
|
||||
|
||||
bool m_batch_transactions; // support for batch transactions
|
||||
bool m_batch_active; // whether batch transaction is in progress
|
||||
|
||||
mdb_txn_cursors m_wcursors;
|
||||
mutable boost::thread_specific_ptr<mdb_threadinfo> m_tinfo;
|
||||
mdb_txn_cursors m_wcursors;
|
||||
mutable boost::thread_specific_ptr<mdb_threadinfo> m_tinfo;
|
||||
|
||||
#if defined(__arm__)
|
||||
// force a value so it can compile with 32-bit ARM
|
||||
constexpr static uint64_t DEFAULT_MAPSIZE = 1LL << 31;
|
||||
// force a value so it can compile with 32-bit ARM
|
||||
constexpr static uint64_t DEFAULT_MAPSIZE = 1LL << 31;
|
||||
#else
|
||||
#if defined(ENABLE_AUTO_RESIZE)
|
||||
constexpr static uint64_t DEFAULT_MAPSIZE = 1LL << 30;
|
||||
constexpr static uint64_t DEFAULT_MAPSIZE = 1LL << 30;
|
||||
#else
|
||||
constexpr static uint64_t DEFAULT_MAPSIZE = 1LL << 33;
|
||||
constexpr static uint64_t DEFAULT_MAPSIZE = 1LL << 33;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Guards LMDB resize
|
||||
std::mutex m_synchronization_lock;
|
||||
// Guards LMDB resize
|
||||
std::mutex m_synchronization_lock;
|
||||
|
||||
constexpr static float RESIZE_PERCENT = 0.9f;
|
||||
constexpr static float RESIZE_PERCENT = 0.9f;
|
||||
};
|
||||
|
||||
} // namespace cryptonote
|
||||
|
|
|
@ -32,27 +32,43 @@
|
|||
|
||||
#include "cryptonote_core/blockchain.h"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
// This class is meant to create a batch when none currently exists.
|
||||
// If a batch exists, it can't be from another thread, since we can
|
||||
// only be called with the txpool lock taken, and it is held during
|
||||
// the whole prepare/handle/cleanup incoming block sequence.
|
||||
class LockedTXN {
|
||||
public:
|
||||
LockedTXN(Blockchain &b): m_db{b.get_db()} {
|
||||
m_batch = m_db.batch_start();
|
||||
}
|
||||
LockedTXN(const LockedTXN &) = delete;
|
||||
LockedTXN &operator=(const LockedTXN &) = delete;
|
||||
LockedTXN(LockedTXN &&o) : m_db{o.m_db}, m_batch{o.m_batch} { o.m_batch = false; }
|
||||
LockedTXN &operator=(LockedTXN &&) = delete;
|
||||
namespace cryptonote {
|
||||
// This class is meant to create a batch when none currently exists.
|
||||
// If a batch exists, it can't be from another thread, since we can
|
||||
// only be called with the txpool lock taken, and it is held during
|
||||
// the whole prepare/handle/cleanup incoming block sequence.
|
||||
class LockedTXN {
|
||||
public:
|
||||
LockedTXN(Blockchain& b) : m_db{b.get_db()} { m_batch = m_db.batch_start(); }
|
||||
LockedTXN(const LockedTXN&) = delete;
|
||||
LockedTXN& operator=(const LockedTXN&) = delete;
|
||||
LockedTXN(LockedTXN&& o) : m_db{o.m_db}, m_batch{o.m_batch} { o.m_batch = false; }
|
||||
LockedTXN& operator=(LockedTXN&&) = delete;
|
||||
|
||||
void commit() { try { if (m_batch) { m_db.batch_stop(); m_batch = false; } } catch (const std::exception &e) { log::warning(globallogcat, "LockedTXN::commit filtering exception: {}", e.what()); } }
|
||||
void abort() { try { if (m_batch) { m_db.batch_abort(); m_batch = false; } } catch (const std::exception &e) { log::warning(globallogcat, "LockedTXN::abort filtering exception: {}", e.what()); } }
|
||||
~LockedTXN() { this->abort(); }
|
||||
private:
|
||||
BlockchainDB &m_db;
|
||||
bool m_batch;
|
||||
};
|
||||
}
|
||||
void commit() {
|
||||
try {
|
||||
if (m_batch) {
|
||||
m_db.batch_stop();
|
||||
m_batch = false;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
log::warning(globallogcat, "LockedTXN::commit filtering exception: {}", e.what());
|
||||
}
|
||||
}
|
||||
void abort() {
|
||||
try {
|
||||
if (m_batch) {
|
||||
m_db.batch_abort();
|
||||
m_batch = false;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
log::warning(globallogcat, "LockedTXN::abort filtering exception: {}", e.what());
|
||||
}
|
||||
}
|
||||
~LockedTXN() { this->abort(); }
|
||||
|
||||
private:
|
||||
BlockchainDB& m_db;
|
||||
bool m_batch;
|
||||
};
|
||||
} // namespace cryptonote
|
||||
|
|
|
@ -27,41 +27,44 @@
|
|||
|
||||
#include "db_sqlite.h"
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <sodium.h>
|
||||
#include <fmt/core.h>
|
||||
#include <sodium.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "cryptonote_basic/hardfork.h"
|
||||
#include "cryptonote_config.h"
|
||||
#include "cryptonote_core/blockchain.h"
|
||||
#include "cryptonote_core/service_node_list.h"
|
||||
#include "common/string_util.h"
|
||||
#include "cryptonote_basic/hardfork.h"
|
||||
|
||||
namespace cryptonote {
|
||||
|
||||
static auto logcat = log::Cat("blockchain.db.sqlite");
|
||||
|
||||
BlockchainSQLite::BlockchainSQLite(cryptonote::network_type nettype, fs::path db_path): db::Database(db_path, ""), m_nettype(nettype), filename {db_path.u8string()} {
|
||||
static auto logcat = log::Cat("blockchain.db.sqlite");
|
||||
|
||||
BlockchainSQLite::BlockchainSQLite(cryptonote::network_type nettype, fs::path db_path) :
|
||||
db::Database(db_path, ""), m_nettype(nettype), filename{db_path.u8string()} {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{}", __func__);
|
||||
height = 0;
|
||||
|
||||
if (!db.tableExists("batched_payments_accrued") || !db.tableExists("batched_payments_raw") || !db.tableExists("batch_db_info")) {
|
||||
create_schema();
|
||||
if (!db.tableExists("batched_payments_accrued") || !db.tableExists("batched_payments_raw") ||
|
||||
!db.tableExists("batch_db_info")) {
|
||||
create_schema();
|
||||
}
|
||||
|
||||
upgrade_schema();
|
||||
|
||||
height = prepared_get<int64_t>("SELECT height FROM batch_db_info");
|
||||
}
|
||||
}
|
||||
|
||||
void BlockchainSQLite::create_schema() {
|
||||
void BlockchainSQLite::create_schema() {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{}", __func__);
|
||||
|
||||
auto& netconf = cryptonote::get_config(m_nettype);
|
||||
|
||||
db.exec(fmt::format(R"(
|
||||
db.exec(fmt::format(
|
||||
R"(
|
||||
CREATE TABLE batched_payments_accrued(
|
||||
address VARCHAR NOT NULL,
|
||||
amount BIGINT NOT NULL,
|
||||
|
@ -114,29 +117,27 @@ namespace cryptonote {
|
|||
ON CONFLICT(address) DO UPDATE SET amount = (amount + excluded.amount);
|
||||
END;
|
||||
)",
|
||||
netconf.BATCHING_INTERVAL));
|
||||
netconf.BATCHING_INTERVAL));
|
||||
|
||||
log::debug(logcat, "Database setup complete");
|
||||
}
|
||||
}
|
||||
|
||||
void BlockchainSQLite::upgrade_schema() {
|
||||
void BlockchainSQLite::upgrade_schema() {
|
||||
bool have_offset = false;
|
||||
SQLite::Statement msg_cols{db, "PRAGMA main.table_info(batched_payments_accrued)"};
|
||||
while (msg_cols.executeStep()) {
|
||||
auto [cid, name] = db::get<int64_t, std::string>(msg_cols);
|
||||
if (name == "payout_offset")
|
||||
have_offset = true;
|
||||
auto [cid, name] = db::get<int64_t, std::string>(msg_cols);
|
||||
if (name == "payout_offset")
|
||||
have_offset = true;
|
||||
}
|
||||
|
||||
if (!have_offset) {
|
||||
log::info(logcat, "Adding payout_offset to batching db");
|
||||
auto& netconf = get_config(m_nettype);
|
||||
SQLite::Transaction transaction{
|
||||
db,
|
||||
SQLite::TransactionBehavior::IMMEDIATE
|
||||
};
|
||||
log::info(logcat, "Adding payout_offset to batching db");
|
||||
auto& netconf = get_config(m_nettype);
|
||||
SQLite::Transaction transaction{db, SQLite::TransactionBehavior::IMMEDIATE};
|
||||
|
||||
db.exec(fmt::format(R"(
|
||||
db.exec(fmt::format(
|
||||
R"(
|
||||
ALTER TABLE batched_payments_accrued ADD COLUMN payout_offset INTEGER NOT NULL DEFAULT -1;
|
||||
|
||||
CREATE INDEX batched_payments_accrued_payout_offset_idx ON batched_payments_accrued(payout_offset);
|
||||
|
@ -148,39 +149,44 @@ namespace cryptonote {
|
|||
INSERT INTO batched_payments_accrued(address, payout_offset, amount) VALUES(OLD.address, OLD.height_paid % {}, OLD.amount)
|
||||
ON CONFLICT(address) DO UPDATE SET amount = (amount + excluded.amount);
|
||||
END;
|
||||
)", netconf.BATCHING_INTERVAL));
|
||||
)",
|
||||
netconf.BATCHING_INTERVAL));
|
||||
|
||||
auto st = prepared_st("UPDATE batched_payments_accrued SET payout_offset = ? WHERE address = ?");
|
||||
for (const auto& address : prepared_results<std::string>("SELECT address from batched_payments_accrued")) {
|
||||
cryptonote::address_parse_info addr_info{};
|
||||
cryptonote::get_account_address_from_str(addr_info, m_nettype, address);
|
||||
auto offset = static_cast<int>(addr_info.address.modulus(netconf.BATCHING_INTERVAL));
|
||||
exec_query(st, offset, address);
|
||||
st->reset();
|
||||
}
|
||||
auto st = prepared_st(
|
||||
"UPDATE batched_payments_accrued SET payout_offset = ? WHERE address = ?");
|
||||
for (const auto& address : prepared_results<std::string>("SELECT address from "
|
||||
"batched_payments_accrued")) {
|
||||
cryptonote::address_parse_info addr_info{};
|
||||
cryptonote::get_account_address_from_str(addr_info, m_nettype, address);
|
||||
auto offset = static_cast<int>(addr_info.address.modulus(netconf.BATCHING_INTERVAL));
|
||||
exec_query(st, offset, address);
|
||||
st->reset();
|
||||
}
|
||||
|
||||
auto count = prepared_get<int>("SELECT COUNT(*) FROM batched_payments_accrued WHERE payout_offset NOT BETWEEN 0 AND ?",
|
||||
static_cast<int>(netconf.BATCHING_INTERVAL));
|
||||
auto count = prepared_get<int>(
|
||||
"SELECT COUNT(*) FROM batched_payments_accrued WHERE payout_offset NOT BETWEEN 0 "
|
||||
"AND ?",
|
||||
static_cast<int>(netconf.BATCHING_INTERVAL));
|
||||
|
||||
if (count != 0) {
|
||||
constexpr auto error = "Batching db update to add offsets failed: not all addresses were converted";
|
||||
log::error(logcat, error);
|
||||
throw std::runtime_error{error};
|
||||
}
|
||||
if (count != 0) {
|
||||
constexpr auto error =
|
||||
"Batching db update to add offsets failed: not all addresses were converted";
|
||||
log::error(logcat, error);
|
||||
throw std::runtime_error{error};
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
const auto archive_table_count = prepared_get<int64_t>("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='batched_payments_accrued_archive';");
|
||||
if(archive_table_count == 0)
|
||||
{
|
||||
log::info(logcat, "Adding archiving to batching db");
|
||||
auto& netconf = get_config(m_nettype);
|
||||
SQLite::Transaction transaction{
|
||||
db,
|
||||
SQLite::TransactionBehavior::IMMEDIATE
|
||||
};
|
||||
db.exec(fmt::format(R"(
|
||||
const auto archive_table_count = prepared_get<int64_t>(
|
||||
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND "
|
||||
"name='batched_payments_accrued_archive';");
|
||||
if (archive_table_count == 0) {
|
||||
log::info(logcat, "Adding archiving to batching db");
|
||||
auto& netconf = get_config(m_nettype);
|
||||
SQLite::Transaction transaction{db, SQLite::TransactionBehavior::IMMEDIATE};
|
||||
db.exec(fmt::format(
|
||||
R"(
|
||||
CREATE TABLE batched_payments_accrued_archive(
|
||||
address VARCHAR NOT NULL,
|
||||
amount BIGINT NOT NULL,
|
||||
|
@ -204,12 +210,14 @@ namespace cryptonote {
|
|||
FOR EACH ROW WHEN NEW.height < OLD.height BEGIN
|
||||
DELETE FROM batched_payments_accrued_archive WHERE archive_height >= NEW.height;
|
||||
END;
|
||||
)", netconf.STORE_LONG_TERM_STATE_INTERVAL, 500));
|
||||
transaction.commit();
|
||||
)",
|
||||
netconf.STORE_LONG_TERM_STATE_INTERVAL,
|
||||
500));
|
||||
transaction.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockchainSQLite::reset_database() {
|
||||
void BlockchainSQLite::reset_database() {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{}", __func__);
|
||||
|
||||
db.exec(R"(
|
||||
|
@ -227,45 +235,43 @@ namespace cryptonote {
|
|||
create_schema();
|
||||
upgrade_schema();
|
||||
log::debug(logcat, "Database reset complete");
|
||||
}
|
||||
}
|
||||
|
||||
void BlockchainSQLite::update_height(uint64_t new_height) {
|
||||
void BlockchainSQLite::update_height(uint64_t new_height) {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{} Called with new height: {}", __func__, new_height);
|
||||
height = new_height;
|
||||
prepared_exec(
|
||||
"UPDATE batch_db_info SET height = ?",
|
||||
static_cast<int64_t>(height));
|
||||
}
|
||||
prepared_exec("UPDATE batch_db_info SET height = ?", static_cast<int64_t>(height));
|
||||
}
|
||||
|
||||
void BlockchainSQLite::increment_height() {
|
||||
void BlockchainSQLite::increment_height() {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{} Called with height: {}", __func__, height + 1);
|
||||
update_height(height + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void BlockchainSQLite::decrement_height() {
|
||||
void BlockchainSQLite::decrement_height() {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{} Called with height: {}", __func__, height - 1);
|
||||
update_height(height - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void BlockchainSQLite::blockchain_detached(uint64_t new_height)
|
||||
{
|
||||
void BlockchainSQLite::blockchain_detached(uint64_t new_height) {
|
||||
if (height < new_height)
|
||||
return;
|
||||
return;
|
||||
int64_t revert_to_height = new_height - 1;
|
||||
auto maybe_prev_interval = prepared_maybe_get<int64_t>(
|
||||
"SELECT DISTINCT archive_height FROM batched_payments_accrued_archive WHERE archive_height <= ? ORDER BY archive_height DESC LIMIT 1",
|
||||
revert_to_height);
|
||||
"SELECT DISTINCT archive_height FROM batched_payments_accrued_archive WHERE "
|
||||
"archive_height <= ? ORDER BY archive_height DESC LIMIT 1",
|
||||
revert_to_height);
|
||||
|
||||
if (!maybe_prev_interval)
|
||||
{
|
||||
auto fork_height = cryptonote::get_hard_fork_heights(m_nettype, hf::hf19_reward_batching);
|
||||
reset_database();
|
||||
update_height(fork_height.first.value_or(0));
|
||||
return;
|
||||
if (!maybe_prev_interval) {
|
||||
auto fork_height = cryptonote::get_hard_fork_heights(m_nettype, hf::hf19_reward_batching);
|
||||
reset_database();
|
||||
update_height(fork_height.first.value_or(0));
|
||||
return;
|
||||
}
|
||||
const auto prev_interval = *maybe_prev_interval;
|
||||
|
||||
db.exec(fmt::format(R"(
|
||||
db.exec(fmt::format(
|
||||
R"(
|
||||
DELETE FROM batched_payments_raw WHERE height_paid > {0};
|
||||
|
||||
DELETE FROM batched_payments_accrued;
|
||||
|
@ -275,430 +281,494 @@ namespace cryptonote {
|
|||
FROM batched_payments_accrued_archive WHERE archive_height = {0};
|
||||
|
||||
DELETE FROM batched_payments_accrued_archive WHERE archive_height >= {0};
|
||||
)", prev_interval));
|
||||
)",
|
||||
prev_interval));
|
||||
update_height(prev_interval);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Must be called with the address_str_cache_mutex held!
|
||||
const std::string& BlockchainSQLite::get_address_str(const account_public_address& addr)
|
||||
{
|
||||
// Must be called with the address_str_cache_mutex held!
|
||||
const std::string& BlockchainSQLite::get_address_str(const account_public_address& addr) {
|
||||
auto& address_str = address_str_cache[addr];
|
||||
if (address_str.empty())
|
||||
address_str = cryptonote::get_account_address_as_str(m_nettype, 0, addr);
|
||||
address_str = cryptonote::get_account_address_as_str(m_nettype, 0, addr);
|
||||
return address_str;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool BlockchainSQLite::add_sn_rewards(const std::vector<cryptonote::batch_sn_payment>& payments) {
|
||||
bool BlockchainSQLite::add_sn_rewards(const std::vector<cryptonote::batch_sn_payment>& payments) {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{}", __func__);
|
||||
auto insert_payment = prepared_st(
|
||||
"INSERT INTO batched_payments_accrued (address, payout_offset, amount) VALUES (?, ?, ?)"
|
||||
" ON CONFLICT (address) DO UPDATE SET amount = amount + excluded.amount");
|
||||
"INSERT INTO batched_payments_accrued (address, payout_offset, amount) VALUES (?, ?, ?)"
|
||||
" ON CONFLICT (address) DO UPDATE SET amount = amount + excluded.amount");
|
||||
|
||||
const auto& netconf = get_config(m_nettype);
|
||||
|
||||
for (auto& payment: payments) {
|
||||
auto offset = static_cast<int>(payment.address_info.address.modulus(netconf.BATCHING_INTERVAL));
|
||||
auto amt = static_cast<int64_t>(payment.amount);
|
||||
const auto& address_str = get_address_str(payment.address_info.address);
|
||||
log::trace(logcat, "Adding record for SN reward contributor {} to database with amount {}", address_str, amt);
|
||||
db::exec_query(insert_payment, address_str, offset, amt);
|
||||
insert_payment->reset();
|
||||
for (auto& payment : payments) {
|
||||
auto offset =
|
||||
static_cast<int>(payment.address_info.address.modulus(netconf.BATCHING_INTERVAL));
|
||||
auto amt = static_cast<int64_t>(payment.amount);
|
||||
const auto& address_str = get_address_str(payment.address_info.address);
|
||||
log::trace(
|
||||
logcat,
|
||||
"Adding record for SN reward contributor {} to database with amount {}",
|
||||
address_str,
|
||||
amt);
|
||||
db::exec_query(insert_payment, address_str, offset, amt);
|
||||
insert_payment->reset();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockchainSQLite::subtract_sn_rewards(const std::vector<cryptonote::batch_sn_payment>& payments) {
|
||||
bool BlockchainSQLite::subtract_sn_rewards(
|
||||
const std::vector<cryptonote::batch_sn_payment>& payments) {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{}", __func__);
|
||||
auto update_payment = prepared_st(
|
||||
"UPDATE batched_payments_accrued SET amount = (amount - ?) WHERE address = ?");
|
||||
"UPDATE batched_payments_accrued SET amount = (amount - ?) WHERE address = ?");
|
||||
|
||||
for (auto& payment: payments) {
|
||||
const auto& address_str = get_address_str(payment.address_info.address);
|
||||
auto result = db::exec_query(update_payment, static_cast<int64_t>(payment.amount), address_str);
|
||||
if (!result) {
|
||||
log::error(logcat, "tried to subtract payment from an address that doesn't exist: {}", address_str);
|
||||
return false;
|
||||
}
|
||||
update_payment->reset();
|
||||
for (auto& payment : payments) {
|
||||
const auto& address_str = get_address_str(payment.address_info.address);
|
||||
auto result =
|
||||
db::exec_query(update_payment, static_cast<int64_t>(payment.amount), address_str);
|
||||
if (!result) {
|
||||
log::error(
|
||||
logcat,
|
||||
"tried to subtract payment from an address that doesn't exist: {}",
|
||||
address_str);
|
||||
return false;
|
||||
}
|
||||
update_payment->reset();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<cryptonote::batch_sn_payment> BlockchainSQLite::get_sn_payments(uint64_t block_height) {
|
||||
std::vector<cryptonote::batch_sn_payment> BlockchainSQLite::get_sn_payments(uint64_t block_height) {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{}", __func__);
|
||||
|
||||
// <= here because we might have crap in the db that we don't clear until we actually add the HF
|
||||
// block later on. (This is a pretty slim edge case that happened on devnet and is probably
|
||||
// virtually impossible on mainnet).
|
||||
if (m_nettype != cryptonote::network_type::FAKECHAIN && block_height <= cryptonote::get_hard_fork_heights(m_nettype, hf::hf19_reward_batching).first.value_or(0))
|
||||
return {};
|
||||
if (m_nettype != cryptonote::network_type::FAKECHAIN &&
|
||||
block_height <= cryptonote::get_hard_fork_heights(m_nettype, hf::hf19_reward_batching)
|
||||
.first.value_or(0))
|
||||
return {};
|
||||
|
||||
const auto& conf = get_config(m_nettype);
|
||||
|
||||
auto accrued_amounts = prepared_results<std::string_view, int64_t>(
|
||||
"SELECT address, amount FROM batched_payments_accrued WHERE payout_offset = ? AND amount >= ? ORDER BY address ASC",
|
||||
static_cast<int>(block_height % conf.BATCHING_INTERVAL),
|
||||
static_cast<int64_t>(conf.MIN_BATCH_PAYMENT_AMOUNT * BATCH_REWARD_FACTOR));
|
||||
"SELECT address, amount FROM batched_payments_accrued WHERE payout_offset = ? AND "
|
||||
"amount >= ? ORDER BY address ASC",
|
||||
static_cast<int>(block_height % conf.BATCHING_INTERVAL),
|
||||
static_cast<int64_t>(conf.MIN_BATCH_PAYMENT_AMOUNT * BATCH_REWARD_FACTOR));
|
||||
|
||||
std::vector<cryptonote::batch_sn_payment> payments;
|
||||
|
||||
for (auto [address, amount] : accrued_amounts) {
|
||||
auto& p = payments.emplace_back();
|
||||
p.amount = amount / BATCH_REWARD_FACTOR * BATCH_REWARD_FACTOR; /* truncate to atomic OXEN */
|
||||
[[maybe_unused]] bool addr_ok = cryptonote::get_account_address_from_str(p.address_info, m_nettype, address);
|
||||
assert(addr_ok);
|
||||
auto& p = payments.emplace_back();
|
||||
p.amount = amount / BATCH_REWARD_FACTOR * BATCH_REWARD_FACTOR; /* truncate to atomic OXEN */
|
||||
[[maybe_unused]] bool addr_ok =
|
||||
cryptonote::get_account_address_from_str(p.address_info, m_nettype, address);
|
||||
assert(addr_ok);
|
||||
}
|
||||
|
||||
return payments;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint64_t BlockchainSQLite::get_accrued_earnings(const std::string& address) {
|
||||
uint64_t BlockchainSQLite::get_accrued_earnings(const std::string& address) {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{}", __func__);
|
||||
|
||||
auto earnings = prepared_maybe_get<int64_t>(
|
||||
"SELECT amount FROM batched_payments_accrued WHERE address = ?",
|
||||
address);
|
||||
"SELECT amount FROM batched_payments_accrued WHERE address = ?", address);
|
||||
return static_cast<uint64_t>(earnings.value_or(0) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::vector<std::string>, std::vector<uint64_t>> BlockchainSQLite::get_all_accrued_earnings() {
|
||||
std::pair<std::vector<std::string>, std::vector<uint64_t>>
|
||||
BlockchainSQLite::get_all_accrued_earnings() {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{}", __func__);
|
||||
|
||||
std::pair<std::vector<std::string>, std::vector<uint64_t>> result;
|
||||
auto& [addresses, amounts] = result;
|
||||
|
||||
for (auto [addr, amt] : prepared_results<std::string, int64_t>(
|
||||
"SELECT address, amount FROM batched_payments_accrued")) {
|
||||
auto amount = static_cast<uint64_t>(amt / 1000);
|
||||
if (amount > 0) {
|
||||
addresses.push_back(std::move(addr));
|
||||
amounts.push_back(amount);
|
||||
}
|
||||
for (auto [addr, amt] : prepared_results<std::string, int64_t>("SELECT address, amount FROM "
|
||||
"batched_payments_accrued")) {
|
||||
auto amount = static_cast<uint64_t>(amt / 1000);
|
||||
if (amount > 0) {
|
||||
addresses.push_back(std::move(addr));
|
||||
amounts.push_back(amount);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void BlockchainSQLite::calculate_rewards(hf hf_version, uint64_t distribution_amount, const service_nodes::service_node_info& sn_info, std::vector<cryptonote::batch_sn_payment>& payments) {
|
||||
void BlockchainSQLite::calculate_rewards(
|
||||
hf hf_version,
|
||||
uint64_t distribution_amount,
|
||||
const service_nodes::service_node_info& sn_info,
|
||||
std::vector<cryptonote::batch_sn_payment>& payments) {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{}", __func__);
|
||||
|
||||
// Find out how much is due for the operator: fee_portions/PORTIONS * reward
|
||||
assert(sn_info.portions_for_operator <= old::STAKING_PORTIONS);
|
||||
uint64_t operator_fee = mul128_div64(sn_info.portions_for_operator, distribution_amount, old::STAKING_PORTIONS);
|
||||
uint64_t operator_fee =
|
||||
mul128_div64(sn_info.portions_for_operator, distribution_amount, old::STAKING_PORTIONS);
|
||||
|
||||
assert(operator_fee <= distribution_amount);
|
||||
|
||||
payments.clear();
|
||||
// Pay the operator fee to the operator
|
||||
if (operator_fee > 0)
|
||||
payments.emplace_back(sn_info.operator_address, operator_fee);
|
||||
payments.emplace_back(sn_info.operator_address, operator_fee);
|
||||
|
||||
// Pay the balance to all the contributors (including the operator again)
|
||||
uint64_t total_contributed_to_sn = std::accumulate(
|
||||
sn_info.contributors.begin(),
|
||||
sn_info.contributors.end(),
|
||||
uint64_t(0),
|
||||
[](auto&& a, auto&& b) { return a + b.amount; });
|
||||
sn_info.contributors.begin(),
|
||||
sn_info.contributors.end(),
|
||||
uint64_t(0),
|
||||
[](auto&& a, auto&& b) { return a + b.amount; });
|
||||
|
||||
for (auto& contributor: sn_info.contributors) {
|
||||
// This calculates (contributor.amount / total_contributed_to_winner_sn) * (distribution_amount - operator_fee) but using 128 bit integer math
|
||||
uint64_t c_reward = mul128_div64(contributor.amount, distribution_amount - operator_fee, total_contributed_to_sn);
|
||||
if (c_reward > 0)
|
||||
payments.emplace_back(contributor.address, c_reward);
|
||||
for (auto& contributor : sn_info.contributors) {
|
||||
// This calculates (contributor.amount / total_contributed_to_winner_sn) *
|
||||
// (distribution_amount - operator_fee) but using 128 bit integer math
|
||||
uint64_t c_reward = mul128_div64(
|
||||
contributor.amount, distribution_amount - operator_fee, total_contributed_to_sn);
|
||||
if (c_reward > 0)
|
||||
payments.emplace_back(contributor.address, c_reward);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculates block rewards, then invokes either `add_sn_rewards` (if `add`) or
|
||||
// `subtract_sn_rewards` (if `!add`) to process them.
|
||||
bool BlockchainSQLite::reward_handler(
|
||||
const cryptonote::block& block,
|
||||
const service_nodes::service_node_list::state_t& service_nodes_state,
|
||||
bool add)
|
||||
{
|
||||
// Calculates block rewards, then invokes either `add_sn_rewards` (if `add`) or
|
||||
// `subtract_sn_rewards` (if `!add`) to process them.
|
||||
bool BlockchainSQLite::reward_handler(
|
||||
const cryptonote::block& block,
|
||||
const service_nodes::service_node_list::state_t& service_nodes_state,
|
||||
bool add) {
|
||||
// The method we call do actually handle the change: either `add_sn_payments` if add is true,
|
||||
// `subtract_sn_payments` otherwise:
|
||||
bool (BlockchainSQLite::* add_or_subtract)(const std::vector<cryptonote::batch_sn_payment>&)
|
||||
= add ? &BlockchainSQLite::add_sn_rewards : &BlockchainSQLite::subtract_sn_rewards;
|
||||
bool (BlockchainSQLite::*add_or_subtract)(const std::vector<cryptonote::batch_sn_payment>&) =
|
||||
add ? &BlockchainSQLite::add_sn_rewards : &BlockchainSQLite::subtract_sn_rewards;
|
||||
|
||||
// From here on we calculate everything in milli-atomic OXEN (i.e. thousanths of an atomic
|
||||
// OXEN) so that our integer math has minimal loss from integer division.
|
||||
if (block.reward > std::numeric_limits<uint64_t>::max() / BATCH_REWARD_FACTOR)
|
||||
throw std::logic_error{"Reward distribution amount is too large"};
|
||||
throw std::logic_error{"Reward distribution amount is too large"};
|
||||
|
||||
uint64_t block_reward = block.reward * BATCH_REWARD_FACTOR;
|
||||
uint64_t service_node_reward = cryptonote::service_node_reward_formula(0, block.major_version) * BATCH_REWARD_FACTOR;
|
||||
uint64_t service_node_reward =
|
||||
cryptonote::service_node_reward_formula(0, block.major_version) * BATCH_REWARD_FACTOR;
|
||||
|
||||
std::vector<cryptonote::batch_sn_payment> payments;
|
||||
|
||||
// Step 1: Pay out the block producer their tx fees (note that, unlike the below, this applies
|
||||
// even if the SN isn't currently payable).
|
||||
if (block_reward < service_node_reward && m_nettype != cryptonote::network_type::FAKECHAIN)
|
||||
throw std::logic_error{"Invalid payment: block reward is too small"};
|
||||
throw std::logic_error{"Invalid payment: block reward is too small"};
|
||||
|
||||
std::lock_guard a_s_lock{address_str_cache_mutex};
|
||||
|
||||
if (uint64_t tx_fees = block_reward - service_node_reward;
|
||||
tx_fees > 0
|
||||
&& block.service_node_winner_key // "service_node_winner_key" tracks the pulse winner; 0 if a mined block
|
||||
&& crypto_core_ed25519_is_valid_point(block.service_node_winner_key.data())
|
||||
) {
|
||||
tx_fees > 0 && block.service_node_winner_key // "service_node_winner_key" tracks the pulse
|
||||
// winner; 0 if a mined block
|
||||
&& crypto_core_ed25519_is_valid_point(block.service_node_winner_key.data())) {
|
||||
|
||||
if (auto service_node_winner = service_nodes_state.service_nodes_infos.find(block.service_node_winner_key);
|
||||
service_node_winner != service_nodes_state.service_nodes_infos.end()) {
|
||||
calculate_rewards(block.major_version, tx_fees, *service_node_winner->second, payments);
|
||||
// Takes the block producer and adds its contributors to the batching database for the transaction fees
|
||||
if (!(this->*add_or_subtract)(payments))
|
||||
return false;
|
||||
}
|
||||
if (auto service_node_winner =
|
||||
service_nodes_state.service_nodes_infos.find(block.service_node_winner_key);
|
||||
service_node_winner != service_nodes_state.service_nodes_infos.end()) {
|
||||
calculate_rewards(block.major_version, tx_fees, *service_node_winner->second, payments);
|
||||
// Takes the block producer and adds its contributors to the batching database for the
|
||||
// transaction fees
|
||||
if (!(this->*add_or_subtract)(payments))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto block_height = get_block_height(block);
|
||||
|
||||
// Step 2: Iterate over the whole service node list and pay each node 1/service_node_list fraction
|
||||
const auto payable_service_nodes = service_nodes_state.payable_service_nodes_infos(block_height, m_nettype);
|
||||
// Step 2: Iterate over the whole service node list and pay each node 1/service_node_list
|
||||
// fraction
|
||||
const auto payable_service_nodes =
|
||||
service_nodes_state.payable_service_nodes_infos(block_height, m_nettype);
|
||||
size_t total_service_nodes_payable = payable_service_nodes.size();
|
||||
for (const auto& [node_pubkey, node_info]: payable_service_nodes) {
|
||||
auto payable_service_node = service_nodes_state.service_nodes_infos.find(node_pubkey);
|
||||
if (payable_service_node == service_nodes_state.service_nodes_infos.end())
|
||||
continue;
|
||||
calculate_rewards(block.major_version, service_node_reward / total_service_nodes_payable, * payable_service_node -> second, payments);
|
||||
// Takes the node and adds its contributors to the batching database
|
||||
if (!(this->*add_or_subtract)(payments))
|
||||
return false;
|
||||
for (const auto& [node_pubkey, node_info] : payable_service_nodes) {
|
||||
auto payable_service_node = service_nodes_state.service_nodes_infos.find(node_pubkey);
|
||||
if (payable_service_node == service_nodes_state.service_nodes_infos.end())
|
||||
continue;
|
||||
calculate_rewards(
|
||||
block.major_version,
|
||||
service_node_reward / total_service_nodes_payable,
|
||||
*payable_service_node->second,
|
||||
payments);
|
||||
// Takes the node and adds its contributors to the batching database
|
||||
if (!(this->*add_or_subtract)(payments))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 3: Add Governance reward to the list
|
||||
if (m_nettype != cryptonote::network_type::FAKECHAIN) {
|
||||
if (parsed_governance_addr.first != block.major_version) {
|
||||
cryptonote::get_account_address_from_str(parsed_governance_addr.second, m_nettype,
|
||||
cryptonote::get_config(m_nettype).governance_wallet_address(block.major_version));
|
||||
parsed_governance_addr.first = block.major_version;
|
||||
}
|
||||
uint64_t foundation_reward = cryptonote::governance_reward_formula(block.major_version) * BATCH_REWARD_FACTOR;
|
||||
payments.clear();
|
||||
payments.emplace_back(parsed_governance_addr.second.address, foundation_reward);
|
||||
if (!(this->*add_or_subtract)(payments))
|
||||
return false;
|
||||
if (parsed_governance_addr.first != block.major_version) {
|
||||
cryptonote::get_account_address_from_str(
|
||||
parsed_governance_addr.second,
|
||||
m_nettype,
|
||||
cryptonote::get_config(m_nettype).governance_wallet_address(
|
||||
block.major_version));
|
||||
parsed_governance_addr.first = block.major_version;
|
||||
}
|
||||
uint64_t foundation_reward =
|
||||
cryptonote::governance_reward_formula(block.major_version) * BATCH_REWARD_FACTOR;
|
||||
payments.clear();
|
||||
payments.emplace_back(parsed_governance_addr.second.address, foundation_reward);
|
||||
if (!(this->*add_or_subtract)(payments))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockchainSQLite::add_block(const cryptonote::block& block,
|
||||
const service_nodes::service_node_list::state_t& service_nodes_state) {
|
||||
bool BlockchainSQLite::add_block(
|
||||
const cryptonote::block& block,
|
||||
const service_nodes::service_node_list::state_t& service_nodes_state) {
|
||||
auto block_height = get_block_height(block);
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{} called on height: {}", __func__, block_height);
|
||||
|
||||
auto hf_version = block.major_version;
|
||||
if (hf_version < hf::hf19_reward_batching) {
|
||||
update_height(block_height);
|
||||
return true;
|
||||
update_height(block_height);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto fork_height = cryptonote::get_hard_fork_heights(m_nettype, hf::hf19_reward_batching);
|
||||
if (block_height == fork_height.first.value_or(0)) {
|
||||
log::debug(logcat, "Batching of Service Node Rewards Begins");
|
||||
reset_database();
|
||||
update_height(block_height - 1);
|
||||
log::debug(logcat, "Batching of Service Node Rewards Begins");
|
||||
reset_database();
|
||||
update_height(block_height - 1);
|
||||
}
|
||||
|
||||
if (block_height != height + 1) {
|
||||
log::error(logcat, "Block height ({}) out of sync with batching database ({})", block_height, height);
|
||||
return false;
|
||||
log::error(
|
||||
logcat,
|
||||
"Block height ({}) out of sync with batching database ({})",
|
||||
block_height,
|
||||
height);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We query our own database as a source of truth to verify the blocks payments against. The calculated_rewards
|
||||
// variable contains a known good list of who should have been paid in this block
|
||||
// We query our own database as a source of truth to verify the blocks payments against. The
|
||||
// calculated_rewards variable contains a known good list of who should have been paid in this
|
||||
// block
|
||||
auto calculated_rewards = get_sn_payments(block_height);
|
||||
|
||||
// We iterate through the block's coinbase payments and build a copy of our own list of the payments
|
||||
// miner_tx_vouts this will be compared against calculated_rewards and if they match we know the block is
|
||||
// paying the correct people only.
|
||||
// We iterate through the block's coinbase payments and build a copy of our own list of the
|
||||
// payments miner_tx_vouts this will be compared against calculated_rewards and if they match we
|
||||
// know the block is paying the correct people only.
|
||||
std::vector<std::pair<crypto::public_key, uint64_t>> miner_tx_vouts;
|
||||
for (auto & vout: block.miner_tx.vout)
|
||||
miner_tx_vouts.emplace_back(var::get<txout_to_key>(vout.target).key, vout.amount);
|
||||
for (auto& vout : block.miner_tx.vout)
|
||||
miner_tx_vouts.emplace_back(var::get<txout_to_key>(vout.target).key, vout.amount);
|
||||
|
||||
try {
|
||||
SQLite::Transaction transaction {
|
||||
db,
|
||||
SQLite::TransactionBehavior::IMMEDIATE
|
||||
};
|
||||
SQLite::Transaction transaction{db, SQLite::TransactionBehavior::IMMEDIATE};
|
||||
|
||||
// Goes through the miner transactions vouts checks they are right and marks them as paid in the database
|
||||
if (!validate_batch_payment(miner_tx_vouts, calculated_rewards, block_height)) {
|
||||
return false;
|
||||
}
|
||||
// Goes through the miner transactions vouts checks they are right and marks them as paid in
|
||||
// the database
|
||||
if (!validate_batch_payment(miner_tx_vouts, calculated_rewards, block_height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!reward_handler(block, service_nodes_state, /*add=*/ true))
|
||||
return false;
|
||||
if (!reward_handler(block, service_nodes_state, /*add=*/true))
|
||||
return false;
|
||||
|
||||
increment_height();
|
||||
increment_height();
|
||||
|
||||
transaction.commit();
|
||||
transaction.commit();
|
||||
} catch (std::exception& e) {
|
||||
log::error(logcat, "Error adding reward payments: {}", e.what());
|
||||
return false;
|
||||
log::error(logcat, "Error adding reward payments: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockchainSQLite::pop_block(const cryptonote::block& block,
|
||||
const service_nodes::service_node_list::state_t& service_nodes_state) {
|
||||
bool BlockchainSQLite::pop_block(
|
||||
const cryptonote::block& block,
|
||||
const service_nodes::service_node_list::state_t& service_nodes_state) {
|
||||
auto block_height = get_block_height(block);
|
||||
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{} called on height: {}", __func__, block_height);
|
||||
if (height < block_height) {
|
||||
log::debug(logcat, "Block above batching DB height skipping pop");
|
||||
return true;
|
||||
log::debug(logcat, "Block above batching DB height skipping pop");
|
||||
return true;
|
||||
}
|
||||
if (block_height != height) {
|
||||
log::error(logcat, "Block height out of sync with batching database");
|
||||
return false;
|
||||
log::error(logcat, "Block height out of sync with batching database");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& conf = get_config(m_nettype);
|
||||
auto hf_version = block.major_version;
|
||||
if (hf_version < hf::hf19_reward_batching) {
|
||||
decrement_height();
|
||||
return true;
|
||||
decrement_height();
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
SQLite::Transaction transaction {
|
||||
db,
|
||||
SQLite::TransactionBehavior::IMMEDIATE
|
||||
};
|
||||
SQLite::Transaction transaction{db, SQLite::TransactionBehavior::IMMEDIATE};
|
||||
|
||||
if (!reward_handler(block, service_nodes_state, /*add=*/false))
|
||||
return false;
|
||||
|
||||
if (!reward_handler(block, service_nodes_state, /*add=*/ false))
|
||||
return false;
|
||||
// Add back to the database payments that had been made in this block
|
||||
delete_block_payments(block_height);
|
||||
|
||||
// Add back to the database payments that had been made in this block
|
||||
delete_block_payments(block_height);
|
||||
|
||||
decrement_height();
|
||||
transaction.commit();
|
||||
decrement_height();
|
||||
transaction.commit();
|
||||
} catch (std::exception& e) {
|
||||
log::error(logcat, "Error subtracting reward payments: {}", e.what());
|
||||
return false;
|
||||
log::error(logcat, "Error subtracting reward payments: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockchainSQLite::validate_batch_payment(
|
||||
const std::vector<std::pair<crypto::public_key, uint64_t>>& miner_tx_vouts,
|
||||
const std::vector<cryptonote::batch_sn_payment>& calculated_payments_from_batching_db,
|
||||
uint64_t block_height) {
|
||||
bool BlockchainSQLite::validate_batch_payment(
|
||||
const std::vector<std::pair<crypto::public_key, uint64_t>>& miner_tx_vouts,
|
||||
const std::vector<cryptonote::batch_sn_payment>& calculated_payments_from_batching_db,
|
||||
uint64_t block_height) {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{}", __func__);
|
||||
|
||||
if (miner_tx_vouts.size() != calculated_payments_from_batching_db.size()) {
|
||||
log::error(logcat, "Length of batch payments ({}) does not match block vouts ({})", calculated_payments_from_batching_db.size(), miner_tx_vouts.size());
|
||||
return false;
|
||||
log::error(
|
||||
logcat,
|
||||
"Length of batch payments ({}) does not match block vouts ({})",
|
||||
calculated_payments_from_batching_db.size(),
|
||||
miner_tx_vouts.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
int8_t vout_index = 0;
|
||||
uint64_t total_oxen_payout_in_our_db = std::accumulate(
|
||||
calculated_payments_from_batching_db.begin(),
|
||||
calculated_payments_from_batching_db.end(),
|
||||
uint64_t(0),
|
||||
[](auto&& a, auto&& b) { return a + b.amount; });
|
||||
calculated_payments_from_batching_db.begin(),
|
||||
calculated_payments_from_batching_db.end(),
|
||||
uint64_t(0),
|
||||
[](auto&& a, auto&& b) { return a + b.amount; });
|
||||
uint64_t total_oxen_payout_in_vouts = 0;
|
||||
std::vector<batch_sn_payment> finalised_payments;
|
||||
cryptonote::keypair
|
||||
const deterministic_keypair = cryptonote::get_deterministic_keypair_from_height(block_height);
|
||||
cryptonote::keypair const deterministic_keypair =
|
||||
cryptonote::get_deterministic_keypair_from_height(block_height);
|
||||
for (size_t vout_index = 0; vout_index < miner_tx_vouts.size(); vout_index++) {
|
||||
const auto& [pubkey, amt] = miner_tx_vouts[vout_index];
|
||||
uint64_t amount = amt * BATCH_REWARD_FACTOR;
|
||||
const auto& from_db = calculated_payments_from_batching_db[vout_index];
|
||||
if (amount != from_db.amount) {
|
||||
log::error(logcat, "Batched payout amount incorrect. Should be {}, not {}", from_db.amount, amount);
|
||||
return false;
|
||||
}
|
||||
crypto::public_key out_eph_public_key{};
|
||||
if (!cryptonote::get_deterministic_output_key(from_db.address_info.address, deterministic_keypair, vout_index, out_eph_public_key)) {
|
||||
log::error(logcat, "Failed to generate output one-time public key");
|
||||
return false;
|
||||
}
|
||||
if (tools::view_guts(pubkey) != tools::view_guts(out_eph_public_key)) {
|
||||
log::error(logcat, "Output ephemeral public key does not match");
|
||||
return false;
|
||||
}
|
||||
total_oxen_payout_in_vouts += amount;
|
||||
finalised_payments.emplace_back(from_db.address_info, amount);
|
||||
const auto& [pubkey, amt] = miner_tx_vouts[vout_index];
|
||||
uint64_t amount = amt * BATCH_REWARD_FACTOR;
|
||||
const auto& from_db = calculated_payments_from_batching_db[vout_index];
|
||||
if (amount != from_db.amount) {
|
||||
log::error(
|
||||
logcat,
|
||||
"Batched payout amount incorrect. Should be {}, not {}",
|
||||
from_db.amount,
|
||||
amount);
|
||||
return false;
|
||||
}
|
||||
crypto::public_key out_eph_public_key{};
|
||||
if (!cryptonote::get_deterministic_output_key(
|
||||
from_db.address_info.address,
|
||||
deterministic_keypair,
|
||||
vout_index,
|
||||
out_eph_public_key)) {
|
||||
log::error(logcat, "Failed to generate output one-time public key");
|
||||
return false;
|
||||
}
|
||||
if (tools::view_guts(pubkey) != tools::view_guts(out_eph_public_key)) {
|
||||
log::error(logcat, "Output ephemeral public key does not match");
|
||||
return false;
|
||||
}
|
||||
total_oxen_payout_in_vouts += amount;
|
||||
finalised_payments.emplace_back(from_db.address_info, amount);
|
||||
}
|
||||
if (total_oxen_payout_in_vouts != total_oxen_payout_in_our_db) {
|
||||
log::error(logcat, "Total batched payout amount incorrect. Should be {}, not {}", total_oxen_payout_in_our_db, total_oxen_payout_in_vouts);
|
||||
return false;
|
||||
log::error(
|
||||
logcat,
|
||||
"Total batched payout amount incorrect. Should be {}, not {}",
|
||||
total_oxen_payout_in_our_db,
|
||||
total_oxen_payout_in_vouts);
|
||||
return false;
|
||||
}
|
||||
|
||||
return save_payments(block_height, finalised_payments);
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockchainSQLite::save_payments(uint64_t block_height, const std::vector<batch_sn_payment>& paid_amounts) {
|
||||
bool BlockchainSQLite::save_payments(
|
||||
uint64_t block_height, const std::vector<batch_sn_payment>& paid_amounts) {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{}", __func__);
|
||||
|
||||
auto select_sum = prepared_st(
|
||||
"SELECT amount from batched_payments_accrued WHERE address = ?");
|
||||
auto select_sum = prepared_st("SELECT amount from batched_payments_accrued WHERE address = ?");
|
||||
|
||||
auto update_paid = prepared_st(
|
||||
"INSERT INTO batched_payments_paid (address, amount, height_paid) VALUES (?,?,?)");
|
||||
"INSERT INTO batched_payments_paid (address, amount, height_paid) VALUES (?,?,?)");
|
||||
|
||||
std::lock_guard a_s_lock{address_str_cache_mutex};
|
||||
|
||||
for (const auto& payment: paid_amounts) {
|
||||
const auto& address_str = get_address_str(payment.address_info.address);
|
||||
if (auto maybe_amount = db::exec_and_maybe_get<int64_t>(select_sum, address_str))
|
||||
{
|
||||
// Truncate the thousanths amount to an atomic OXEN:
|
||||
auto amount = static_cast<uint64_t>(*maybe_amount) / BATCH_REWARD_FACTOR * BATCH_REWARD_FACTOR;
|
||||
for (const auto& payment : paid_amounts) {
|
||||
const auto& address_str = get_address_str(payment.address_info.address);
|
||||
if (auto maybe_amount = db::exec_and_maybe_get<int64_t>(select_sum, address_str)) {
|
||||
// Truncate the thousanths amount to an atomic OXEN:
|
||||
auto amount = static_cast<uint64_t>(*maybe_amount) / BATCH_REWARD_FACTOR *
|
||||
BATCH_REWARD_FACTOR;
|
||||
|
||||
if (amount != payment.amount) {
|
||||
log::error(logcat, "Invalid amounts passed in to save payments for address {}: received {}, expected {} (truncated from {})", address_str, payment.amount, amount, *maybe_amount);
|
||||
return false;
|
||||
if (amount != payment.amount) {
|
||||
log::error(
|
||||
logcat,
|
||||
"Invalid amounts passed in to save payments for address {}: received {}, "
|
||||
"expected {} (truncated from {})",
|
||||
address_str,
|
||||
payment.amount,
|
||||
amount,
|
||||
*maybe_amount);
|
||||
return false;
|
||||
}
|
||||
|
||||
db::exec_query(
|
||||
update_paid,
|
||||
address_str,
|
||||
static_cast<int64_t>(amount),
|
||||
static_cast<int64_t>(block_height));
|
||||
update_paid->reset();
|
||||
} else {
|
||||
// This shouldn't occur: we validate payout addresses much earlier in the block
|
||||
// validation.
|
||||
log::error(
|
||||
logcat,
|
||||
"Internal error: Invalid amounts passed in to save payments for address {}: "
|
||||
"that address has no accrued rewards",
|
||||
address_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
db::exec_query(update_paid, address_str, static_cast<int64_t>(amount), static_cast<int64_t>(block_height));
|
||||
update_paid->reset();
|
||||
}
|
||||
else {
|
||||
// This shouldn't occur: we validate payout addresses much earlier in the block validation.
|
||||
log::error(logcat, "Internal error: Invalid amounts passed in to save payments for address {}: that address has no accrued rewards", address_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
select_sum->reset();
|
||||
select_sum->reset();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<cryptonote::batch_sn_payment> BlockchainSQLite::get_block_payments(uint64_t block_height) {
|
||||
std::vector<cryptonote::batch_sn_payment> BlockchainSQLite::get_block_payments(
|
||||
uint64_t block_height) {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{} Called with height: {}", __func__, block_height);
|
||||
|
||||
std::vector<cryptonote::batch_sn_payment> payments_at_height;
|
||||
auto paid = prepared_results<std::string_view, int64_t>(
|
||||
"SELECT address, amount FROM batched_payments_paid WHERE height_paid = ? ORDER BY address",
|
||||
static_cast<int64_t>(block_height));
|
||||
"SELECT address, amount FROM batched_payments_paid WHERE height_paid = ? ORDER BY "
|
||||
"address",
|
||||
static_cast<int64_t>(block_height));
|
||||
|
||||
for (auto [addr, amt] : paid) {
|
||||
auto& p = payments_at_height.emplace_back();
|
||||
p.amount = static_cast<uint64_t>(amt);
|
||||
cryptonote::get_account_address_from_str(p.address_info, m_nettype, addr);
|
||||
auto& p = payments_at_height.emplace_back();
|
||||
p.amount = static_cast<uint64_t>(amt);
|
||||
cryptonote::get_account_address_from_str(p.address_info, m_nettype, addr);
|
||||
}
|
||||
|
||||
return payments_at_height;
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockchainSQLite::delete_block_payments(uint64_t block_height) {
|
||||
bool BlockchainSQLite::delete_block_payments(uint64_t block_height) {
|
||||
log::trace(logcat, "BlockchainDB_SQLITE::{} Called with height: {}", __func__, block_height);
|
||||
prepared_exec(
|
||||
"DELETE FROM batched_payments_paid WHERE height_paid >= ?",
|
||||
static_cast<int64_t>(block_height));
|
||||
"DELETE FROM batched_payments_paid WHERE height_paid >= ?",
|
||||
static_cast<int64_t>(block_height));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cryptonote
|
||||
} // namespace cryptonote
|
||||
|
|
|
@ -27,103 +27,116 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <SQLiteCpp/SQLiteCpp.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include "common/fs.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "cryptonote_core/cryptonote_tx_utils.h"
|
||||
#include "sqlitedb/database.hpp"
|
||||
#include "common/fs.h"
|
||||
|
||||
#include <SQLiteCpp/SQLiteCpp.h>
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
namespace cryptonote {
|
||||
|
||||
fs::path check_if_copy_filename(std::string_view db_path);
|
||||
|
||||
class BlockchainSQLite : public db::Database
|
||||
{
|
||||
public:
|
||||
explicit BlockchainSQLite(cryptonote::network_type nettype, fs::path db_path);
|
||||
BlockchainSQLite(const BlockchainSQLite&) = delete;
|
||||
class BlockchainSQLite : public db::Database {
|
||||
public:
|
||||
explicit BlockchainSQLite(cryptonote::network_type nettype, fs::path db_path);
|
||||
BlockchainSQLite(const BlockchainSQLite&) = delete;
|
||||
|
||||
// Database management functions. Should be called on creation of BlockchainSQLite
|
||||
void create_schema();
|
||||
void upgrade_schema();
|
||||
void reset_database();
|
||||
// Database management functions. Should be called on creation of BlockchainSQLite
|
||||
void create_schema();
|
||||
void upgrade_schema();
|
||||
void reset_database();
|
||||
|
||||
// The batching database maintains a height variable to know if it gets out of sync with the mainchain. Calling increment and decrement is the primary method of interacting with this height variable
|
||||
void update_height(uint64_t new_height);
|
||||
void increment_height();
|
||||
void decrement_height();
|
||||
// The batching database maintains a height variable to know if it gets out of sync with the
|
||||
// mainchain. Calling increment and decrement is the primary method of interacting with this
|
||||
// height variable
|
||||
void update_height(uint64_t new_height);
|
||||
void increment_height();
|
||||
void decrement_height();
|
||||
|
||||
void blockchain_detached(uint64_t new_height);
|
||||
void blockchain_detached(uint64_t new_height);
|
||||
|
||||
// add_sn_payments/subtract_sn_payments -> passing an array of addresses and amounts. These will be added or subtracted to the database for each address specified. If the address does not exist it will be created.
|
||||
bool add_sn_rewards(const std::vector<cryptonote::batch_sn_payment>& payments);
|
||||
bool subtract_sn_rewards(const std::vector<cryptonote::batch_sn_payment>& payments);
|
||||
// add_sn_payments/subtract_sn_payments -> passing an array of addresses and amounts. These will
|
||||
// be added or subtracted to the database for each address specified. If the address does not
|
||||
// exist it will be created.
|
||||
bool add_sn_rewards(const std::vector<cryptonote::batch_sn_payment>& payments);
|
||||
bool subtract_sn_rewards(const std::vector<cryptonote::batch_sn_payment>& payments);
|
||||
|
||||
private:
|
||||
bool reward_handler(
|
||||
const cryptonote::block& block,
|
||||
const service_nodes::service_node_list::state_t& service_nodes_state,
|
||||
bool add);
|
||||
private:
|
||||
bool reward_handler(
|
||||
const cryptonote::block& block,
|
||||
const service_nodes::service_node_list::state_t& service_nodes_state,
|
||||
bool add);
|
||||
|
||||
std::unordered_map<account_public_address, std::string> address_str_cache;
|
||||
std::pair<hf, cryptonote::address_parse_info> parsed_governance_addr = {hf::none, {}};
|
||||
const std::string& get_address_str(const account_public_address& addr);
|
||||
std::mutex address_str_cache_mutex;
|
||||
std::unordered_map<account_public_address, std::string> address_str_cache;
|
||||
std::pair<hf, cryptonote::address_parse_info> parsed_governance_addr = {hf::none, {}};
|
||||
const std::string& get_address_str(const account_public_address& addr);
|
||||
std::mutex address_str_cache_mutex;
|
||||
|
||||
public:
|
||||
public:
|
||||
// get_accrued_earnings -> queries the database for the amount that has been accrued to
|
||||
// `service_node_address` will return the atomic value in oxen that the service node is owed.
|
||||
uint64_t get_accrued_earnings(const std::string& address);
|
||||
// get_all_accrued_earnings -> queries the database for all the amount that has been accrued to
|
||||
// service nodes will return 2 vectors corresponding to the addresses and the atomic value in
|
||||
// oxen that the service nodes are owed.
|
||||
std::pair<std::vector<std::string>, std::vector<uint64_t>> get_all_accrued_earnings();
|
||||
|
||||
// get_accrued_earnings -> queries the database for the amount that has been accrued to `service_node_address` will return the atomic value in oxen that
|
||||
// the service node is owed.
|
||||
uint64_t get_accrued_earnings(const std::string& address);
|
||||
// get_all_accrued_earnings -> queries the database for all the amount that has been accrued to service nodes will return
|
||||
// 2 vectors corresponding to the addresses and the atomic value in oxen that the service nodes are owed.
|
||||
std::pair<std::vector<std::string>, std::vector<uint64_t>> get_all_accrued_earnings();
|
||||
// get_payments -> passing a block height will return an array of payments that should be
|
||||
// created in a coinbase transaction on that block given the current batching DB state.
|
||||
std::vector<cryptonote::batch_sn_payment> get_sn_payments(uint64_t block_height);
|
||||
|
||||
// get_payments -> passing a block height will return an array of payments that should be created in a coinbase transaction on that block given the current batching DB state.
|
||||
std::vector<cryptonote::batch_sn_payment> get_sn_payments(uint64_t block_height);
|
||||
// calculate_rewards -> takes the list of contributors from sn_info with their SN contribution
|
||||
// amounts and will calculate how much of the block rewards should be the allocated to the
|
||||
// contributors. The function will set a list suitable for passing to add_sn_payments into the
|
||||
// vector (any existing values will be cleared).
|
||||
//
|
||||
// Note that distribution_amount here is typically passed as milli-atomic OXEN for extra
|
||||
// precision.
|
||||
void calculate_rewards(
|
||||
hf hf_version,
|
||||
uint64_t distribution_amount,
|
||||
const service_nodes::service_node_info& sn_info,
|
||||
std::vector<cryptonote::batch_sn_payment>& rewards);
|
||||
|
||||
// calculate_rewards -> takes the list of contributors from sn_info with their SN contribution
|
||||
// amounts and will calculate how much of the block rewards should be the allocated to the
|
||||
// contributors. The function will set a list suitable for passing to add_sn_payments into the
|
||||
// vector (any existing values will be cleared).
|
||||
//
|
||||
// Note that distribution_amount here is typically passed as milli-atomic OXEN for extra
|
||||
// precision.
|
||||
void calculate_rewards(hf hf_version, uint64_t distribution_amount, const service_nodes::service_node_info& sn_info, std::vector<cryptonote::batch_sn_payment>& rewards);
|
||||
// add/pop_block -> takes a block that contains new block rewards to be batched and added to the
|
||||
// database and/or batching payments that need to be subtracted from the database, in addition
|
||||
// it takes a reference to the service node state which it will use to calculate the individual
|
||||
// payouts. The function will then process this block add and subtracting to the batching DB
|
||||
// appropriately. This is the primary entry point for the blockchain to add to the batching
|
||||
// database. Each accepted block should call this passing in the SN list structure.
|
||||
bool add_block(
|
||||
const cryptonote::block& block,
|
||||
const service_nodes::service_node_list::state_t& service_nodes_state);
|
||||
bool pop_block(
|
||||
const cryptonote::block& block,
|
||||
const service_nodes::service_node_list::state_t& service_nodes_state);
|
||||
|
||||
// add/pop_block -> takes a block that contains new block rewards to be batched and added to the database
|
||||
// and/or batching payments that need to be subtracted from the database, in addition it takes a reference to
|
||||
// the service node state which it will use to calculate the individual payouts.
|
||||
// The function will then process this block add and subtracting to the batching DB appropriately.
|
||||
// This is the primary entry point for the blockchain to add to the batching database.
|
||||
// Each accepted block should call this passing in the SN list structure.
|
||||
bool add_block(const cryptonote::block& block, const service_nodes::service_node_list::state_t& service_nodes_state);
|
||||
bool pop_block(const cryptonote::block& block, const service_nodes::service_node_list::state_t& service_nodes_state);
|
||||
// validate_batch_payment -> used to make sure that list of miner_tx_vouts is correct. Compares
|
||||
// the miner_tx_vouts with a list previously extracted payments to make sure that the correct
|
||||
// persons are being paid.
|
||||
bool validate_batch_payment(
|
||||
const std::vector<std::pair<crypto::public_key, uint64_t>>& miner_tx_vouts,
|
||||
const std::vector<cryptonote::batch_sn_payment>& calculated_payments_from_batching_db,
|
||||
uint64_t block_height);
|
||||
|
||||
// validate_batch_payment -> used to make sure that list of miner_tx_vouts is correct. Compares the miner_tx_vouts with a list previously extracted payments to make sure that the correct persons are being paid.
|
||||
bool validate_batch_payment(
|
||||
const std::vector<std::pair<crypto::public_key, uint64_t>>& miner_tx_vouts,
|
||||
const std::vector<cryptonote::batch_sn_payment>& calculated_payments_from_batching_db,
|
||||
uint64_t block_height);
|
||||
|
||||
// these keep track of payments made to SN operators after then payment has been made. Allows for popping blocks back and knowing who got paid in those blocks.
|
||||
// passing in a list of people to be marked as paid in the paid_amounts vector. Block height will be added to the batched_payments_paid database as height_paid.
|
||||
bool save_payments(uint64_t block_height, const std::vector<batch_sn_payment>& paid_amounts);
|
||||
std::vector<cryptonote::batch_sn_payment> get_block_payments(uint64_t block_height);
|
||||
bool delete_block_payments(uint64_t block_height);
|
||||
// these keep track of payments made to SN operators after then payment has been made. Allows
|
||||
// for popping blocks back and knowing who got paid in those blocks. passing in a list of people
|
||||
// to be marked as paid in the paid_amounts vector. Block height will be added to the
|
||||
// batched_payments_paid database as height_paid.
|
||||
bool save_payments(uint64_t block_height, const std::vector<batch_sn_payment>& paid_amounts);
|
||||
std::vector<cryptonote::batch_sn_payment> get_block_payments(uint64_t block_height);
|
||||
bool delete_block_payments(uint64_t block_height);
|
||||
|
||||
uint64_t height;
|
||||
|
||||
protected:
|
||||
|
||||
cryptonote::network_type m_nettype;
|
||||
std::string filename;
|
||||
uint64_t height;
|
||||
|
||||
protected:
|
||||
cryptonote::network_type m_nettype;
|
||||
std::string filename;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace cryptonote
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -25,160 +25,351 @@
|
|||
// 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.
|
||||
//
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "blockchain_db.h"
|
||||
#include "cryptonote_core/service_node_list.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "cryptonote_core/service_node_list.h"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
struct checkpoint_t;
|
||||
class BaseTestDB: public cryptonote::BlockchainDB {
|
||||
public:
|
||||
BaseTestDB() {}
|
||||
virtual void open(const fs::path& filename, network_type nettype = network_type::FAKECHAIN, const int db_flags = 0) override { }
|
||||
virtual void close() override {}
|
||||
virtual void sync() override {}
|
||||
virtual void safesyncmode(const bool onoff) override {}
|
||||
virtual void reset() override {}
|
||||
virtual std::vector<fs::path> get_filenames() const override { return {}; }
|
||||
virtual bool remove_data_file(const fs::path& folder) const override { return true; }
|
||||
virtual std::string get_db_name() const override { return std::string(); }
|
||||
virtual void lock() override { }
|
||||
virtual bool try_lock() override { return true; }
|
||||
virtual void unlock() override { }
|
||||
virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) override { return true; }
|
||||
virtual void batch_stop() override {}
|
||||
virtual void batch_abort() override {}
|
||||
virtual void set_batch_transactions(bool) override {}
|
||||
virtual void block_wtxn_start() override {}
|
||||
virtual void block_wtxn_stop() override {}
|
||||
virtual void block_wtxn_abort() override {}
|
||||
virtual bool block_rtxn_start() const override { return true; }
|
||||
virtual void block_rtxn_stop() const override {}
|
||||
virtual void block_rtxn_abort() const override {}
|
||||
namespace cryptonote {
|
||||
struct checkpoint_t;
|
||||
class BaseTestDB : public cryptonote::BlockchainDB {
|
||||
public:
|
||||
BaseTestDB() {}
|
||||
virtual void open(
|
||||
const fs::path& filename,
|
||||
network_type nettype = network_type::FAKECHAIN,
|
||||
const int db_flags = 0) override {}
|
||||
virtual void close() override {}
|
||||
virtual void sync() override {}
|
||||
virtual void safesyncmode(const bool onoff) override {}
|
||||
virtual void reset() override {}
|
||||
virtual std::vector<fs::path> get_filenames() const override { return {}; }
|
||||
virtual bool remove_data_file(const fs::path& folder) const override { return true; }
|
||||
virtual std::string get_db_name() const override { return std::string(); }
|
||||
virtual void lock() override {}
|
||||
virtual bool try_lock() override { return true; }
|
||||
virtual void unlock() override {}
|
||||
virtual bool batch_start(uint64_t batch_num_blocks = 0, uint64_t batch_bytes = 0) override {
|
||||
return true;
|
||||
}
|
||||
virtual void batch_stop() override {}
|
||||
virtual void batch_abort() override {}
|
||||
virtual void set_batch_transactions(bool) override {}
|
||||
virtual void block_wtxn_start() override {}
|
||||
virtual void block_wtxn_stop() override {}
|
||||
virtual void block_wtxn_abort() override {}
|
||||
virtual bool block_rtxn_start() const override { return true; }
|
||||
virtual void block_rtxn_stop() const override {}
|
||||
virtual void block_rtxn_abort() const override {}
|
||||
|
||||
virtual bool block_exists(const crypto::hash& h, uint64_t *height) const override { return false; }
|
||||
virtual std::string get_block_blob_from_height(uint64_t height) const override { return cryptonote::t_serializable_object_to_blob(get_block_from_height(height)); }
|
||||
virtual std::string get_block_blob(const crypto::hash& h) const override { return std::string(); }
|
||||
virtual cryptonote::block_header get_block_header_from_height(uint64_t height) const override { return get_block_from_height(height); }
|
||||
virtual bool get_tx_blob(const crypto::hash& h, std::string &tx) const override { return false; }
|
||||
virtual bool get_pruned_tx_blob(const crypto::hash& h, std::string &tx) const override { return false; }
|
||||
virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<std::string> &bd) const override { return false; }
|
||||
virtual bool get_prunable_tx_blob(const crypto::hash& h, std::string &tx) const override { return false; }
|
||||
virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const override { return false; }
|
||||
virtual uint64_t get_block_height(const crypto::hash& h) const override { return 0; }
|
||||
virtual uint64_t get_block_timestamp(const uint64_t& height) const override { return 0; }
|
||||
virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const override { return {}; }
|
||||
virtual uint64_t get_top_block_timestamp() const override { return 0; }
|
||||
virtual size_t get_block_weight(const uint64_t& height) const override { return 128; }
|
||||
virtual std::vector<uint64_t> get_block_weights(uint64_t start_height, size_t count) const override { return {}; }
|
||||
virtual cryptonote::difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const override { return 10; }
|
||||
virtual cryptonote::difficulty_type get_block_difficulty(const uint64_t& height) const override { return 0; }
|
||||
virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const override { return 10000000000; }
|
||||
virtual uint64_t get_block_long_term_weight(const uint64_t& height) const override { return 128; }
|
||||
virtual std::vector<uint64_t> get_long_term_block_weights(uint64_t start_height, size_t count) const override { return {}; }
|
||||
virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const override { return crypto::hash(); }
|
||||
virtual std::vector<cryptonote::block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const override { return std::vector<cryptonote::block>(); }
|
||||
virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const override { return std::vector<crypto::hash>(); }
|
||||
virtual crypto::hash top_block_hash(uint64_t *block_height = NULL) const override { if (block_height) *block_height = 0; return crypto::hash(); }
|
||||
virtual cryptonote::block get_top_block() const override { return cryptonote::block(); }
|
||||
virtual uint64_t height() const override { return 1; }
|
||||
virtual bool tx_exists(const crypto::hash& h) const override { return false; }
|
||||
virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const override { return false; }
|
||||
virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const override { return 0; }
|
||||
virtual cryptonote::transaction get_tx(const crypto::hash& h) const override { return cryptonote::transaction(); }
|
||||
virtual bool get_tx(const crypto::hash& h, cryptonote::transaction &tx) const override { return false; }
|
||||
virtual uint64_t get_tx_count() const override { return 0; }
|
||||
virtual std::vector<cryptonote::transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const override { return std::vector<cryptonote::transaction>(); }
|
||||
virtual std::vector<uint64_t> get_tx_block_heights(const std::vector<crypto::hash>& h) const override { return {h.size(), 0}; }
|
||||
virtual uint64_t get_num_outputs(const uint64_t& amount) const override { return 1; }
|
||||
virtual uint64_t get_indexing_base() const override { return 0; }
|
||||
virtual cryptonote::output_data_t get_output_key(const uint64_t& amount, const uint64_t& index, bool include_commitmemt) const override { return cryptonote::output_data_t(); }
|
||||
virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const override { return cryptonote::tx_out_index(); }
|
||||
virtual cryptonote::tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const override { return cryptonote::tx_out_index(); }
|
||||
virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<cryptonote::tx_out_index> &indices) const override {}
|
||||
virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<cryptonote::output_data_t> &outputs, bool allow_partial = false) const override {}
|
||||
virtual bool can_thread_bulk_indices() const override { return false; }
|
||||
virtual std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(const uint64_t tx_index, size_t n_txes) const override { return std::vector<std::vector<uint64_t>>(); }
|
||||
virtual bool has_key_image(const crypto::key_image& img) const override { return false; }
|
||||
virtual void remove_block() override { }
|
||||
virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const std::pair<cryptonote::transaction, std::string>& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) override {return 0;}
|
||||
virtual void remove_transaction_data(const crypto::hash& tx_hash, const cryptonote::transaction& tx) override {}
|
||||
virtual uint64_t add_output(const crypto::hash& tx_hash, const cryptonote::tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) override {return 0;}
|
||||
virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector<uint64_t>& amount_output_indices) override {}
|
||||
virtual void add_spent_key(const crypto::key_image& k_image) override {}
|
||||
virtual void remove_spent_key(const crypto::key_image& k_image) override {}
|
||||
virtual bool block_exists(const crypto::hash& h, uint64_t* height) const override {
|
||||
return false;
|
||||
}
|
||||
virtual std::string get_block_blob_from_height(uint64_t height) const override {
|
||||
return cryptonote::t_serializable_object_to_blob(get_block_from_height(height));
|
||||
}
|
||||
virtual std::string get_block_blob(const crypto::hash& h) const override {
|
||||
return std::string();
|
||||
}
|
||||
virtual cryptonote::block_header get_block_header_from_height(uint64_t height) const override {
|
||||
return get_block_from_height(height);
|
||||
}
|
||||
virtual bool get_tx_blob(const crypto::hash& h, std::string& tx) const override {
|
||||
return false;
|
||||
}
|
||||
virtual bool get_pruned_tx_blob(const crypto::hash& h, std::string& tx) const override {
|
||||
return false;
|
||||
}
|
||||
virtual bool get_pruned_tx_blobs_from(
|
||||
const crypto::hash& h, size_t count, std::vector<std::string>& bd) const override {
|
||||
return false;
|
||||
}
|
||||
virtual bool get_prunable_tx_blob(const crypto::hash& h, std::string& tx) const override {
|
||||
return false;
|
||||
}
|
||||
virtual bool get_prunable_tx_hash(
|
||||
const crypto::hash& tx_hash, crypto::hash& prunable_hash) const override {
|
||||
return false;
|
||||
}
|
||||
virtual uint64_t get_block_height(const crypto::hash& h) const override { return 0; }
|
||||
virtual uint64_t get_block_timestamp(const uint64_t& height) const override { return 0; }
|
||||
virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(
|
||||
const std::vector<uint64_t>& heights) const override {
|
||||
return {};
|
||||
}
|
||||
virtual uint64_t get_top_block_timestamp() const override { return 0; }
|
||||
virtual size_t get_block_weight(const uint64_t& height) const override { return 128; }
|
||||
virtual std::vector<uint64_t> get_block_weights(
|
||||
uint64_t start_height, size_t count) const override {
|
||||
return {};
|
||||
}
|
||||
virtual cryptonote::difficulty_type get_block_cumulative_difficulty(
|
||||
const uint64_t& height) const override {
|
||||
return 10;
|
||||
}
|
||||
virtual cryptonote::difficulty_type get_block_difficulty(
|
||||
const uint64_t& height) const override {
|
||||
return 0;
|
||||
}
|
||||
virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const override {
|
||||
return 10000000000;
|
||||
}
|
||||
virtual uint64_t get_block_long_term_weight(const uint64_t& height) const override {
|
||||
return 128;
|
||||
}
|
||||
virtual std::vector<uint64_t> get_long_term_block_weights(
|
||||
uint64_t start_height, size_t count) const override {
|
||||
return {};
|
||||
}
|
||||
virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const override {
|
||||
return crypto::hash();
|
||||
}
|
||||
virtual std::vector<cryptonote::block> get_blocks_range(
|
||||
const uint64_t& h1, const uint64_t& h2) const override {
|
||||
return std::vector<cryptonote::block>();
|
||||
}
|
||||
virtual std::vector<crypto::hash> get_hashes_range(
|
||||
const uint64_t& h1, const uint64_t& h2) const override {
|
||||
return std::vector<crypto::hash>();
|
||||
}
|
||||
virtual crypto::hash top_block_hash(uint64_t* block_height = NULL) const override {
|
||||
if (block_height)
|
||||
*block_height = 0;
|
||||
return crypto::hash();
|
||||
}
|
||||
virtual cryptonote::block get_top_block() const override { return cryptonote::block(); }
|
||||
virtual uint64_t height() const override { return 1; }
|
||||
virtual bool tx_exists(const crypto::hash& h) const override { return false; }
|
||||
virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const override {
|
||||
return false;
|
||||
}
|
||||
virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const override { return 0; }
|
||||
virtual cryptonote::transaction get_tx(const crypto::hash& h) const override {
|
||||
return cryptonote::transaction();
|
||||
}
|
||||
virtual bool get_tx(const crypto::hash& h, cryptonote::transaction& tx) const override {
|
||||
return false;
|
||||
}
|
||||
virtual uint64_t get_tx_count() const override { return 0; }
|
||||
virtual std::vector<cryptonote::transaction> get_tx_list(
|
||||
const std::vector<crypto::hash>& hlist) const override {
|
||||
return std::vector<cryptonote::transaction>();
|
||||
}
|
||||
virtual std::vector<uint64_t> get_tx_block_heights(
|
||||
const std::vector<crypto::hash>& h) const override {
|
||||
return {h.size(), 0};
|
||||
}
|
||||
virtual uint64_t get_num_outputs(const uint64_t& amount) const override { return 1; }
|
||||
virtual uint64_t get_indexing_base() const override { return 0; }
|
||||
virtual cryptonote::output_data_t get_output_key(
|
||||
const uint64_t& amount, const uint64_t& index, bool include_commitmemt) const override {
|
||||
return cryptonote::output_data_t();
|
||||
}
|
||||
virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(
|
||||
const uint64_t& index) const override {
|
||||
return cryptonote::tx_out_index();
|
||||
}
|
||||
virtual cryptonote::tx_out_index get_output_tx_and_index(
|
||||
const uint64_t& amount, const uint64_t& index) const override {
|
||||
return cryptonote::tx_out_index();
|
||||
}
|
||||
virtual void get_output_tx_and_index(
|
||||
const uint64_t& amount,
|
||||
const std::vector<uint64_t>& offsets,
|
||||
std::vector<cryptonote::tx_out_index>& indices) const override {}
|
||||
virtual void get_output_key(
|
||||
const epee::span<const uint64_t>& amounts,
|
||||
const std::vector<uint64_t>& offsets,
|
||||
std::vector<cryptonote::output_data_t>& outputs,
|
||||
bool allow_partial = false) const override {}
|
||||
virtual bool can_thread_bulk_indices() const override { return false; }
|
||||
virtual std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(
|
||||
const uint64_t tx_index, size_t n_txes) const override {
|
||||
return std::vector<std::vector<uint64_t>>();
|
||||
}
|
||||
virtual bool has_key_image(const crypto::key_image& img) const override { return false; }
|
||||
virtual void remove_block() override {}
|
||||
virtual uint64_t add_transaction_data(
|
||||
const crypto::hash& blk_hash,
|
||||
const std::pair<cryptonote::transaction, std::string>& tx,
|
||||
const crypto::hash& tx_hash,
|
||||
const crypto::hash& tx_prunable_hash) override {
|
||||
return 0;
|
||||
}
|
||||
virtual void remove_transaction_data(
|
||||
const crypto::hash& tx_hash, const cryptonote::transaction& tx) override {}
|
||||
virtual uint64_t add_output(
|
||||
const crypto::hash& tx_hash,
|
||||
const cryptonote::tx_out& tx_output,
|
||||
const uint64_t& local_index,
|
||||
const uint64_t unlock_time,
|
||||
const rct::key* commitment) override {
|
||||
return 0;
|
||||
}
|
||||
virtual void add_tx_amount_output_indices(
|
||||
const uint64_t tx_index, const std::vector<uint64_t>& amount_output_indices) override {}
|
||||
virtual void add_spent_key(const crypto::key_image& k_image) override {}
|
||||
virtual void remove_spent_key(const crypto::key_image& k_image) override {}
|
||||
|
||||
virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const override { return true; }
|
||||
virtual bool for_blocks_range(const uint64_t&, const uint64_t&, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const override { return true; }
|
||||
virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const override { return true; }
|
||||
virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const override { return true; }
|
||||
virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const override { return true; }
|
||||
virtual bool is_read_only() const override { return false; }
|
||||
virtual std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const override { return std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>>(); }
|
||||
virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector<uint64_t> &distribution, uint64_t &base) const override { return false; }
|
||||
virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const override {
|
||||
return true;
|
||||
}
|
||||
virtual bool for_blocks_range(
|
||||
const uint64_t&,
|
||||
const uint64_t&,
|
||||
std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>)
|
||||
const override {
|
||||
return true;
|
||||
}
|
||||
virtual bool for_all_transactions(
|
||||
std::function<bool(const crypto::hash&, const cryptonote::transaction&)>,
|
||||
bool pruned) const override {
|
||||
return true;
|
||||
}
|
||||
virtual bool for_all_outputs(
|
||||
std::function<bool(
|
||||
uint64_t amount, const crypto::hash& tx_hash, uint64_t height, size_t tx_idx)>
|
||||
f) const override {
|
||||
return true;
|
||||
}
|
||||
virtual bool for_all_outputs(
|
||||
uint64_t amount, const std::function<bool(uint64_t height)>& f) const override {
|
||||
return true;
|
||||
}
|
||||
virtual bool is_read_only() const override { return false; }
|
||||
virtual std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> get_output_histogram(
|
||||
const std::vector<uint64_t>& amounts,
|
||||
bool unlocked,
|
||||
uint64_t recent_cutoff,
|
||||
uint64_t min_count) const override {
|
||||
return std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>>();
|
||||
}
|
||||
virtual bool get_output_distribution(
|
||||
uint64_t amount,
|
||||
uint64_t from_height,
|
||||
uint64_t to_height,
|
||||
std::vector<uint64_t>& distribution,
|
||||
uint64_t& base) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void add_txpool_tx(const crypto::hash &txid, const std::string &blob, const cryptonote::txpool_tx_meta_t& details) override {}
|
||||
virtual void update_txpool_tx(const crypto::hash &txid, const cryptonote::txpool_tx_meta_t& details) override {}
|
||||
virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const override { return 0; }
|
||||
virtual bool txpool_has_tx(const crypto::hash &txid) const override { return false; }
|
||||
virtual void remove_txpool_tx(const crypto::hash& txid) override {}
|
||||
virtual bool get_txpool_tx_meta(const crypto::hash& txid, cryptonote::txpool_tx_meta_t &meta) const override { return false; }
|
||||
virtual bool get_txpool_tx_blob(const crypto::hash& txid, std::string &bd) const override { return false; }
|
||||
virtual uint64_t get_database_size() const override { return 0; }
|
||||
virtual std::string get_txpool_tx_blob(const crypto::hash& txid) const override { return ""; }
|
||||
virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const std::string*)>, bool include_blob = false, bool include_unrelayed_txes = false) const override { return false; }
|
||||
virtual void add_txpool_tx(
|
||||
const crypto::hash& txid,
|
||||
const std::string& blob,
|
||||
const cryptonote::txpool_tx_meta_t& details) override {}
|
||||
virtual void update_txpool_tx(
|
||||
const crypto::hash& txid, const cryptonote::txpool_tx_meta_t& details) override {}
|
||||
virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const override {
|
||||
return 0;
|
||||
}
|
||||
virtual bool txpool_has_tx(const crypto::hash& txid) const override { return false; }
|
||||
virtual void remove_txpool_tx(const crypto::hash& txid) override {}
|
||||
virtual bool get_txpool_tx_meta(
|
||||
const crypto::hash& txid, cryptonote::txpool_tx_meta_t& meta) const override {
|
||||
return false;
|
||||
}
|
||||
virtual bool get_txpool_tx_blob(const crypto::hash& txid, std::string& bd) const override {
|
||||
return false;
|
||||
}
|
||||
virtual uint64_t get_database_size() const override { return 0; }
|
||||
virtual std::string get_txpool_tx_blob(const crypto::hash& txid) const override { return ""; }
|
||||
virtual bool for_all_txpool_txes(
|
||||
std::function<bool(
|
||||
const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const std::string*)>,
|
||||
bool include_blob = false,
|
||||
bool include_unrelayed_txes = false) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void add_block( const cryptonote::block& blk
|
||||
, size_t block_weight
|
||||
, uint64_t long_term_block_weight
|
||||
, const cryptonote::difficulty_type& cumulative_difficulty
|
||||
, const uint64_t& coins_generated
|
||||
, uint64_t num_rct_outs
|
||||
, const crypto::hash& blk_hash
|
||||
) override { }
|
||||
virtual cryptonote::block get_block_from_height(uint64_t height) const override { return cryptonote::block(); }
|
||||
virtual void add_block(
|
||||
const cryptonote::block& blk,
|
||||
size_t block_weight,
|
||||
uint64_t long_term_block_weight,
|
||||
const cryptonote::difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
uint64_t num_rct_outs,
|
||||
const crypto::hash& blk_hash) override {}
|
||||
virtual cryptonote::block get_block_from_height(uint64_t height) const override {
|
||||
return cryptonote::block();
|
||||
}
|
||||
|
||||
virtual uint32_t get_blockchain_pruning_seed() const override { return 0; }
|
||||
virtual bool prune_blockchain(uint32_t pruning_seed = 0) override { return true; }
|
||||
virtual bool update_pruning() override { return true; }
|
||||
virtual bool check_pruning() override { return true; }
|
||||
virtual void prune_outputs(uint64_t amount) override {}
|
||||
virtual uint32_t get_blockchain_pruning_seed() const override { return 0; }
|
||||
virtual bool prune_blockchain(uint32_t pruning_seed = 0) override { return true; }
|
||||
virtual bool update_pruning() override { return true; }
|
||||
virtual bool check_pruning() override { return true; }
|
||||
virtual void prune_outputs(uint64_t amount) override {}
|
||||
|
||||
virtual uint64_t get_max_block_size() override { return 100000000; }
|
||||
virtual void add_max_block_size(uint64_t sz) override { }
|
||||
virtual uint64_t get_max_block_size() override { return 100000000; }
|
||||
virtual void add_max_block_size(uint64_t sz) override {}
|
||||
|
||||
virtual void update_block_checkpoint(struct checkpoint_t const &checkpoint) override {}
|
||||
virtual bool get_block_checkpoint (uint64_t height, struct checkpoint_t &checkpoint) const override { return false; }
|
||||
virtual bool get_top_checkpoint (struct checkpoint_t &checkpoint) const override { return false; }
|
||||
virtual void remove_block_checkpoint(uint64_t height) override { }
|
||||
std::vector<cryptonote::checkpoint_t> get_checkpoints_range(uint64_t start, uint64_t end, size_t num_desired_checkpoints = BlockchainDB::GET_ALL_CHECKPOINTS) const override { return {}; }
|
||||
virtual void update_block_checkpoint(struct checkpoint_t const& checkpoint) override {}
|
||||
virtual bool get_block_checkpoint(
|
||||
uint64_t height, struct checkpoint_t& checkpoint) const override {
|
||||
return false;
|
||||
}
|
||||
virtual bool get_top_checkpoint(struct checkpoint_t& checkpoint) const override {
|
||||
return false;
|
||||
}
|
||||
virtual void remove_block_checkpoint(uint64_t height) override {}
|
||||
std::vector<cryptonote::checkpoint_t> get_checkpoints_range(
|
||||
uint64_t start,
|
||||
uint64_t end,
|
||||
size_t num_desired_checkpoints = BlockchainDB::GET_ALL_CHECKPOINTS) const override {
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual void get_output_blacklist (std::vector<uint64_t> &blacklist) const override { }
|
||||
virtual void add_output_blacklist (std::vector<uint64_t> const &blacklist) override { }
|
||||
virtual void set_service_node_data (const std::string& data, bool long_term) override { }
|
||||
virtual bool get_service_node_data (std::string& data, bool long_term) const override { return false; }
|
||||
virtual void clear_service_node_data() override { }
|
||||
virtual void get_output_blacklist(std::vector<uint64_t>& blacklist) const override {}
|
||||
virtual void add_output_blacklist(std::vector<uint64_t> const& blacklist) override {}
|
||||
virtual void set_service_node_data(const std::string& data, bool long_term) override {}
|
||||
virtual bool get_service_node_data(std::string& data, bool long_term) const override {
|
||||
return false;
|
||||
}
|
||||
virtual void clear_service_node_data() override {}
|
||||
|
||||
bool get_service_node_proof(const crypto::public_key &pubkey, service_nodes::proof_info &proof) const override { return false; }
|
||||
std::unordered_map<crypto::public_key, service_nodes::proof_info> get_all_service_node_proofs() const override { return {}; }
|
||||
void set_service_node_proof(const crypto::public_key &pubkey, const service_nodes::proof_info &proof) override { }
|
||||
bool remove_service_node_proof(const crypto::public_key &pubkey) override { return false; }
|
||||
bool get_service_node_proof(
|
||||
const crypto::public_key& pubkey, service_nodes::proof_info& proof) const override {
|
||||
return false;
|
||||
}
|
||||
std::unordered_map<crypto::public_key, service_nodes::proof_info> get_all_service_node_proofs()
|
||||
const override {
|
||||
return {};
|
||||
}
|
||||
void set_service_node_proof(
|
||||
const crypto::public_key& pubkey, const service_nodes::proof_info& proof) override {}
|
||||
bool remove_service_node_proof(const crypto::public_key& pubkey) override { return false; }
|
||||
|
||||
virtual void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const std::string &blob, const std::string *checkpoint) override {}
|
||||
virtual bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, std::string *blob, std::string *checkpoint) const override { return false; }
|
||||
virtual void remove_alt_block(const crypto::hash &blkid) override {}
|
||||
virtual uint64_t get_alt_block_count() override { return 0; }
|
||||
virtual void drop_alt_blocks() override {}
|
||||
virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const std::string *block_blob, const std::string *checkpoint_blob)> f, bool include_blob = false) const override { return true; }
|
||||
virtual void add_alt_block(
|
||||
const crypto::hash& blkid,
|
||||
const cryptonote::alt_block_data_t& data,
|
||||
const std::string& blob,
|
||||
const std::string* checkpoint) override {}
|
||||
virtual bool get_alt_block(
|
||||
const crypto::hash& blkid,
|
||||
alt_block_data_t* data,
|
||||
std::string* blob,
|
||||
std::string* checkpoint) const override {
|
||||
return false;
|
||||
}
|
||||
virtual void remove_alt_block(const crypto::hash& blkid) override {}
|
||||
virtual uint64_t get_alt_block_count() override { return 0; }
|
||||
virtual void drop_alt_blocks() override {}
|
||||
virtual bool for_all_alt_blocks(
|
||||
std::function<
|
||||
bool(const crypto::hash& blkid,
|
||||
const alt_block_data_t& data,
|
||||
const std::string* block_blob,
|
||||
const std::string* checkpoint_blob)> f,
|
||||
bool include_blob = false) const override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace cryptonote
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -26,13 +26,13 @@
|
|||
// 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.
|
||||
|
||||
#include "common/command_line.h"
|
||||
#include "common/varint.h"
|
||||
#include "common/median.h"
|
||||
#include "common/fs-format.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "blockchain_objects.h"
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "blockchain_objects.h"
|
||||
#include "common/command_line.h"
|
||||
#include "common/fs-format.h"
|
||||
#include "common/median.h"
|
||||
#include "common/varint.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "cryptonote_core/uptime_proof.h"
|
||||
#include "version.h"
|
||||
|
||||
|
@ -41,276 +41,264 @@ using namespace cryptonote;
|
|||
|
||||
static auto logcat = log::Cat("bcutil");
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
TRY_ENTRY();
|
||||
int main(int argc, char* argv[]) {
|
||||
TRY_ENTRY();
|
||||
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
tools::on_startup();
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
tools::on_startup();
|
||||
|
||||
auto opt_size = command_line::boost_option_sizes();
|
||||
auto opt_size = command_line::boost_option_sizes();
|
||||
|
||||
po::options_description desc_cmd_only("Command line options", opt_size.first, opt_size.second);
|
||||
po::options_description desc_cmd_sett("Command line options and settings options", opt_size.first, opt_size.second);
|
||||
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
|
||||
const command_line::arg_descriptor<std::string> arg_txid = {"txid", "Get min depth for this txid", ""};
|
||||
const command_line::arg_descriptor<uint64_t> arg_height = {"height", "Get min depth for all txes at this height", 0};
|
||||
const command_line::arg_descriptor<bool> arg_include_coinbase = {"include-coinbase", "Include coinbase in the average", false};
|
||||
po::options_description desc_cmd_only("Command line options", opt_size.first, opt_size.second);
|
||||
po::options_description desc_cmd_sett(
|
||||
"Command line options and settings options", opt_size.first, opt_size.second);
|
||||
const command_line::arg_descriptor<std::string> arg_log_level = {
|
||||
"log-level", "0-4 or categories", ""};
|
||||
const command_line::arg_descriptor<std::string> arg_txid = {
|
||||
"txid", "Get min depth for this txid", ""};
|
||||
const command_line::arg_descriptor<uint64_t> arg_height = {
|
||||
"height", "Get min depth for all txes at this height", 0};
|
||||
const command_line::arg_descriptor<bool> arg_include_coinbase = {
|
||||
"include-coinbase", "Include coinbase in the average", false};
|
||||
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_devnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, arg_log_level);
|
||||
command_line::add_arg(desc_cmd_sett, arg_txid);
|
||||
command_line::add_arg(desc_cmd_sett, arg_height);
|
||||
command_line::add_arg(desc_cmd_sett, arg_include_coinbase);
|
||||
command_line::add_arg(desc_cmd_only, command_line::arg_help);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_devnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, arg_log_level);
|
||||
command_line::add_arg(desc_cmd_sett, arg_txid);
|
||||
command_line::add_arg(desc_cmd_sett, arg_height);
|
||||
command_line::add_arg(desc_cmd_sett, arg_include_coinbase);
|
||||
command_line::add_arg(desc_cmd_only, command_line::arg_help);
|
||||
|
||||
po::options_description desc_options("Allowed options");
|
||||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
po::options_description desc_options("Allowed options");
|
||||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
auto parser = po::command_line_parser(argc, argv).options(desc_options);
|
||||
po::store(parser.run(), vm);
|
||||
po::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (! r)
|
||||
return 1;
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]() {
|
||||
auto parser = po::command_line_parser(argc, argv).options(desc_options);
|
||||
po::store(parser.run(), vm);
|
||||
po::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
std::cout << "Oxen '" << OXEN_RELEASE_NAME << "' (v" << OXEN_VERSION_FULL << ")\n\n";
|
||||
std::cout << desc_options << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
auto log_file_path = m_config_folder + "oxen-blockchain-depth.log";
|
||||
log::Level log_level;
|
||||
if(auto level = oxen::logging::parse_level(command_line::get_arg(vm, arg_log_level).c_str())) {
|
||||
log_level = *level;
|
||||
} else {
|
||||
std::cerr << "Incorrect log level: " << command_line::get_arg(vm, arg_log_level).c_str() << std::endl;
|
||||
throw std::runtime_error{"Incorrect log level"};
|
||||
}
|
||||
oxen::logging::init(log_file_path, log_level);
|
||||
log::warning(logcat, "Starting...");
|
||||
|
||||
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
||||
bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on);
|
||||
network_type net_type = opt_testnet ? network_type::TESTNET : opt_devnet ? network_type::DEVNET : network_type::MAINNET;
|
||||
std::string opt_txid_string = command_line::get_arg(vm, arg_txid);
|
||||
uint64_t opt_height = command_line::get_arg(vm, arg_height);
|
||||
bool opt_include_coinbase = command_line::get_arg(vm, arg_include_coinbase);
|
||||
|
||||
if (!opt_txid_string.empty() && opt_height)
|
||||
{
|
||||
std::cerr << "txid and height cannot be given at the same time" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
crypto::hash opt_txid{};
|
||||
if (!opt_txid_string.empty())
|
||||
{
|
||||
if (!tools::hex_to_type(opt_txid_string, opt_txid))
|
||||
{
|
||||
std::cerr << "Invalid txid" << std::endl;
|
||||
return 1;
|
||||
if (command_line::get_arg(vm, command_line::arg_help)) {
|
||||
std::cout << "Oxen '" << OXEN_RELEASE_NAME << "' (v" << OXEN_VERSION_FULL << ")\n\n";
|
||||
std::cout << desc_options << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
log::warning(logcat, "Initializing source blockchain (BlockchainDB)");
|
||||
blockchain_objects_t blockchain_objects = {};
|
||||
Blockchain *core_storage = &blockchain_objects.m_blockchain;
|
||||
BlockchainDB *db = new_db();
|
||||
if (db == NULL)
|
||||
{
|
||||
log::error(logcat, "Failed to initialize a database");
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
log::warning(logcat, "database: LMDB");
|
||||
|
||||
const fs::path filename = fs::u8path(command_line::get_arg(vm, cryptonote::arg_data_dir)) / db->get_db_name();
|
||||
log::warning(logcat, "Loading blockchain from folder {} ...", filename);
|
||||
|
||||
try
|
||||
{
|
||||
db->open(filename, core_storage->nettype(), DBF_RDONLY);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
r = core_storage->init(db, nullptr /*ons_db*/, nullptr, net_type);
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
|
||||
log::warning(logcat, "Source blockchain storage initialized OK");
|
||||
|
||||
std::vector<crypto::hash> start_txids;
|
||||
if (!opt_txid_string.empty())
|
||||
{
|
||||
start_txids.push_back(opt_txid);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string bd = db->get_block_blob_from_height(opt_height);
|
||||
cryptonote::block b;
|
||||
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
|
||||
{
|
||||
log::warning(logcat, "Bad block from db");
|
||||
return 1;
|
||||
auto m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
auto log_file_path = m_config_folder + "oxen-blockchain-depth.log";
|
||||
log::Level log_level;
|
||||
if (auto level = oxen::logging::parse_level(command_line::get_arg(vm, arg_log_level).c_str())) {
|
||||
log_level = *level;
|
||||
} else {
|
||||
std::cerr << "Incorrect log level: " << command_line::get_arg(vm, arg_log_level).c_str()
|
||||
<< std::endl;
|
||||
throw std::runtime_error{"Incorrect log level"};
|
||||
}
|
||||
for (const crypto::hash &txid: b.tx_hashes)
|
||||
start_txids.push_back(txid);
|
||||
if (opt_include_coinbase)
|
||||
start_txids.push_back(cryptonote::get_transaction_hash(b.miner_tx));
|
||||
}
|
||||
oxen::logging::init(log_file_path, log_level);
|
||||
log::warning(logcat, "Starting...");
|
||||
|
||||
if (start_txids.empty())
|
||||
{
|
||||
log::warning(logcat, "No transaction(s) to check");
|
||||
return 1;
|
||||
}
|
||||
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
||||
bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on);
|
||||
network_type net_type = opt_testnet ? network_type::TESTNET
|
||||
: opt_devnet ? network_type::DEVNET
|
||||
: network_type::MAINNET;
|
||||
std::string opt_txid_string = command_line::get_arg(vm, arg_txid);
|
||||
uint64_t opt_height = command_line::get_arg(vm, arg_height);
|
||||
bool opt_include_coinbase = command_line::get_arg(vm, arg_include_coinbase);
|
||||
|
||||
std::vector<uint64_t> depths;
|
||||
for (const crypto::hash &start_txid: start_txids)
|
||||
{
|
||||
uint64_t depth = 0;
|
||||
bool coinbase = false;
|
||||
|
||||
log::warning(logcat, "Checking depth for txid {}", start_txid);
|
||||
std::vector<crypto::hash> txids(1, start_txid);
|
||||
while (!coinbase)
|
||||
{
|
||||
log::warning(logcat, "Considering {} transaction(s) at depth {}", txids.size(), depth);
|
||||
std::vector<crypto::hash> new_txids;
|
||||
for (const crypto::hash &txid: txids)
|
||||
{
|
||||
std::string bd;
|
||||
if (!db->get_pruned_tx_blob(txid, bd))
|
||||
{
|
||||
log::warning(logcat, "Failed to get txid {} from db", txid);
|
||||
return 1;
|
||||
}
|
||||
cryptonote::transaction tx;
|
||||
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx))
|
||||
{
|
||||
log::warning(logcat, "Bad tx: {}", txid);
|
||||
return 1;
|
||||
}
|
||||
for (size_t ring = 0; ring < tx.vin.size(); ++ring)
|
||||
{
|
||||
if (std::holds_alternative<cryptonote::txin_gen>(tx.vin[ring]))
|
||||
{
|
||||
log::debug(logcat, "{} is a coinbase transaction", txid);
|
||||
coinbase = true;
|
||||
goto done;
|
||||
}
|
||||
if (auto* txin = std::get_if<cryptonote::txin_to_key>(&tx.vin[ring]))
|
||||
{
|
||||
const uint64_t amount = txin->amount;
|
||||
auto absolute_offsets = cryptonote::relative_output_offsets_to_absolute(txin->key_offsets);
|
||||
for (uint64_t offset: absolute_offsets)
|
||||
{
|
||||
const output_data_t od = db->get_output_key(amount, offset);
|
||||
const crypto::hash block_hash = db->get_block_hash_from_height(od.height);
|
||||
bd = db->get_block_blob(block_hash);
|
||||
cryptonote::block b;
|
||||
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
|
||||
{
|
||||
log::warning(logcat, "Bad block from db");
|
||||
return 1;
|
||||
}
|
||||
// find the tx which created this output
|
||||
bool found = false;
|
||||
for (size_t out = 0; out < b.miner_tx.vout.size(); ++out)
|
||||
{
|
||||
if (auto* txout = std::get_if<cryptonote::txout_to_key>(&b.miner_tx.vout[out].target))
|
||||
{
|
||||
if (txout->key == od.pubkey)
|
||||
{
|
||||
found = true;
|
||||
new_txids.push_back(cryptonote::get_transaction_hash(b.miner_tx));
|
||||
log::debug(logcat, "adding txid: {}", cryptonote::get_transaction_hash(b.miner_tx));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log::warning(logcat, "Bad vout type in txid {}", cryptonote::get_transaction_hash(b.miner_tx));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
for (const crypto::hash &block_txid: b.tx_hashes)
|
||||
{
|
||||
if (found)
|
||||
break;
|
||||
if (!db->get_pruned_tx_blob(block_txid, bd))
|
||||
{
|
||||
log::warning(logcat, "Failed to get txid {} from db", block_txid);
|
||||
return 1;
|
||||
}
|
||||
cryptonote::transaction tx2;
|
||||
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx2))
|
||||
{
|
||||
log::warning(logcat, "Bad tx: {}", block_txid);
|
||||
return 1;
|
||||
}
|
||||
for (size_t out = 0; out < tx2.vout.size(); ++out)
|
||||
{
|
||||
if (auto* txout = std::get_if<cryptonote::txout_to_key>(&tx2.vout[out].target))
|
||||
{
|
||||
if (txout->key == od.pubkey)
|
||||
{
|
||||
found = true;
|
||||
new_txids.push_back(block_txid);
|
||||
log::debug(logcat, "adding txid: {}", block_txid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log::warning(logcat, "Bad vout type in txid {}", block_txid);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
log::warning(logcat, "Output originating transaction not found");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log::warning(logcat, "Bad vin type in txid {}", txid);
|
||||
if (!opt_txid_string.empty() && opt_height) {
|
||||
std::cerr << "txid and height cannot be given at the same time" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
crypto::hash opt_txid{};
|
||||
if (!opt_txid_string.empty()) {
|
||||
if (!tools::hex_to_type(opt_txid_string, opt_txid)) {
|
||||
std::cerr << "Invalid txid" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!coinbase)
|
||||
{
|
||||
std::swap(txids, new_txids);
|
||||
++depth;
|
||||
}
|
||||
}
|
||||
done:
|
||||
log::warning(logcat, "Min depth for txid {}: {}", start_txid, depth);
|
||||
depths.push_back(depth);
|
||||
}
|
||||
|
||||
uint64_t cumulative_depth = 0;
|
||||
for (uint64_t depth: depths)
|
||||
cumulative_depth += depth;
|
||||
log::warning(logcat, "Average min depth for {} transaction(s): {}", start_txids.size(), cumulative_depth/(float)depths.size());
|
||||
log::warning(logcat, "Median min depth for {} transaction(s): {}", start_txids.size(), tools::median(std::move(depths)));
|
||||
log::warning(logcat, "Initializing source blockchain (BlockchainDB)");
|
||||
blockchain_objects_t blockchain_objects = {};
|
||||
Blockchain* core_storage = &blockchain_objects.m_blockchain;
|
||||
BlockchainDB* db = new_db();
|
||||
if (db == NULL) {
|
||||
log::error(logcat, "Failed to initialize a database");
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
log::warning(logcat, "database: LMDB");
|
||||
|
||||
core_storage->deinit();
|
||||
return 0;
|
||||
const fs::path filename =
|
||||
fs::u8path(command_line::get_arg(vm, cryptonote::arg_data_dir)) / db->get_db_name();
|
||||
log::warning(logcat, "Loading blockchain from folder {} ...", filename);
|
||||
|
||||
CATCH_ENTRY("Depth query error", 1);
|
||||
try {
|
||||
db->open(filename, core_storage->nettype(), DBF_RDONLY);
|
||||
} catch (const std::exception& e) {
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
r = core_storage->init(db, nullptr /*ons_db*/, nullptr, net_type);
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
|
||||
log::warning(logcat, "Source blockchain storage initialized OK");
|
||||
|
||||
std::vector<crypto::hash> start_txids;
|
||||
if (!opt_txid_string.empty()) {
|
||||
start_txids.push_back(opt_txid);
|
||||
} else {
|
||||
const std::string bd = db->get_block_blob_from_height(opt_height);
|
||||
cryptonote::block b;
|
||||
if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) {
|
||||
log::warning(logcat, "Bad block from db");
|
||||
return 1;
|
||||
}
|
||||
for (const crypto::hash& txid : b.tx_hashes)
|
||||
start_txids.push_back(txid);
|
||||
if (opt_include_coinbase)
|
||||
start_txids.push_back(cryptonote::get_transaction_hash(b.miner_tx));
|
||||
}
|
||||
|
||||
if (start_txids.empty()) {
|
||||
log::warning(logcat, "No transaction(s) to check");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> depths;
|
||||
for (const crypto::hash& start_txid : start_txids) {
|
||||
uint64_t depth = 0;
|
||||
bool coinbase = false;
|
||||
|
||||
log::warning(logcat, "Checking depth for txid {}", start_txid);
|
||||
std::vector<crypto::hash> txids(1, start_txid);
|
||||
while (!coinbase) {
|
||||
log::warning(logcat, "Considering {} transaction(s) at depth {}", txids.size(), depth);
|
||||
std::vector<crypto::hash> new_txids;
|
||||
for (const crypto::hash& txid : txids) {
|
||||
std::string bd;
|
||||
if (!db->get_pruned_tx_blob(txid, bd)) {
|
||||
log::warning(logcat, "Failed to get txid {} from db", txid);
|
||||
return 1;
|
||||
}
|
||||
cryptonote::transaction tx;
|
||||
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx)) {
|
||||
log::warning(logcat, "Bad tx: {}", txid);
|
||||
return 1;
|
||||
}
|
||||
for (size_t ring = 0; ring < tx.vin.size(); ++ring) {
|
||||
if (std::holds_alternative<cryptonote::txin_gen>(tx.vin[ring])) {
|
||||
log::debug(logcat, "{} is a coinbase transaction", txid);
|
||||
coinbase = true;
|
||||
goto done;
|
||||
}
|
||||
if (auto* txin = std::get_if<cryptonote::txin_to_key>(&tx.vin[ring])) {
|
||||
const uint64_t amount = txin->amount;
|
||||
auto absolute_offsets =
|
||||
cryptonote::relative_output_offsets_to_absolute(txin->key_offsets);
|
||||
for (uint64_t offset : absolute_offsets) {
|
||||
const output_data_t od = db->get_output_key(amount, offset);
|
||||
const crypto::hash block_hash =
|
||||
db->get_block_hash_from_height(od.height);
|
||||
bd = db->get_block_blob(block_hash);
|
||||
cryptonote::block b;
|
||||
if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) {
|
||||
log::warning(logcat, "Bad block from db");
|
||||
return 1;
|
||||
}
|
||||
// find the tx which created this output
|
||||
bool found = false;
|
||||
for (size_t out = 0; out < b.miner_tx.vout.size(); ++out) {
|
||||
if (auto* txout = std::get_if<cryptonote::txout_to_key>(
|
||||
&b.miner_tx.vout[out].target)) {
|
||||
if (txout->key == od.pubkey) {
|
||||
found = true;
|
||||
new_txids.push_back(
|
||||
cryptonote::get_transaction_hash(b.miner_tx));
|
||||
log::debug(
|
||||
logcat,
|
||||
"adding txid: {}",
|
||||
cryptonote::get_transaction_hash(b.miner_tx));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
log::warning(
|
||||
logcat,
|
||||
"Bad vout type in txid {}",
|
||||
cryptonote::get_transaction_hash(b.miner_tx));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
for (const crypto::hash& block_txid : b.tx_hashes) {
|
||||
if (found)
|
||||
break;
|
||||
if (!db->get_pruned_tx_blob(block_txid, bd)) {
|
||||
log::warning(
|
||||
logcat, "Failed to get txid {} from db", block_txid);
|
||||
return 1;
|
||||
}
|
||||
cryptonote::transaction tx2;
|
||||
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx2)) {
|
||||
log::warning(logcat, "Bad tx: {}", block_txid);
|
||||
return 1;
|
||||
}
|
||||
for (size_t out = 0; out < tx2.vout.size(); ++out) {
|
||||
if (auto* txout = std::get_if<cryptonote::txout_to_key>(
|
||||
&tx2.vout[out].target)) {
|
||||
if (txout->key == od.pubkey) {
|
||||
found = true;
|
||||
new_txids.push_back(block_txid);
|
||||
log::debug(logcat, "adding txid: {}", block_txid);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
log::warning(
|
||||
logcat, "Bad vout type in txid {}", block_txid);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
log::warning(logcat, "Output originating transaction not found");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::warning(logcat, "Bad vin type in txid {}", txid);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!coinbase) {
|
||||
std::swap(txids, new_txids);
|
||||
++depth;
|
||||
}
|
||||
}
|
||||
done:
|
||||
log::warning(logcat, "Min depth for txid {}: {}", start_txid, depth);
|
||||
depths.push_back(depth);
|
||||
}
|
||||
|
||||
uint64_t cumulative_depth = 0;
|
||||
for (uint64_t depth : depths)
|
||||
cumulative_depth += depth;
|
||||
log::warning(
|
||||
logcat,
|
||||
"Average min depth for {} transaction(s): {}",
|
||||
start_txids.size(),
|
||||
cumulative_depth / (float)depths.size());
|
||||
log::warning(
|
||||
logcat,
|
||||
"Median min depth for {} transaction(s): {}",
|
||||
start_txids.size(),
|
||||
tools::median(std::move(depths)));
|
||||
|
||||
core_storage->deinit();
|
||||
return 0;
|
||||
|
||||
CATCH_ENTRY("Depth query error", 1);
|
||||
}
|
||||
|
|
|
@ -27,148 +27,148 @@
|
|||
// 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.
|
||||
|
||||
#include "bootstrap_file.h"
|
||||
#include "blockchain_objects.h"
|
||||
#include "blocksdat_file.h"
|
||||
#include "bootstrap_file.h"
|
||||
#include "common/command_line.h"
|
||||
#include "common/fs-format.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "blockchain_objects.h"
|
||||
#include "version.h"
|
||||
#include "cryptonote_core/uptime_proof.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace po = boost::program_options;
|
||||
using namespace blockchain_utils;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
using namespace oxen;
|
||||
auto logcat = log::Cat("bcutil");
|
||||
int main(int argc, char* argv[]) {
|
||||
using namespace oxen;
|
||||
auto logcat = log::Cat("bcutil");
|
||||
|
||||
TRY_ENTRY();
|
||||
TRY_ENTRY();
|
||||
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
uint64_t block_stop = 0;
|
||||
bool blocks_dat = false;
|
||||
tools::on_startup();
|
||||
auto opt_size = command_line::boost_option_sizes();
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
uint64_t block_stop = 0;
|
||||
bool blocks_dat = false;
|
||||
tools::on_startup();
|
||||
auto opt_size = command_line::boost_option_sizes();
|
||||
|
||||
po::options_description desc_cmd_only("Command line options", opt_size.first, opt_size.second);
|
||||
po::options_description desc_cmd_sett("Command line options and settings options", opt_size.first, opt_size.second);
|
||||
const command_line::arg_descriptor<std::string> arg_output_file = {"output-file", "Specify output file", "", true};
|
||||
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
|
||||
const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop};
|
||||
const command_line::arg_descriptor<bool> arg_blocks_dat = {"blocksdat", "Output in blocks.dat format", blocks_dat};
|
||||
po::options_description desc_cmd_only("Command line options", opt_size.first, opt_size.second);
|
||||
po::options_description desc_cmd_sett(
|
||||
"Command line options and settings options", opt_size.first, opt_size.second);
|
||||
const command_line::arg_descriptor<std::string> arg_output_file = {
|
||||
"output-file", "Specify output file", "", true};
|
||||
const command_line::arg_descriptor<std::string> arg_log_level = {
|
||||
"log-level", "0-4 or categories", ""};
|
||||
const command_line::arg_descriptor<uint64_t> arg_block_stop = {
|
||||
"block-stop", "Stop at block number", block_stop};
|
||||
const command_line::arg_descriptor<bool> arg_blocks_dat = {
|
||||
"blocksdat", "Output in blocks.dat format", blocks_dat};
|
||||
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
|
||||
command_line::add_arg(desc_cmd_sett, arg_output_file);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_devnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, arg_log_level);
|
||||
command_line::add_arg(desc_cmd_sett, arg_block_stop);
|
||||
command_line::add_arg(desc_cmd_sett, arg_blocks_dat);
|
||||
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
|
||||
command_line::add_arg(desc_cmd_sett, arg_output_file);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_devnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, arg_log_level);
|
||||
command_line::add_arg(desc_cmd_sett, arg_block_stop);
|
||||
command_line::add_arg(desc_cmd_sett, arg_blocks_dat);
|
||||
command_line::add_arg(desc_cmd_only, command_line::arg_help);
|
||||
|
||||
command_line::add_arg(desc_cmd_only, command_line::arg_help);
|
||||
po::options_description desc_options("Allowed options");
|
||||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
|
||||
po::options_description desc_options("Allowed options");
|
||||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]() {
|
||||
po::store(po::parse_command_line(argc, argv, desc_options), vm);
|
||||
po::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
po::store(po::parse_command_line(argc, argv, desc_options), vm);
|
||||
po::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (! r)
|
||||
return 1;
|
||||
if (command_line::get_arg(vm, command_line::arg_help)) {
|
||||
std::cout << "Oxen '" << OXEN_RELEASE_NAME << "' (v" << OXEN_VERSION_FULL << ")\n\n";
|
||||
std::cout << desc_options << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
std::cout << "Oxen '" << OXEN_RELEASE_NAME << "' (v" << OXEN_VERSION_FULL << ")\n\n";
|
||||
std::cout << desc_options << std::endl;
|
||||
return 1;
|
||||
}
|
||||
block_stop = command_line::get_arg(vm, arg_block_stop);
|
||||
|
||||
block_stop = command_line::get_arg(vm, arg_block_stop);
|
||||
auto m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
auto log_file_path = m_config_folder + "oxen-blockchain-export.log";
|
||||
log::Level log_level;
|
||||
if (auto level = oxen::logging::parse_level(command_line::get_arg(vm, arg_log_level).c_str())) {
|
||||
log_level = *level;
|
||||
} else {
|
||||
std::cerr << "Incorrect log level: " << command_line::get_arg(vm, arg_log_level).c_str()
|
||||
<< std::endl;
|
||||
throw std::runtime_error{"Incorrect log level"};
|
||||
}
|
||||
oxen::logging::init(log_file_path, log_level);
|
||||
log::warning(logcat, "Starting...");
|
||||
|
||||
auto m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
auto log_file_path = m_config_folder + "oxen-blockchain-export.log";
|
||||
log::Level log_level;
|
||||
if(auto level = oxen::logging::parse_level(command_line::get_arg(vm, arg_log_level).c_str())) {
|
||||
log_level = *level;
|
||||
} else {
|
||||
std::cerr << "Incorrect log level: " << command_line::get_arg(vm, arg_log_level).c_str() << std::endl;
|
||||
throw std::runtime_error{"Incorrect log level"};
|
||||
}
|
||||
oxen::logging::init(log_file_path, log_level);
|
||||
log::warning(logcat, "Starting...");
|
||||
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
||||
bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on);
|
||||
if (opt_testnet && opt_devnet) {
|
||||
std::cerr << "Can't specify more than one of --testnet and --devnet" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
bool opt_blocks_dat = command_line::get_arg(vm, arg_blocks_dat);
|
||||
|
||||
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
||||
bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on);
|
||||
if (opt_testnet && opt_devnet)
|
||||
{
|
||||
std::cerr << "Can't specify more than one of --testnet and --devnet" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
bool opt_blocks_dat = command_line::get_arg(vm, arg_blocks_dat);
|
||||
auto config_folder = fs::u8path(command_line::get_arg(vm, cryptonote::arg_data_dir));
|
||||
|
||||
auto config_folder = fs::u8path(command_line::get_arg(vm, cryptonote::arg_data_dir));
|
||||
fs::path output_file_path;
|
||||
if (command_line::has_arg(vm, arg_output_file))
|
||||
output_file_path = fs::u8path(command_line::get_arg(vm, arg_output_file));
|
||||
else
|
||||
output_file_path = config_folder / "export" / BLOCKCHAIN_RAW;
|
||||
log::warning(logcat, "Export output file: {}", output_file_path.string());
|
||||
|
||||
fs::path output_file_path;
|
||||
if (command_line::has_arg(vm, arg_output_file))
|
||||
output_file_path = fs::u8path(command_line::get_arg(vm, arg_output_file));
|
||||
else
|
||||
output_file_path = config_folder / "export" / BLOCKCHAIN_RAW;
|
||||
log::warning(logcat, "Export output file: {}", output_file_path.string());
|
||||
log::warning(logcat, "Initializing source blockchain (BlockchainDB)");
|
||||
blockchain_objects_t blockchain_objects = {};
|
||||
Blockchain* core_storage = &blockchain_objects.m_blockchain;
|
||||
BlockchainDB* db = new_db();
|
||||
if (db == NULL) {
|
||||
log::error(logcat, "Failed to initialize a database");
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
log::warning(logcat, "database: LMDB");
|
||||
|
||||
log::warning(logcat, "Initializing source blockchain (BlockchainDB)");
|
||||
blockchain_objects_t blockchain_objects = {};
|
||||
Blockchain *core_storage = &blockchain_objects.m_blockchain;
|
||||
BlockchainDB *db = new_db();
|
||||
if (db == NULL)
|
||||
{
|
||||
log::error(logcat, "Failed to initialize a database");
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
log::warning(logcat, "database: LMDB");
|
||||
auto filename = config_folder / db->get_db_name();
|
||||
|
||||
auto filename = config_folder / db->get_db_name();
|
||||
log::warning(logcat, "Loading blockchain from folder {} ...", filename);
|
||||
try {
|
||||
db->open(filename, core_storage->nettype(), DBF_RDONLY);
|
||||
} catch (const std::exception& e) {
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
r = core_storage->init(
|
||||
db,
|
||||
nullptr,
|
||||
nullptr,
|
||||
opt_testnet ? cryptonote::network_type::TESTNET
|
||||
: opt_devnet ? cryptonote::network_type::DEVNET
|
||||
: cryptonote::network_type::MAINNET);
|
||||
|
||||
log::warning(logcat, "Loading blockchain from folder {} ...", filename);
|
||||
try
|
||||
{
|
||||
db->open(filename, core_storage->nettype(), DBF_RDONLY);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
r = core_storage->init(db, nullptr, nullptr, opt_testnet ? cryptonote::network_type::TESTNET : opt_devnet ? cryptonote::network_type::DEVNET : cryptonote::network_type::MAINNET);
|
||||
if (core_storage->get_blockchain_pruning_seed() && !opt_blocks_dat) {
|
||||
log::warning(logcat, "Blockchain is pruned, cannot export");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (core_storage->get_blockchain_pruning_seed() && !opt_blocks_dat)
|
||||
{
|
||||
log::warning(logcat, "Blockchain is pruned, cannot export");
|
||||
return 1;
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
|
||||
log::warning(logcat, "Source blockchain storage initialized OK");
|
||||
log::warning(logcat, "Exporting blockchain raw data...");
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
|
||||
log::warning(logcat, "Source blockchain storage initialized OK");
|
||||
log::warning(logcat, "Exporting blockchain raw data...");
|
||||
if (opt_blocks_dat) {
|
||||
BlocksdatFile blocksdat;
|
||||
r = blocksdat.store_blockchain_raw(core_storage, NULL, output_file_path, block_stop);
|
||||
} else {
|
||||
BootstrapFile bootstrap;
|
||||
r = bootstrap.store_blockchain_raw(core_storage, NULL, output_file_path, block_stop);
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to export blockchain raw data");
|
||||
log::warning(logcat, "Blockchain raw data exported OK");
|
||||
return 0;
|
||||
|
||||
if (opt_blocks_dat)
|
||||
{
|
||||
BlocksdatFile blocksdat;
|
||||
r = blocksdat.store_blockchain_raw(core_storage, NULL, output_file_path, block_stop);
|
||||
}
|
||||
else
|
||||
{
|
||||
BootstrapFile bootstrap;
|
||||
r = bootstrap.store_blockchain_raw(core_storage, NULL, output_file_path, block_stop);
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to export blockchain raw data");
|
||||
log::warning(logcat, "Blockchain raw data exported OK");
|
||||
return 0;
|
||||
|
||||
CATCH_ENTRY("Export error", 1);
|
||||
CATCH_ENTRY("Export error", 1);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,20 +2,19 @@
|
|||
#define BLOCKCHAIN_OBJECTS_H
|
||||
|
||||
#include "cryptonote_core/blockchain.h"
|
||||
#include "cryptonote_core/tx_pool.h"
|
||||
#include "cryptonote_core/service_node_list.h"
|
||||
#include "cryptonote_core/service_node_voting.h"
|
||||
#include "cryptonote_core/tx_pool.h"
|
||||
|
||||
// NOTE(oxen): This is done this way because of the circular constructors.
|
||||
struct blockchain_objects_t
|
||||
{
|
||||
cryptonote::Blockchain m_blockchain;
|
||||
cryptonote::tx_memory_pool m_mempool;
|
||||
service_nodes::service_node_list m_service_node_list;
|
||||
blockchain_objects_t() :
|
||||
m_blockchain(m_mempool, m_service_node_list),
|
||||
m_service_node_list(m_blockchain),
|
||||
m_mempool(m_blockchain) { }
|
||||
struct blockchain_objects_t {
|
||||
cryptonote::Blockchain m_blockchain;
|
||||
cryptonote::tx_memory_pool m_mempool;
|
||||
service_nodes::service_node_list m_service_node_list;
|
||||
blockchain_objects_t() :
|
||||
m_blockchain(m_mempool, m_service_node_list),
|
||||
m_service_node_list(m_blockchain),
|
||||
m_mempool(m_blockchain) {}
|
||||
};
|
||||
|
||||
#endif // BLOCKCHAIN_OBJECTS_H
|
||||
#endif // BLOCKCHAIN_OBJECTS_H
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -27,14 +27,14 @@
|
|||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifdef _WIN32
|
||||
#define __STDC_FORMAT_MACROS // NOTE(oxen): Explicitly define the PRIu64 macro on Mingw
|
||||
#define __STDC_FORMAT_MACROS // NOTE(oxen): Explicitly define the PRIu64 macro on Mingw
|
||||
#endif
|
||||
|
||||
#include "common/command_line.h"
|
||||
#include "serialization/crypto.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "blockchain_objects.h"
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "blockchain_objects.h"
|
||||
#include "common/command_line.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "serialization/crypto.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace po = boost::program_options;
|
||||
|
@ -42,244 +42,234 @@ using namespace cryptonote;
|
|||
|
||||
static auto logcat = log::Cat("bcutil");
|
||||
|
||||
static std::map<uint64_t, uint64_t> load_outputs(const fs::path& filename)
|
||||
{
|
||||
std::map<uint64_t, uint64_t> outputs;
|
||||
uint64_t amount = std::numeric_limits<uint64_t>::max();
|
||||
static std::map<uint64_t, uint64_t> load_outputs(const fs::path& filename) {
|
||||
std::map<uint64_t, uint64_t> outputs;
|
||||
uint64_t amount = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
FILE *f =
|
||||
FILE* f =
|
||||
#ifdef _WIN32
|
||||
_wfopen(filename.c_str(), L"r");
|
||||
_wfopen(filename.c_str(), L"r");
|
||||
#else
|
||||
fopen(filename.c_str(), "r");
|
||||
fopen(filename.c_str(), "r");
|
||||
#endif
|
||||
|
||||
if (!f)
|
||||
{
|
||||
log::error(logcat, "Failed to load outputs from {}: {}", filename, strerror(errno));
|
||||
return {};
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
char s[256];
|
||||
if (!fgets(s, sizeof(s), f))
|
||||
break;
|
||||
if (feof(f))
|
||||
break;
|
||||
const size_t len = strlen(s);
|
||||
if (len > 0 && s[len - 1] == '\n')
|
||||
s[len - 1] = 0;
|
||||
if (!s[0])
|
||||
continue;
|
||||
std::pair<uint64_t, uint64_t> output;
|
||||
uint64_t offset, num_offsets;
|
||||
if (sscanf(s, "@%" PRIu64, &amount) == 1)
|
||||
{
|
||||
continue;
|
||||
if (!f) {
|
||||
log::error(logcat, "Failed to load outputs from {}: {}", filename, strerror(errno));
|
||||
return {};
|
||||
}
|
||||
if (amount == std::numeric_limits<uint64_t>::max())
|
||||
{
|
||||
log::error(logcat, "Bad format in {}", filename);
|
||||
continue;
|
||||
while (1) {
|
||||
char s[256];
|
||||
if (!fgets(s, sizeof(s), f))
|
||||
break;
|
||||
if (feof(f))
|
||||
break;
|
||||
const size_t len = strlen(s);
|
||||
if (len > 0 && s[len - 1] == '\n')
|
||||
s[len - 1] = 0;
|
||||
if (!s[0])
|
||||
continue;
|
||||
std::pair<uint64_t, uint64_t> output;
|
||||
uint64_t offset, num_offsets;
|
||||
if (sscanf(s, "@%" PRIu64, &amount) == 1) {
|
||||
continue;
|
||||
}
|
||||
if (amount == std::numeric_limits<uint64_t>::max()) {
|
||||
log::error(logcat, "Bad format in {}", filename);
|
||||
continue;
|
||||
}
|
||||
if (sscanf(s, "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 &&
|
||||
num_offsets < std::numeric_limits<uint64_t>::max() - offset) {
|
||||
outputs[amount] += num_offsets;
|
||||
} else if (sscanf(s, "%" PRIu64, &offset) == 1) {
|
||||
outputs[amount] += 1;
|
||||
} else {
|
||||
log::error(logcat, "Bad format in {}", filename);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (sscanf(s, "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets < std::numeric_limits<uint64_t>::max() - offset)
|
||||
{
|
||||
outputs[amount] += num_offsets;
|
||||
}
|
||||
else if (sscanf(s, "%" PRIu64, &offset) == 1)
|
||||
{
|
||||
outputs[amount] += 1;
|
||||
fclose(f);
|
||||
return outputs;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
TRY_ENTRY();
|
||||
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
|
||||
uint32_t log_level = 0;
|
||||
|
||||
tools::on_startup();
|
||||
|
||||
auto opt_size = command_line::boost_option_sizes();
|
||||
|
||||
po::options_description desc_cmd_only("Command line options", opt_size.first, opt_size.second);
|
||||
po::options_description desc_cmd_sett(
|
||||
"Command line options and settings options", opt_size.first, opt_size.second);
|
||||
const command_line::arg_descriptor<std::string> arg_log_level = {
|
||||
"log-level", "0-4 or categories", ""};
|
||||
const command_line::arg_descriptor<bool> arg_verbose = {"verbose", "Verbose output", false};
|
||||
const command_line::arg_descriptor<bool> arg_dry_run = {
|
||||
"dry-run", "Do not actually prune", false};
|
||||
const command_line::arg_descriptor<std::string> arg_input = {
|
||||
"input", "Path to the known spent outputs file"};
|
||||
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_devnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, arg_log_level);
|
||||
command_line::add_arg(desc_cmd_sett, arg_verbose);
|
||||
command_line::add_arg(desc_cmd_sett, arg_dry_run);
|
||||
command_line::add_arg(desc_cmd_sett, arg_input);
|
||||
command_line::add_arg(desc_cmd_only, command_line::arg_help);
|
||||
|
||||
po::options_description desc_options("Allowed options");
|
||||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]() {
|
||||
auto parser = po::command_line_parser(argc, argv).options(desc_options);
|
||||
po::store(parser.run(), vm);
|
||||
po::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help)) {
|
||||
std::cout << "Oxen '" << OXEN_RELEASE_NAME << "' (v" << OXEN_VERSION_FULL << ")\n\n";
|
||||
std::cout << desc_options << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
mlog_configure(mlog_get_default_log_path("oxen-blockchain-prune-known-spent-data.log"), true);
|
||||
if (!command_line::is_arg_defaulted(vm, arg_log_level))
|
||||
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
|
||||
else
|
||||
{
|
||||
log::error(logcat, "Bad format in {}", filename);
|
||||
continue;
|
||||
mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str());
|
||||
|
||||
log::warning(logcat, "Starting...");
|
||||
|
||||
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
||||
bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on);
|
||||
network_type net_type = opt_testnet ? network_type::TESTNET
|
||||
: opt_devnet ? network_type::DEVNET
|
||||
: network_type::MAINNET;
|
||||
bool opt_verbose = command_line::get_arg(vm, arg_verbose);
|
||||
bool opt_dry_run = command_line::get_arg(vm, arg_dry_run);
|
||||
|
||||
const auto input = fs::u8path(command_line::get_arg(vm, arg_input));
|
||||
|
||||
log::warning(logcat, "Initializing source blockchain (BlockchainDB)");
|
||||
blockchain_objects_t blockchain_objects = {};
|
||||
Blockchain* core_storage = &blockchain_objects.m_blockchain;
|
||||
BlockchainDB* db = new_db();
|
||||
if (db == NULL) {
|
||||
log::error(logcat, "Failed to initialize a database");
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
return outputs;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
TRY_ENTRY();
|
||||
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
|
||||
uint32_t log_level = 0;
|
||||
|
||||
tools::on_startup();
|
||||
|
||||
auto opt_size = command_line::boost_option_sizes();
|
||||
|
||||
po::options_description desc_cmd_only("Command line options", opt_size.first, opt_size.second);
|
||||
po::options_description desc_cmd_sett("Command line options and settings options", opt_size.first, opt_size.second);
|
||||
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
|
||||
const command_line::arg_descriptor<bool> arg_verbose = {"verbose", "Verbose output", false};
|
||||
const command_line::arg_descriptor<bool> arg_dry_run = {"dry-run", "Do not actually prune", false};
|
||||
const command_line::arg_descriptor<std::string> arg_input = {"input", "Path to the known spent outputs file"};
|
||||
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_devnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, arg_log_level);
|
||||
command_line::add_arg(desc_cmd_sett, arg_verbose);
|
||||
command_line::add_arg(desc_cmd_sett, arg_dry_run);
|
||||
command_line::add_arg(desc_cmd_sett, arg_input);
|
||||
command_line::add_arg(desc_cmd_only, command_line::arg_help);
|
||||
|
||||
po::options_description desc_options("Allowed options");
|
||||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
auto parser = po::command_line_parser(argc, argv).options(desc_options);
|
||||
po::store(parser.run(), vm);
|
||||
po::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (! r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
std::cout << "Oxen '" << OXEN_RELEASE_NAME << "' (v" << OXEN_VERSION_FULL << ")\n\n";
|
||||
std::cout << desc_options << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
mlog_configure(mlog_get_default_log_path("oxen-blockchain-prune-known-spent-data.log"), true);
|
||||
if (!command_line::is_arg_defaulted(vm, arg_log_level))
|
||||
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
|
||||
else
|
||||
mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str());
|
||||
|
||||
log::warning(logcat, "Starting...");
|
||||
|
||||
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
||||
bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on);
|
||||
network_type net_type = opt_testnet ? network_type::TESTNET : opt_devnet ? network_type::DEVNET : network_type::MAINNET;
|
||||
bool opt_verbose = command_line::get_arg(vm, arg_verbose);
|
||||
bool opt_dry_run = command_line::get_arg(vm, arg_dry_run);
|
||||
|
||||
const auto input = fs::u8path(command_line::get_arg(vm, arg_input));
|
||||
|
||||
log::warning(logcat, "Initializing source blockchain (BlockchainDB)");
|
||||
blockchain_objects_t blockchain_objects = {};
|
||||
Blockchain *core_storage = &blockchain_objects.m_blockchain;
|
||||
BlockchainDB *db = new_db();
|
||||
if (db == NULL)
|
||||
{
|
||||
log::error(logcat, "Failed to initialize a database");
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
|
||||
const fs::path filename = fs::u8path(command_line::get_arg(vm, cryptonote::arg_data_dir)) / db->get_db_name();
|
||||
log::warning(logcat, "Loading blockchain from folder {} ...", filename);
|
||||
|
||||
try
|
||||
{
|
||||
db->open(filename, core_storage->nettype(), 0);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
r = core_storage->init(db, nullptr /*ons_db*/, net_type);
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
|
||||
log::warning(logcat, "Source blockchain storage initialized OK");
|
||||
|
||||
std::map<uint64_t, uint64_t> known_spent_outputs;
|
||||
if (input.empty())
|
||||
{
|
||||
std::map<uint64_t, std::pair<uint64_t, uint64_t>> outputs;
|
||||
|
||||
log::warning(logcat, "Scanning for known spent data...");
|
||||
db->for_all_transactions([&](const crypto::hash &txid, const cryptonote::transaction &tx){
|
||||
const bool miner_tx = tx.vin.size() == 1 && std::holds_alternative<txin_gen>(tx.vin[0]);
|
||||
for (const auto &in: tx.vin)
|
||||
if (const auto* txin = std::get_if<txin_to_key>(&in); txin && txin->amount != 0)
|
||||
outputs[txin->amount].second++;
|
||||
|
||||
for (const auto &out: tx.vout)
|
||||
{
|
||||
uint64_t amount = out.amount;
|
||||
if (miner_tx && tx.version >= txversion::v2_ringct)
|
||||
amount = 0;
|
||||
if (amount == 0)
|
||||
continue;
|
||||
if (!std::holds_alternative<txout_to_key>(out.target))
|
||||
continue;
|
||||
|
||||
outputs[amount].first++;
|
||||
}
|
||||
return true;
|
||||
}, true);
|
||||
|
||||
for (const auto &i: outputs)
|
||||
{
|
||||
known_spent_outputs[i.first] = i.second.second;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log::warning(logcat, "Loading known spent data...");
|
||||
known_spent_outputs = load_outputs(input);
|
||||
}
|
||||
|
||||
log::warning(logcat, "Pruning known spent data...");
|
||||
|
||||
bool stop_requested = false;
|
||||
tools::signal_handler::install([&stop_requested](int type) {
|
||||
stop_requested = true;
|
||||
});
|
||||
|
||||
db->batch_start();
|
||||
|
||||
size_t num_total_outputs = 0, num_prunable_outputs = 0, num_known_spent_outputs = 0, num_eligible_outputs = 0, num_eligible_known_spent_outputs = 0;
|
||||
for (auto i = known_spent_outputs.begin(); i != known_spent_outputs.end(); ++i)
|
||||
{
|
||||
uint64_t num_outputs = db->get_num_outputs(i->first);
|
||||
num_total_outputs += num_outputs;
|
||||
num_known_spent_outputs += i->second;
|
||||
if (i->first == 0)
|
||||
{
|
||||
if (opt_verbose)
|
||||
log::info(logcat, "Ignoring output value {}, with {} outputs", i->first, num_outputs);
|
||||
continue;
|
||||
}
|
||||
num_eligible_outputs += num_outputs;
|
||||
num_eligible_known_spent_outputs += i->second;
|
||||
if (opt_verbose)
|
||||
log::info(logcat, "{}: {}/{}", i->first, i->second, num_outputs);
|
||||
if (num_outputs > i->second)
|
||||
continue;
|
||||
if (num_outputs && num_outputs < i->second)
|
||||
{
|
||||
log::error(logcat, "More outputs are spent than known for amount {}, not touching", i->first);
|
||||
continue;
|
||||
}
|
||||
if (opt_verbose)
|
||||
log::info(logcat, "Pruning data for {} outputs", num_outputs);
|
||||
if (!opt_dry_run)
|
||||
db->prune_outputs(i->first);
|
||||
num_prunable_outputs += i->second;
|
||||
}
|
||||
|
||||
db->batch_stop();
|
||||
|
||||
log::info(logcat, "Total outputs: {}", num_total_outputs);
|
||||
log::info(logcat, "Known spent outputs: {}", num_known_spent_outputs);
|
||||
log::info(logcat, "Eligible outputs: {}", num_eligible_outputs);
|
||||
log::info(logcat, "Eligible known spent outputs: {}", num_eligible_known_spent_outputs);
|
||||
log::info(logcat, "Prunable outputs: {}", num_prunable_outputs);
|
||||
|
||||
log::warning(logcat, "Blockchain known spent data pruned OK");
|
||||
core_storage->deinit();
|
||||
return 0;
|
||||
|
||||
CATCH_ENTRY("Error", 1);
|
||||
|
||||
const fs::path filename =
|
||||
fs::u8path(command_line::get_arg(vm, cryptonote::arg_data_dir)) / db->get_db_name();
|
||||
log::warning(logcat, "Loading blockchain from folder {} ...", filename);
|
||||
|
||||
try {
|
||||
db->open(filename, core_storage->nettype(), 0);
|
||||
} catch (const std::exception& e) {
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
r = core_storage->init(db, nullptr /*ons_db*/, net_type);
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
|
||||
log::warning(logcat, "Source blockchain storage initialized OK");
|
||||
|
||||
std::map<uint64_t, uint64_t> known_spent_outputs;
|
||||
if (input.empty()) {
|
||||
std::map<uint64_t, std::pair<uint64_t, uint64_t>> outputs;
|
||||
|
||||
log::warning(logcat, "Scanning for known spent data...");
|
||||
db->for_all_transactions(
|
||||
[&](const crypto::hash& txid, const cryptonote::transaction& tx) {
|
||||
const bool miner_tx =
|
||||
tx.vin.size() == 1 && std::holds_alternative<txin_gen>(tx.vin[0]);
|
||||
for (const auto& in : tx.vin)
|
||||
if (const auto* txin = std::get_if<txin_to_key>(&in);
|
||||
txin && txin->amount != 0)
|
||||
outputs[txin->amount].second++;
|
||||
|
||||
for (const auto& out : tx.vout) {
|
||||
uint64_t amount = out.amount;
|
||||
if (miner_tx && tx.version >= txversion::v2_ringct)
|
||||
amount = 0;
|
||||
if (amount == 0)
|
||||
continue;
|
||||
if (!std::holds_alternative<txout_to_key>(out.target))
|
||||
continue;
|
||||
|
||||
outputs[amount].first++;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
true);
|
||||
|
||||
for (const auto& i : outputs) {
|
||||
known_spent_outputs[i.first] = i.second.second;
|
||||
}
|
||||
} else {
|
||||
log::warning(logcat, "Loading known spent data...");
|
||||
known_spent_outputs = load_outputs(input);
|
||||
}
|
||||
|
||||
log::warning(logcat, "Pruning known spent data...");
|
||||
|
||||
bool stop_requested = false;
|
||||
tools::signal_handler::install([&stop_requested](int type) { stop_requested = true; });
|
||||
|
||||
db->batch_start();
|
||||
|
||||
size_t num_total_outputs = 0, num_prunable_outputs = 0, num_known_spent_outputs = 0,
|
||||
num_eligible_outputs = 0, num_eligible_known_spent_outputs = 0;
|
||||
for (auto i = known_spent_outputs.begin(); i != known_spent_outputs.end(); ++i) {
|
||||
uint64_t num_outputs = db->get_num_outputs(i->first);
|
||||
num_total_outputs += num_outputs;
|
||||
num_known_spent_outputs += i->second;
|
||||
if (i->first == 0) {
|
||||
if (opt_verbose)
|
||||
log::info(
|
||||
logcat, "Ignoring output value {}, with {} outputs", i->first, num_outputs);
|
||||
continue;
|
||||
}
|
||||
num_eligible_outputs += num_outputs;
|
||||
num_eligible_known_spent_outputs += i->second;
|
||||
if (opt_verbose)
|
||||
log::info(logcat, "{}: {}/{}", i->first, i->second, num_outputs);
|
||||
if (num_outputs > i->second)
|
||||
continue;
|
||||
if (num_outputs && num_outputs < i->second) {
|
||||
log::error(
|
||||
logcat,
|
||||
"More outputs are spent than known for amount {}, not touching",
|
||||
i->first);
|
||||
continue;
|
||||
}
|
||||
if (opt_verbose)
|
||||
log::info(logcat, "Pruning data for {} outputs", num_outputs);
|
||||
if (!opt_dry_run)
|
||||
db->prune_outputs(i->first);
|
||||
num_prunable_outputs += i->second;
|
||||
}
|
||||
|
||||
db->batch_stop();
|
||||
|
||||
log::info(logcat, "Total outputs: {}", num_total_outputs);
|
||||
log::info(logcat, "Known spent outputs: {}", num_known_spent_outputs);
|
||||
log::info(logcat, "Eligible outputs: {}", num_eligible_outputs);
|
||||
log::info(logcat, "Eligible known spent outputs: {}", num_eligible_known_spent_outputs);
|
||||
log::info(logcat, "Prunable outputs: {}", num_prunable_outputs);
|
||||
|
||||
log::warning(logcat, "Blockchain known spent data pruned OK");
|
||||
core_storage->deinit();
|
||||
return 0;
|
||||
|
||||
CATCH_ENTRY("Error", 1);
|
||||
}
|
||||
|
|
|
@ -26,295 +26,305 @@
|
|||
// 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.
|
||||
|
||||
#include <date/date.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <chrono>
|
||||
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "blockchain_objects.h"
|
||||
#include "common/command_line.h"
|
||||
#include "common/varint.h"
|
||||
#include "common/signal_handler.h"
|
||||
#include "common/fs-format.h"
|
||||
#include "common/signal_handler.h"
|
||||
#include "common/varint.h"
|
||||
#include "cryptonote_basic/cryptonote_boost_serialization.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "blockchain_objects.h"
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "version.h"
|
||||
#include "cryptonote_core/uptime_proof.h"
|
||||
#include <date/date.h>
|
||||
#include "version.h"
|
||||
|
||||
namespace po = boost::program_options;
|
||||
using namespace cryptonote;
|
||||
|
||||
static bool stop_requested = false;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
static auto logcat = log::Cat("bcutil");
|
||||
int main(int argc, char* argv[]) {
|
||||
static auto logcat = log::Cat("bcutil");
|
||||
|
||||
TRY_ENTRY();
|
||||
TRY_ENTRY();
|
||||
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
uint64_t block_start = 0;
|
||||
uint64_t block_stop = 0;
|
||||
tools::on_startup();
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
uint64_t block_start = 0;
|
||||
uint64_t block_stop = 0;
|
||||
tools::on_startup();
|
||||
|
||||
auto opt_size = command_line::boost_option_sizes();
|
||||
auto opt_size = command_line::boost_option_sizes();
|
||||
|
||||
po::options_description desc_cmd_only("Command line options", opt_size.first, opt_size.second);
|
||||
po::options_description desc_cmd_sett("Command line options and settings options", opt_size.first, opt_size.second);
|
||||
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
|
||||
const command_line::arg_descriptor<uint64_t> arg_block_start = {"block-start", "start at block number", block_start};
|
||||
const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop};
|
||||
const command_line::arg_descriptor<bool> arg_inputs = {"with-inputs", "with input stats", false};
|
||||
const command_line::arg_descriptor<bool> arg_outputs = {"with-outputs", "with output stats", false};
|
||||
const command_line::arg_descriptor<bool> arg_ringsize = {"with-ringsize", "with ringsize stats", false};
|
||||
const command_line::arg_descriptor<bool> arg_hours = {"with-hours", "with txns per hour", false};
|
||||
po::options_description desc_cmd_only("Command line options", opt_size.first, opt_size.second);
|
||||
po::options_description desc_cmd_sett(
|
||||
"Command line options and settings options", opt_size.first, opt_size.second);
|
||||
const command_line::arg_descriptor<std::string> arg_log_level = {
|
||||
"log-level", "0-4 or categories", ""};
|
||||
const command_line::arg_descriptor<uint64_t> arg_block_start = {
|
||||
"block-start", "start at block number", block_start};
|
||||
const command_line::arg_descriptor<uint64_t> arg_block_stop = {
|
||||
"block-stop", "Stop at block number", block_stop};
|
||||
const command_line::arg_descriptor<bool> arg_inputs = {
|
||||
"with-inputs", "with input stats", false};
|
||||
const command_line::arg_descriptor<bool> arg_outputs = {
|
||||
"with-outputs", "with output stats", false};
|
||||
const command_line::arg_descriptor<bool> arg_ringsize = {
|
||||
"with-ringsize", "with ringsize stats", false};
|
||||
const command_line::arg_descriptor<bool> arg_hours = {
|
||||
"with-hours", "with txns per hour", false};
|
||||
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_devnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, arg_log_level);
|
||||
command_line::add_arg(desc_cmd_sett, arg_block_start);
|
||||
command_line::add_arg(desc_cmd_sett, arg_block_stop);
|
||||
command_line::add_arg(desc_cmd_sett, arg_inputs);
|
||||
command_line::add_arg(desc_cmd_sett, arg_outputs);
|
||||
command_line::add_arg(desc_cmd_sett, arg_ringsize);
|
||||
command_line::add_arg(desc_cmd_sett, arg_hours);
|
||||
command_line::add_arg(desc_cmd_only, command_line::arg_help);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_devnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, arg_log_level);
|
||||
command_line::add_arg(desc_cmd_sett, arg_block_start);
|
||||
command_line::add_arg(desc_cmd_sett, arg_block_stop);
|
||||
command_line::add_arg(desc_cmd_sett, arg_inputs);
|
||||
command_line::add_arg(desc_cmd_sett, arg_outputs);
|
||||
command_line::add_arg(desc_cmd_sett, arg_ringsize);
|
||||
command_line::add_arg(desc_cmd_sett, arg_hours);
|
||||
command_line::add_arg(desc_cmd_only, command_line::arg_help);
|
||||
|
||||
po::options_description desc_options("Allowed options");
|
||||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
po::options_description desc_options("Allowed options");
|
||||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
auto parser = po::command_line_parser(argc, argv).options(desc_options);
|
||||
po::store(parser.run(), vm);
|
||||
po::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (! r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
std::cout << "Oxen '" << OXEN_RELEASE_NAME << "' (v" << OXEN_VERSION_FULL << ")\n\n";
|
||||
std::cout << desc_options << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
auto log_file_path = m_config_folder + "oxen-blockchain-stats.log";
|
||||
log::Level log_level;
|
||||
if(auto level = oxen::logging::parse_level(command_line::get_arg(vm, arg_log_level).c_str())) {
|
||||
log_level = *level;
|
||||
} else {
|
||||
std::cerr << "Incorrect log level: " << command_line::get_arg(vm, arg_log_level).c_str() << std::endl;
|
||||
throw std::runtime_error{"Incorrect log level"};
|
||||
}
|
||||
oxen::logging::init(log_file_path, log_level);
|
||||
log::warning(logcat, "Starting...");
|
||||
|
||||
std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
||||
bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on);
|
||||
network_type net_type = opt_testnet ? network_type::TESTNET : opt_devnet ? network_type::DEVNET : network_type::MAINNET;
|
||||
block_start = command_line::get_arg(vm, arg_block_start);
|
||||
block_stop = command_line::get_arg(vm, arg_block_stop);
|
||||
bool do_inputs = command_line::get_arg(vm, arg_inputs);
|
||||
bool do_outputs = command_line::get_arg(vm, arg_outputs);
|
||||
bool do_ringsize = command_line::get_arg(vm, arg_ringsize);
|
||||
bool do_hours = command_line::get_arg(vm, arg_hours);
|
||||
|
||||
log::warning(logcat, "Initializing source blockchain (BlockchainDB)");
|
||||
blockchain_objects_t blockchain_objects = {};
|
||||
Blockchain *core_storage = &blockchain_objects.m_blockchain;
|
||||
BlockchainDB *db = new_db();
|
||||
if (db == NULL)
|
||||
{
|
||||
log::error(logcat, "Failed to initialize a database");
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
|
||||
const fs::path filename = fs::u8path(opt_data_dir) / db->get_db_name();
|
||||
log::warning(logcat, "Loading blockchain from folder {} ...", filename);
|
||||
|
||||
try
|
||||
{
|
||||
db->open(filename, core_storage->nettype(), DBF_RDONLY);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
r = core_storage->init(db, nullptr /*ons_db*/, nullptr, net_type);
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
|
||||
log::warning(logcat, "Source blockchain storage initialized OK");
|
||||
|
||||
tools::signal_handler::install([](int type) {
|
||||
stop_requested = true;
|
||||
});
|
||||
|
||||
const uint64_t db_height = db->height();
|
||||
if (!block_stop)
|
||||
block_stop = db_height;
|
||||
log::info(logcat, "Starting from height {}, stopping at height {}", block_start, block_stop);
|
||||
|
||||
/*
|
||||
* The default output can be plotted with GnuPlot using these commands:
|
||||
set key autotitle columnhead
|
||||
set title "Oxen Blockchain Growth"
|
||||
set timefmt "%Y-%m-%d"
|
||||
set xdata time
|
||||
set xrange ["2014-04-17":*]
|
||||
set format x "%Y-%m-%d"
|
||||
set yrange [0:*]
|
||||
set y2range [0:*]
|
||||
set ylabel "Txs/Day"
|
||||
set y2label "Bytes"
|
||||
set y2tics nomirror
|
||||
plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, '' using (timecolumn(1,"%Y-%m-%d")):7 axes x1y2 with lines
|
||||
*/
|
||||
|
||||
// spit out a comment that GnuPlot can use as an index
|
||||
std::cout << "\n# DATA\n";
|
||||
std::cout << "Date\tBlocks/day\tBlocks\tTxs/Day\tTxs\tBytes/Day\tBytes";
|
||||
if (do_inputs)
|
||||
std::cout << "\tInMin\tInMax\tInAvg";
|
||||
if (do_outputs)
|
||||
std::cout << "\tOutMin\tOutMax\tOutAvg";
|
||||
if (do_ringsize)
|
||||
std::cout << "\tRingMin\tRingMax\tRingAvg";
|
||||
if (do_hours) {
|
||||
char buf[8];
|
||||
unsigned int i;
|
||||
for (i=0; i<24; i++) {
|
||||
sprintf(buf, "\t%02u:00", i);
|
||||
std::cout << buf;
|
||||
}
|
||||
}
|
||||
std::cout << "\n";
|
||||
|
||||
std::optional<std::chrono::system_clock::time_point> prev_ts;
|
||||
uint64_t prevsz = 0, currsz = 0;
|
||||
uint64_t prevtxs = 0, currtxs = 0;
|
||||
uint64_t currblks = 0;
|
||||
uint64_t totins = 0, totouts = 0, totrings = 0;
|
||||
uint32_t minins = 10, maxins = 0;
|
||||
uint32_t minouts = 10, maxouts = 0;
|
||||
uint32_t minrings = 50, maxrings = 0;
|
||||
uint32_t io, tottxs = 0;
|
||||
uint32_t txhr[24] = {0};
|
||||
unsigned int i;
|
||||
|
||||
for (uint64_t h = block_start; h < block_stop; ++h)
|
||||
{
|
||||
std::string bd = db->get_block_blob_from_height(h);
|
||||
cryptonote::block blk;
|
||||
if (!cryptonote::parse_and_validate_block_from_blob(bd, blk))
|
||||
{
|
||||
log::warning(logcat, "Bad block from db");
|
||||
return 1;
|
||||
}
|
||||
auto ts = std::chrono::system_clock::from_time_t(blk.timestamp);
|
||||
using namespace date;
|
||||
year_month_day curr_date{floor<days>(ts)};
|
||||
if (!prev_ts)
|
||||
prev_ts = ts;
|
||||
year_month_day prev_date{floor<days>(*prev_ts)};
|
||||
// catch change of day
|
||||
if (curr_date.day() > prev_date.day() || (curr_date.day() == day{1} && prev_date.day() > day{27}))
|
||||
{
|
||||
// check for timestamp fudging around month ends
|
||||
if (curr_date.day() == day{1} && prev_date.day() > day{27})
|
||||
goto skip;
|
||||
prev_ts = ts;
|
||||
std::cout << format("%Y-%m-%d", prev_date) << "\t" << currblks << "\t" << h << "\t" << currtxs << "\t" << prevtxs + currtxs << "\t" << currsz << "\t" << prevsz + currsz;
|
||||
prevsz += currsz;
|
||||
currsz = 0;
|
||||
currblks = 0;
|
||||
prevtxs += currtxs;
|
||||
currtxs = 0;
|
||||
if (!tottxs)
|
||||
tottxs = 1;
|
||||
if (do_inputs) {
|
||||
std::cout << "\t" << (maxins ? minins : 0) << "\t" << maxins << "\t" << totins / tottxs;
|
||||
minins = 10; maxins = 0; totins = 0;
|
||||
}
|
||||
if (do_outputs) {
|
||||
std::cout << "\t" << (maxouts ? minouts : 0) << "\t" << maxouts << "\t" << totouts / tottxs;
|
||||
minouts = 10; maxouts = 0; totouts = 0;
|
||||
}
|
||||
if (do_ringsize) {
|
||||
std::cout << "\t" << (maxrings ? minrings : 0) << "\t" << maxrings << "\t" << totrings / tottxs;
|
||||
minrings = 50; maxrings = 0; totrings = 0;
|
||||
}
|
||||
tottxs = 0;
|
||||
if (do_hours) {
|
||||
for (i=0; i<24; i++) {
|
||||
std::cout << "\t" << txhr[i];
|
||||
txhr[i] = 0;
|
||||
}
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
skip:
|
||||
currsz += bd.size();
|
||||
for (const auto& tx_id : blk.tx_hashes)
|
||||
{
|
||||
if (!tx_id)
|
||||
{
|
||||
throw std::runtime_error("Aborting: null txid");
|
||||
}
|
||||
if (!db->get_pruned_tx_blob(tx_id, bd))
|
||||
{
|
||||
throw std::runtime_error("Aborting: tx not found");
|
||||
}
|
||||
transaction tx;
|
||||
if (!parse_and_validate_tx_base_from_blob(bd, tx))
|
||||
{
|
||||
log::warning(logcat, "Bad txn from db");
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]() {
|
||||
auto parser = po::command_line_parser(argc, argv).options(desc_options);
|
||||
po::store(parser.run(), vm);
|
||||
po::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help)) {
|
||||
std::cout << "Oxen '" << OXEN_RELEASE_NAME << "' (v" << OXEN_VERSION_FULL << ")\n\n";
|
||||
std::cout << desc_options << std::endl;
|
||||
return 1;
|
||||
}
|
||||
currsz += bd.size();
|
||||
currtxs++;
|
||||
if (do_hours)
|
||||
txhr[hh_mm_ss{ts - floor<days>(ts)}.hours().count()]++;
|
||||
if (do_inputs) {
|
||||
io = tx.vin.size();
|
||||
if (io < minins)
|
||||
minins = io;
|
||||
else if (io > maxins)
|
||||
maxins = io;
|
||||
totins += io;
|
||||
}
|
||||
if (do_ringsize) {
|
||||
const auto& tx_in_to_key = var::get<cryptonote::txin_to_key>(tx.vin[0]);
|
||||
io = tx_in_to_key.key_offsets.size();
|
||||
if (io < minrings)
|
||||
minrings = io;
|
||||
else if (io > maxrings)
|
||||
maxrings = io;
|
||||
totrings += io;
|
||||
}
|
||||
if (do_outputs) {
|
||||
io = tx.vout.size();
|
||||
if (io < minouts)
|
||||
minouts = io;
|
||||
else if (io > maxouts)
|
||||
maxouts = io;
|
||||
totouts += io;
|
||||
}
|
||||
tottxs++;
|
||||
}
|
||||
currblks++;
|
||||
|
||||
if (stop_requested)
|
||||
break;
|
||||
}
|
||||
auto m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
auto log_file_path = m_config_folder + "oxen-blockchain-stats.log";
|
||||
log::Level log_level;
|
||||
if (auto level = oxen::logging::parse_level(command_line::get_arg(vm, arg_log_level).c_str())) {
|
||||
log_level = *level;
|
||||
} else {
|
||||
std::cerr << "Incorrect log level: " << command_line::get_arg(vm, arg_log_level).c_str()
|
||||
<< std::endl;
|
||||
throw std::runtime_error{"Incorrect log level"};
|
||||
}
|
||||
oxen::logging::init(log_file_path, log_level);
|
||||
log::warning(logcat, "Starting...");
|
||||
|
||||
core_storage->deinit();
|
||||
return 0;
|
||||
std::string opt_data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
||||
bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on);
|
||||
network_type net_type = opt_testnet ? network_type::TESTNET
|
||||
: opt_devnet ? network_type::DEVNET
|
||||
: network_type::MAINNET;
|
||||
block_start = command_line::get_arg(vm, arg_block_start);
|
||||
block_stop = command_line::get_arg(vm, arg_block_stop);
|
||||
bool do_inputs = command_line::get_arg(vm, arg_inputs);
|
||||
bool do_outputs = command_line::get_arg(vm, arg_outputs);
|
||||
bool do_ringsize = command_line::get_arg(vm, arg_ringsize);
|
||||
bool do_hours = command_line::get_arg(vm, arg_hours);
|
||||
|
||||
CATCH_ENTRY("Stats reporting error", 1);
|
||||
log::warning(logcat, "Initializing source blockchain (BlockchainDB)");
|
||||
blockchain_objects_t blockchain_objects = {};
|
||||
Blockchain* core_storage = &blockchain_objects.m_blockchain;
|
||||
BlockchainDB* db = new_db();
|
||||
if (db == NULL) {
|
||||
log::error(logcat, "Failed to initialize a database");
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
|
||||
const fs::path filename = fs::u8path(opt_data_dir) / db->get_db_name();
|
||||
log::warning(logcat, "Loading blockchain from folder {} ...", filename);
|
||||
|
||||
try {
|
||||
db->open(filename, core_storage->nettype(), DBF_RDONLY);
|
||||
} catch (const std::exception& e) {
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
r = core_storage->init(db, nullptr /*ons_db*/, nullptr, net_type);
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
|
||||
log::warning(logcat, "Source blockchain storage initialized OK");
|
||||
|
||||
tools::signal_handler::install([](int type) { stop_requested = true; });
|
||||
|
||||
const uint64_t db_height = db->height();
|
||||
if (!block_stop)
|
||||
block_stop = db_height;
|
||||
log::info(logcat, "Starting from height {}, stopping at height {}", block_start, block_stop);
|
||||
|
||||
/*
|
||||
* The default output can be plotted with GnuPlot using these commands:
|
||||
set key autotitle columnhead
|
||||
set title "Oxen Blockchain Growth"
|
||||
set timefmt "%Y-%m-%d"
|
||||
set xdata time
|
||||
set xrange ["2014-04-17":*]
|
||||
set format x "%Y-%m-%d"
|
||||
set yrange [0:*]
|
||||
set y2range [0:*]
|
||||
set ylabel "Txs/Day"
|
||||
set y2label "Bytes"
|
||||
set y2tics nomirror
|
||||
plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, '' using
|
||||
(timecolumn(1,"%Y-%m-%d")):7 axes x1y2 with lines
|
||||
*/
|
||||
|
||||
// spit out a comment that GnuPlot can use as an index
|
||||
std::cout << "\n# DATA\n";
|
||||
std::cout << "Date\tBlocks/day\tBlocks\tTxs/Day\tTxs\tBytes/Day\tBytes";
|
||||
if (do_inputs)
|
||||
std::cout << "\tInMin\tInMax\tInAvg";
|
||||
if (do_outputs)
|
||||
std::cout << "\tOutMin\tOutMax\tOutAvg";
|
||||
if (do_ringsize)
|
||||
std::cout << "\tRingMin\tRingMax\tRingAvg";
|
||||
if (do_hours) {
|
||||
char buf[8];
|
||||
unsigned int i;
|
||||
for (i = 0; i < 24; i++) {
|
||||
sprintf(buf, "\t%02u:00", i);
|
||||
std::cout << buf;
|
||||
}
|
||||
}
|
||||
std::cout << "\n";
|
||||
|
||||
std::optional<std::chrono::system_clock::time_point> prev_ts;
|
||||
uint64_t prevsz = 0, currsz = 0;
|
||||
uint64_t prevtxs = 0, currtxs = 0;
|
||||
uint64_t currblks = 0;
|
||||
uint64_t totins = 0, totouts = 0, totrings = 0;
|
||||
uint32_t minins = 10, maxins = 0;
|
||||
uint32_t minouts = 10, maxouts = 0;
|
||||
uint32_t minrings = 50, maxrings = 0;
|
||||
uint32_t io, tottxs = 0;
|
||||
uint32_t txhr[24] = {0};
|
||||
unsigned int i;
|
||||
|
||||
for (uint64_t h = block_start; h < block_stop; ++h) {
|
||||
std::string bd = db->get_block_blob_from_height(h);
|
||||
cryptonote::block blk;
|
||||
if (!cryptonote::parse_and_validate_block_from_blob(bd, blk)) {
|
||||
log::warning(logcat, "Bad block from db");
|
||||
return 1;
|
||||
}
|
||||
auto ts = std::chrono::system_clock::from_time_t(blk.timestamp);
|
||||
using namespace date;
|
||||
year_month_day curr_date{floor<days>(ts)};
|
||||
if (!prev_ts)
|
||||
prev_ts = ts;
|
||||
year_month_day prev_date{floor<days>(*prev_ts)};
|
||||
// catch change of day
|
||||
if (curr_date.day() > prev_date.day() ||
|
||||
(curr_date.day() == day{1} && prev_date.day() > day{27})) {
|
||||
// check for timestamp fudging around month ends
|
||||
if (curr_date.day() == day{1} && prev_date.day() > day{27})
|
||||
goto skip;
|
||||
prev_ts = ts;
|
||||
std::cout << format("%Y-%m-%d", prev_date) << "\t" << currblks << "\t" << h << "\t"
|
||||
<< currtxs << "\t" << prevtxs + currtxs << "\t" << currsz << "\t"
|
||||
<< prevsz + currsz;
|
||||
prevsz += currsz;
|
||||
currsz = 0;
|
||||
currblks = 0;
|
||||
prevtxs += currtxs;
|
||||
currtxs = 0;
|
||||
if (!tottxs)
|
||||
tottxs = 1;
|
||||
if (do_inputs) {
|
||||
std::cout << "\t" << (maxins ? minins : 0) << "\t" << maxins << "\t"
|
||||
<< totins / tottxs;
|
||||
minins = 10;
|
||||
maxins = 0;
|
||||
totins = 0;
|
||||
}
|
||||
if (do_outputs) {
|
||||
std::cout << "\t" << (maxouts ? minouts : 0) << "\t" << maxouts << "\t"
|
||||
<< totouts / tottxs;
|
||||
minouts = 10;
|
||||
maxouts = 0;
|
||||
totouts = 0;
|
||||
}
|
||||
if (do_ringsize) {
|
||||
std::cout << "\t" << (maxrings ? minrings : 0) << "\t" << maxrings << "\t"
|
||||
<< totrings / tottxs;
|
||||
minrings = 50;
|
||||
maxrings = 0;
|
||||
totrings = 0;
|
||||
}
|
||||
tottxs = 0;
|
||||
if (do_hours) {
|
||||
for (i = 0; i < 24; i++) {
|
||||
std::cout << "\t" << txhr[i];
|
||||
txhr[i] = 0;
|
||||
}
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
skip:
|
||||
currsz += bd.size();
|
||||
for (const auto& tx_id : blk.tx_hashes) {
|
||||
if (!tx_id) {
|
||||
throw std::runtime_error("Aborting: null txid");
|
||||
}
|
||||
if (!db->get_pruned_tx_blob(tx_id, bd)) {
|
||||
throw std::runtime_error("Aborting: tx not found");
|
||||
}
|
||||
transaction tx;
|
||||
if (!parse_and_validate_tx_base_from_blob(bd, tx)) {
|
||||
log::warning(logcat, "Bad txn from db");
|
||||
return 1;
|
||||
}
|
||||
currsz += bd.size();
|
||||
currtxs++;
|
||||
if (do_hours)
|
||||
txhr[hh_mm_ss{ts - floor<days>(ts)}.hours().count()]++;
|
||||
if (do_inputs) {
|
||||
io = tx.vin.size();
|
||||
if (io < minins)
|
||||
minins = io;
|
||||
else if (io > maxins)
|
||||
maxins = io;
|
||||
totins += io;
|
||||
}
|
||||
if (do_ringsize) {
|
||||
const auto& tx_in_to_key = var::get<cryptonote::txin_to_key>(tx.vin[0]);
|
||||
io = tx_in_to_key.key_offsets.size();
|
||||
if (io < minrings)
|
||||
minrings = io;
|
||||
else if (io > maxrings)
|
||||
maxrings = io;
|
||||
totrings += io;
|
||||
}
|
||||
if (do_outputs) {
|
||||
io = tx.vout.size();
|
||||
if (io < minouts)
|
||||
minouts = io;
|
||||
else if (io > maxouts)
|
||||
maxouts = io;
|
||||
totouts += io;
|
||||
}
|
||||
tottxs++;
|
||||
}
|
||||
currblks++;
|
||||
|
||||
if (stop_requested)
|
||||
break;
|
||||
}
|
||||
|
||||
core_storage->deinit();
|
||||
return 0;
|
||||
|
||||
CATCH_ENTRY("Stats reporting error", 1);
|
||||
}
|
||||
|
|
|
@ -27,12 +27,12 @@
|
|||
// 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.
|
||||
|
||||
#include "common/command_line.h"
|
||||
#include "common/varint.h"
|
||||
#include "common/fs-format.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "blockchain_objects.h"
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "blockchain_objects.h"
|
||||
#include "common/command_line.h"
|
||||
#include "common/fs-format.h"
|
||||
#include "common/varint.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "cryptonote_core/uptime_proof.h"
|
||||
#include "version.h"
|
||||
|
||||
|
@ -41,203 +41,207 @@ using namespace cryptonote;
|
|||
|
||||
static auto logcat = log::Cat("quorum_cop");
|
||||
|
||||
struct output_data
|
||||
{
|
||||
uint64_t amount;
|
||||
uint64_t index;
|
||||
mutable bool coinbase;
|
||||
mutable uint64_t height;
|
||||
output_data(uint64_t a, uint64_t i, bool cb, uint64_t h): amount(a), index(i), coinbase(cb), height(h) {}
|
||||
bool operator==(const output_data &other) const { return other.amount == amount && other.index == index; }
|
||||
void info(bool c, uint64_t h) const { coinbase = c; height =h; }
|
||||
};
|
||||
namespace std
|
||||
{
|
||||
template<> struct hash<output_data>
|
||||
{
|
||||
size_t operator()(const output_data &od) const
|
||||
{
|
||||
const uint64_t data[2] = {od.amount, od.index};
|
||||
crypto::hash h;
|
||||
crypto::cn_fast_hash(data, 2 * sizeof(uint64_t), h);
|
||||
return reinterpret_cast<const std::size_t &>(h);
|
||||
struct output_data {
|
||||
uint64_t amount;
|
||||
uint64_t index;
|
||||
mutable bool coinbase;
|
||||
mutable uint64_t height;
|
||||
output_data(uint64_t a, uint64_t i, bool cb, uint64_t h) :
|
||||
amount(a), index(i), coinbase(cb), height(h) {}
|
||||
bool operator==(const output_data& other) const {
|
||||
return other.amount == amount && other.index == index;
|
||||
}
|
||||
};
|
||||
}
|
||||
void info(bool c, uint64_t h) const {
|
||||
coinbase = c;
|
||||
height = h;
|
||||
}
|
||||
};
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<output_data> {
|
||||
size_t operator()(const output_data& od) const {
|
||||
const uint64_t data[2] = {od.amount, od.index};
|
||||
crypto::hash h;
|
||||
crypto::cn_fast_hash(data, 2 * sizeof(uint64_t), h);
|
||||
return reinterpret_cast<const std::size_t&>(h);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
struct reference
|
||||
{
|
||||
uint64_t height;
|
||||
uint64_t ring_size;
|
||||
uint64_t position;
|
||||
reference(uint64_t h, uint64_t rs, uint64_t p): height(h), ring_size(rs), position(p) {}
|
||||
struct reference {
|
||||
uint64_t height;
|
||||
uint64_t ring_size;
|
||||
uint64_t position;
|
||||
reference(uint64_t h, uint64_t rs, uint64_t p) : height(h), ring_size(rs), position(p) {}
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
TRY_ENTRY();
|
||||
int main(int argc, char* argv[]) {
|
||||
TRY_ENTRY();
|
||||
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
tools::on_startup();
|
||||
auto opt_size = command_line::boost_option_sizes();
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
tools::on_startup();
|
||||
auto opt_size = command_line::boost_option_sizes();
|
||||
|
||||
po::options_description desc_cmd_only("Command line options", opt_size.first, opt_size.second);
|
||||
po::options_description desc_cmd_sett("Command line options and settings options", opt_size.first, opt_size.second);
|
||||
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
|
||||
const command_line::arg_descriptor<bool> arg_rct_only = {"rct-only", "Only work on ringCT outputs", false};
|
||||
const command_line::arg_descriptor<std::string> arg_input = {"input", ""};
|
||||
po::options_description desc_cmd_only("Command line options", opt_size.first, opt_size.second);
|
||||
po::options_description desc_cmd_sett(
|
||||
"Command line options and settings options", opt_size.first, opt_size.second);
|
||||
const command_line::arg_descriptor<std::string> arg_log_level = {
|
||||
"log-level", "0-4 or categories", ""};
|
||||
const command_line::arg_descriptor<bool> arg_rct_only = {
|
||||
"rct-only", "Only work on ringCT outputs", false};
|
||||
const command_line::arg_descriptor<std::string> arg_input = {"input", ""};
|
||||
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_devnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, arg_log_level);
|
||||
command_line::add_arg(desc_cmd_sett, arg_rct_only);
|
||||
command_line::add_arg(desc_cmd_sett, arg_input);
|
||||
command_line::add_arg(desc_cmd_only, command_line::arg_help);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_devnet_on);
|
||||
command_line::add_arg(desc_cmd_sett, arg_log_level);
|
||||
command_line::add_arg(desc_cmd_sett, arg_rct_only);
|
||||
command_line::add_arg(desc_cmd_sett, arg_input);
|
||||
command_line::add_arg(desc_cmd_only, command_line::arg_help);
|
||||
|
||||
po::options_description desc_options("Allowed options");
|
||||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
po::options_description desc_options("Allowed options");
|
||||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
|
||||
po::positional_options_description positional_options;
|
||||
positional_options.add(arg_input.name, -1);
|
||||
po::positional_options_description positional_options;
|
||||
positional_options.add(arg_input.name, -1);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
auto parser = po::command_line_parser(argc, argv).options(desc_options).positional(positional_options);
|
||||
po::store(parser.run(), vm);
|
||||
po::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (! r)
|
||||
return 1;
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]() {
|
||||
auto parser = po::command_line_parser(argc, argv)
|
||||
.options(desc_options)
|
||||
.positional(positional_options);
|
||||
po::store(parser.run(), vm);
|
||||
po::notify(vm);
|
||||
return true;
|
||||
});
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
std::cout << "Oxen '" << OXEN_RELEASE_NAME << "' (v" << OXEN_VERSION_FULL << ")\n\n";
|
||||
std::cout << desc_options << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
auto log_file_path = m_config_folder + "oxen-blockchain-usage.log";
|
||||
log::Level log_level;
|
||||
if(auto level = oxen::logging::parse_level(command_line::get_arg(vm, arg_log_level).c_str())) {
|
||||
log_level = *level;
|
||||
} else {
|
||||
std::cerr << "Incorrect log level: " << command_line::get_arg(vm, arg_log_level).c_str() << std::endl;
|
||||
throw std::runtime_error{"Incorrect log level"};
|
||||
}
|
||||
oxen::logging::init(log_file_path, log_level);
|
||||
log::warning(logcat, "Starting...");
|
||||
|
||||
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
||||
bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on);
|
||||
network_type net_type = opt_testnet ? network_type::TESTNET : opt_devnet ? network_type::DEVNET : network_type::MAINNET;
|
||||
bool opt_rct_only = command_line::get_arg(vm, arg_rct_only);
|
||||
|
||||
// If we wanted to use the memory pool, we would set up a fake_core.
|
||||
|
||||
// Use Blockchain instead of lower-level BlockchainDB for two reasons:
|
||||
// 1. Blockchain has the init() method for easy setup
|
||||
// 2. exporter needs to use get_current_blockchain_height(), get_block_id_by_height(), get_block_by_hash()
|
||||
//
|
||||
// cannot match blockchain_storage setup above with just one line,
|
||||
// e.g.
|
||||
// Blockchain* core_storage = new Blockchain(NULL);
|
||||
// because unlike blockchain_storage constructor, which takes a pointer to
|
||||
// tx_memory_pool, Blockchain's constructor takes tx_memory_pool object.
|
||||
log::warning(logcat, "Initializing source blockchain (BlockchainDB)");
|
||||
const std::string input = command_line::get_arg(vm, arg_input);
|
||||
blockchain_objects_t blockchain_objects = {};
|
||||
Blockchain *core_storage = &blockchain_objects.m_blockchain;
|
||||
tx_memory_pool& m_mempool = blockchain_objects.m_mempool;
|
||||
BlockchainDB *db = new_db();
|
||||
if (db == NULL)
|
||||
{
|
||||
log::error(logcat, "Failed to initialize a database");
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
log::warning(logcat, "database: LMDB");
|
||||
|
||||
const fs::path filename = fs::u8path(input);
|
||||
log::warning(logcat, "Loading blockchain from folder {} ...", filename);
|
||||
|
||||
try
|
||||
{
|
||||
db->open(filename, core_storage->nettype(), DBF_RDONLY);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = core_storage->init(db, nullptr /*ons_db*/, nullptr, net_type);
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
|
||||
log::warning(logcat, "Source blockchain storage initialized OK");
|
||||
|
||||
log::warning(logcat, "Building usage patterns...");
|
||||
|
||||
size_t done = 0;
|
||||
std::unordered_map<output_data, std::list<reference>> outputs;
|
||||
std::unordered_map<uint64_t,uint64_t> indices;
|
||||
|
||||
log::warning(logcat, "Reading blockchain from {}", input);
|
||||
core_storage->for_all_transactions([&](const crypto::hash &hash, const cryptonote::transaction &tx)->bool
|
||||
{
|
||||
const bool coinbase = tx.vin.size() == 1 && std::holds_alternative<txin_gen>(tx.vin[0]);
|
||||
const uint64_t height = core_storage->get_db().get_tx_block_height(hash);
|
||||
|
||||
// create new outputs
|
||||
for (const auto &out: tx.vout)
|
||||
{
|
||||
if (opt_rct_only && out.amount)
|
||||
continue;
|
||||
uint64_t index = indices[out.amount]++;
|
||||
output_data od(out.amount, indices[out.amount], coinbase, height);
|
||||
auto itb = outputs.emplace(od, std::list<reference>());
|
||||
itb.first->first.info(coinbase, height);
|
||||
if (command_line::get_arg(vm, command_line::arg_help)) {
|
||||
std::cout << "Oxen '" << OXEN_RELEASE_NAME << "' (v" << OXEN_VERSION_FULL << ")\n\n";
|
||||
std::cout << desc_options << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (const auto &in: tx.vin)
|
||||
{
|
||||
const auto* txin = std::get_if<txin_to_key>(&in);
|
||||
if (!txin || (opt_rct_only && txin->amount != 0))
|
||||
continue;
|
||||
|
||||
const std::vector<uint64_t> absolute = cryptonote::relative_output_offsets_to_absolute(txin->key_offsets);
|
||||
for (size_t n = 0; n < txin->key_offsets.size(); ++n)
|
||||
{
|
||||
output_data od(txin->amount, absolute[n], coinbase, height);
|
||||
outputs[od].push_back(reference(height, txin->key_offsets.size(), n));
|
||||
}
|
||||
auto m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
auto log_file_path = m_config_folder + "oxen-blockchain-usage.log";
|
||||
log::Level log_level;
|
||||
if (auto level = oxen::logging::parse_level(command_line::get_arg(vm, arg_log_level).c_str())) {
|
||||
log_level = *level;
|
||||
} else {
|
||||
std::cerr << "Incorrect log level: " << command_line::get_arg(vm, arg_log_level).c_str()
|
||||
<< std::endl;
|
||||
throw std::runtime_error{"Incorrect log level"};
|
||||
}
|
||||
return true;
|
||||
}, true);
|
||||
oxen::logging::init(log_file_path, log_level);
|
||||
log::warning(logcat, "Starting...");
|
||||
|
||||
std::unordered_map<uint64_t, uint64_t> counts;
|
||||
size_t total = 0;
|
||||
for (const auto &out: outputs)
|
||||
{
|
||||
counts[out.second.size()]++;
|
||||
total++;
|
||||
}
|
||||
if (total > 0)
|
||||
{
|
||||
for (const auto &c: counts)
|
||||
{
|
||||
float percent = 100.f * c.second / total;
|
||||
log::info(logcat, "{} outputs used {} times ({}%)", std::to_string(c.second), c.first, percent);
|
||||
bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
||||
bool opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on);
|
||||
network_type net_type = opt_testnet ? network_type::TESTNET
|
||||
: opt_devnet ? network_type::DEVNET
|
||||
: network_type::MAINNET;
|
||||
bool opt_rct_only = command_line::get_arg(vm, arg_rct_only);
|
||||
|
||||
// If we wanted to use the memory pool, we would set up a fake_core.
|
||||
|
||||
// Use Blockchain instead of lower-level BlockchainDB for two reasons:
|
||||
// 1. Blockchain has the init() method for easy setup
|
||||
// 2. exporter needs to use get_current_blockchain_height(), get_block_id_by_height(),
|
||||
// get_block_by_hash()
|
||||
//
|
||||
// cannot match blockchain_storage setup above with just one line,
|
||||
// e.g.
|
||||
// Blockchain* core_storage = new Blockchain(NULL);
|
||||
// because unlike blockchain_storage constructor, which takes a pointer to
|
||||
// tx_memory_pool, Blockchain's constructor takes tx_memory_pool object.
|
||||
log::warning(logcat, "Initializing source blockchain (BlockchainDB)");
|
||||
const std::string input = command_line::get_arg(vm, arg_input);
|
||||
blockchain_objects_t blockchain_objects = {};
|
||||
Blockchain* core_storage = &blockchain_objects.m_blockchain;
|
||||
tx_memory_pool& m_mempool = blockchain_objects.m_mempool;
|
||||
BlockchainDB* db = new_db();
|
||||
if (db == NULL) {
|
||||
log::error(logcat, "Failed to initialize a database");
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log::info(logcat, "No outputs to process");
|
||||
}
|
||||
log::warning(logcat, "database: LMDB");
|
||||
|
||||
log::warning(logcat, "Blockchain usage exported OK");
|
||||
return 0;
|
||||
const fs::path filename = fs::u8path(input);
|
||||
log::warning(logcat, "Loading blockchain from folder {} ...", filename);
|
||||
|
||||
CATCH_ENTRY("Export error", 1);
|
||||
try {
|
||||
db->open(filename, core_storage->nettype(), DBF_RDONLY);
|
||||
} catch (const std::exception& e) {
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = core_storage->init(db, nullptr /*ons_db*/, nullptr, net_type);
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
|
||||
log::warning(logcat, "Source blockchain storage initialized OK");
|
||||
|
||||
log::warning(logcat, "Building usage patterns...");
|
||||
|
||||
size_t done = 0;
|
||||
std::unordered_map<output_data, std::list<reference>> outputs;
|
||||
std::unordered_map<uint64_t, uint64_t> indices;
|
||||
|
||||
log::warning(logcat, "Reading blockchain from {}", input);
|
||||
core_storage->for_all_transactions(
|
||||
[&](const crypto::hash& hash, const cryptonote::transaction& tx) -> bool {
|
||||
const bool coinbase =
|
||||
tx.vin.size() == 1 && std::holds_alternative<txin_gen>(tx.vin[0]);
|
||||
const uint64_t height = core_storage->get_db().get_tx_block_height(hash);
|
||||
|
||||
// create new outputs
|
||||
for (const auto& out : tx.vout) {
|
||||
if (opt_rct_only && out.amount)
|
||||
continue;
|
||||
uint64_t index = indices[out.amount]++;
|
||||
output_data od(out.amount, indices[out.amount], coinbase, height);
|
||||
auto itb = outputs.emplace(od, std::list<reference>());
|
||||
itb.first->first.info(coinbase, height);
|
||||
}
|
||||
|
||||
for (const auto& in : tx.vin) {
|
||||
const auto* txin = std::get_if<txin_to_key>(&in);
|
||||
if (!txin || (opt_rct_only && txin->amount != 0))
|
||||
continue;
|
||||
|
||||
const std::vector<uint64_t> absolute =
|
||||
cryptonote::relative_output_offsets_to_absolute(txin->key_offsets);
|
||||
for (size_t n = 0; n < txin->key_offsets.size(); ++n) {
|
||||
output_data od(txin->amount, absolute[n], coinbase, height);
|
||||
outputs[od].push_back(reference(height, txin->key_offsets.size(), n));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
true);
|
||||
|
||||
std::unordered_map<uint64_t, uint64_t> counts;
|
||||
size_t total = 0;
|
||||
for (const auto& out : outputs) {
|
||||
counts[out.second.size()]++;
|
||||
total++;
|
||||
}
|
||||
if (total > 0) {
|
||||
for (const auto& c : counts) {
|
||||
float percent = 100.f * c.second / total;
|
||||
log::info(
|
||||
logcat,
|
||||
"{} outputs used {} times ({}%)",
|
||||
std::to_string(c.second),
|
||||
c.first,
|
||||
percent);
|
||||
}
|
||||
} else {
|
||||
log::info(logcat, "No outputs to process");
|
||||
}
|
||||
|
||||
log::warning(logcat, "Blockchain usage exported OK");
|
||||
return 0;
|
||||
|
||||
CATCH_ENTRY("Export error", 1);
|
||||
}
|
||||
|
|
|
@ -30,11 +30,12 @@
|
|||
|
||||
#include "version.h"
|
||||
|
||||
namespace blockchain_utils {
|
||||
|
||||
// bounds checking is done before writing to buffer, but buffer size
|
||||
// should be a sensible maximum
|
||||
#define BUFFER_SIZE (2 * 1024 * 1024)
|
||||
#define CHUNK_SIZE_WARNING_THRESHOLD 500000
|
||||
#define NUM_BLOCKS_PER_CHUNK 1
|
||||
#define BLOCKCHAIN_RAW "blockchain.raw"
|
||||
// bounds checking is done before writing to buffer, but buffer size should be a sensible maximum
|
||||
inline constexpr size_t BUFFER_SIZE = 2 * 1024 * 1024;
|
||||
inline constexpr size_t CHUNK_SIZE_WARNING_THRESHOLD = 500000;
|
||||
inline constexpr size_t NUM_BLOCKS_PER_CHUNK = 1;
|
||||
inline constexpr const char* BLOCKCHAIN_RAW = "blockchain.raw";
|
||||
|
||||
} // namespace blockchain_utils
|
||||
|
|
|
@ -28,144 +28,134 @@
|
|||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "blocksdat_file.h"
|
||||
|
||||
#include "common/fs-format.h"
|
||||
|
||||
using namespace cryptonote;
|
||||
using namespace blockchain_utils;
|
||||
|
||||
namespace
|
||||
{
|
||||
static auto logcat = log::Cat("bcutil");
|
||||
namespace {
|
||||
static auto logcat = log::Cat("bcutil");
|
||||
|
||||
std::string refresh_string = "\r \r";
|
||||
}
|
||||
std::string refresh_string = "\r \r";
|
||||
} // namespace
|
||||
|
||||
bool BlocksdatFile::open_writer(const fs::path& file_path, uint64_t block_stop) {
|
||||
const fs::path dir_path = file_path.parent_path();
|
||||
if (!dir_path.empty()) {
|
||||
if (fs::exists(dir_path)) {
|
||||
if (!fs::is_directory(dir_path)) {
|
||||
log::error(logcat, "export directory path is a file: {}", dir_path);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!fs::create_directory(dir_path)) {
|
||||
log::error(logcat, "Failed to create directory {}", dir_path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_raw_data_file = new std::ofstream();
|
||||
|
||||
bool BlocksdatFile::open_writer(const fs::path& file_path, uint64_t block_stop)
|
||||
{
|
||||
const fs::path dir_path = file_path.parent_path();
|
||||
if (!dir_path.empty())
|
||||
{
|
||||
if (fs::exists(dir_path))
|
||||
{
|
||||
if (!fs::is_directory(dir_path))
|
||||
{
|
||||
log::error(logcat, "export directory path is a file: {}", dir_path);
|
||||
log::info(logcat, "creating file");
|
||||
|
||||
m_raw_data_file->open(
|
||||
file_path.string(), std::ios_base::binary | std::ios_base::out | std::ios::trunc);
|
||||
if (m_raw_data_file->fail())
|
||||
return false;
|
||||
}
|
||||
|
||||
initialize_file(block_stop);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlocksdatFile::initialize_file(uint64_t block_stop) {
|
||||
const uint32_t nblocks = (block_stop + 1) / HASH_OF_HASHES_STEP;
|
||||
unsigned char nblocksc[4];
|
||||
|
||||
nblocksc[0] = nblocks & 0xff;
|
||||
nblocksc[1] = (nblocks >> 8) & 0xff;
|
||||
nblocksc[2] = (nblocks >> 16) & 0xff;
|
||||
nblocksc[3] = (nblocks >> 24) & 0xff;
|
||||
|
||||
// 4 bytes little endian
|
||||
*m_raw_data_file << nblocksc[0];
|
||||
*m_raw_data_file << nblocksc[1];
|
||||
*m_raw_data_file << nblocksc[2];
|
||||
*m_raw_data_file << nblocksc[3];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BlocksdatFile::write_block(const crypto::hash& block_hash) {
|
||||
m_hashes.push_back(block_hash);
|
||||
while (m_hashes.size() >= HASH_OF_HASHES_STEP) {
|
||||
crypto::hash hash;
|
||||
crypto::cn_fast_hash(m_hashes.data(), HASH_OF_HASHES_STEP * sizeof(crypto::hash), hash);
|
||||
memmove(m_hashes.data(),
|
||||
m_hashes.data() + HASH_OF_HASHES_STEP,
|
||||
(m_hashes.size() - HASH_OF_HASHES_STEP) * sizeof(crypto::hash));
|
||||
m_hashes.resize(m_hashes.size() - HASH_OF_HASHES_STEP);
|
||||
m_raw_data_file->write(reinterpret_cast<const char*>(hash.data()), hash.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!fs::create_directory(dir_path))
|
||||
{
|
||||
log::error(logcat, "Failed to create directory {}", dir_path);
|
||||
}
|
||||
|
||||
bool BlocksdatFile::close() {
|
||||
if (m_raw_data_file->fail())
|
||||
return false;
|
||||
}
|
||||
|
||||
m_raw_data_file->flush();
|
||||
delete m_raw_data_file;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlocksdatFile::store_blockchain_raw(
|
||||
Blockchain* _blockchain_storage,
|
||||
tx_memory_pool* _tx_pool,
|
||||
fs::path& output_file,
|
||||
uint64_t requested_block_stop) {
|
||||
uint64_t num_blocks_written = 0;
|
||||
m_blockchain_storage = _blockchain_storage;
|
||||
uint64_t progress_interval = 100;
|
||||
block b;
|
||||
|
||||
uint64_t block_start = 0;
|
||||
uint64_t block_stop = 0;
|
||||
log::info(
|
||||
logcat,
|
||||
"source blockchain height: {}",
|
||||
m_blockchain_storage->get_current_blockchain_height() - 1);
|
||||
if ((requested_block_stop > 0) &&
|
||||
(requested_block_stop < m_blockchain_storage->get_current_blockchain_height())) {
|
||||
log::info(logcat, "Using requested block height: {}", requested_block_stop);
|
||||
block_stop = requested_block_stop;
|
||||
} else {
|
||||
block_stop = m_blockchain_storage->get_current_blockchain_height() - 1;
|
||||
log::info(logcat, "Using block height of source blockchain: {}", block_stop);
|
||||
}
|
||||
}
|
||||
|
||||
m_raw_data_file = new std::ofstream();
|
||||
|
||||
log::info(logcat, "creating file");
|
||||
|
||||
m_raw_data_file->open(file_path.string(), std::ios_base::binary | std::ios_base::out | std::ios::trunc);
|
||||
if (m_raw_data_file->fail())
|
||||
return false;
|
||||
|
||||
initialize_file(block_stop);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool BlocksdatFile::initialize_file(uint64_t block_stop)
|
||||
{
|
||||
const uint32_t nblocks = (block_stop + 1) / HASH_OF_HASHES_STEP;
|
||||
unsigned char nblocksc[4];
|
||||
|
||||
nblocksc[0] = nblocks & 0xff;
|
||||
nblocksc[1] = (nblocks >> 8) & 0xff;
|
||||
nblocksc[2] = (nblocks >> 16) & 0xff;
|
||||
nblocksc[3] = (nblocks >> 24) & 0xff;
|
||||
|
||||
// 4 bytes little endian
|
||||
*m_raw_data_file << nblocksc[0];
|
||||
*m_raw_data_file << nblocksc[1];
|
||||
*m_raw_data_file << nblocksc[2];
|
||||
*m_raw_data_file << nblocksc[3];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BlocksdatFile::write_block(const crypto::hash& block_hash)
|
||||
{
|
||||
m_hashes.push_back(block_hash);
|
||||
while (m_hashes.size() >= HASH_OF_HASHES_STEP)
|
||||
{
|
||||
crypto::hash hash;
|
||||
crypto::cn_fast_hash(m_hashes.data(), HASH_OF_HASHES_STEP * sizeof(crypto::hash), hash);
|
||||
memmove(m_hashes.data(), m_hashes.data() + HASH_OF_HASHES_STEP, (m_hashes.size() - HASH_OF_HASHES_STEP) * sizeof(crypto::hash));
|
||||
m_hashes.resize(m_hashes.size() - HASH_OF_HASHES_STEP);
|
||||
m_raw_data_file->write(reinterpret_cast<const char*>(hash.data()), hash.size());
|
||||
}
|
||||
}
|
||||
|
||||
bool BlocksdatFile::close()
|
||||
{
|
||||
if (m_raw_data_file->fail())
|
||||
return false;
|
||||
|
||||
m_raw_data_file->flush();
|
||||
delete m_raw_data_file;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool BlocksdatFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_memory_pool* _tx_pool, fs::path& output_file, uint64_t requested_block_stop)
|
||||
{
|
||||
uint64_t num_blocks_written = 0;
|
||||
m_blockchain_storage = _blockchain_storage;
|
||||
uint64_t progress_interval = 100;
|
||||
block b;
|
||||
|
||||
uint64_t block_start = 0;
|
||||
uint64_t block_stop = 0;
|
||||
log::info(logcat, "source blockchain height: {}", m_blockchain_storage->get_current_blockchain_height()-1);
|
||||
if ((requested_block_stop > 0) && (requested_block_stop < m_blockchain_storage->get_current_blockchain_height()))
|
||||
{
|
||||
log::info(logcat, "Using requested block height: {}", requested_block_stop);
|
||||
block_stop = requested_block_stop;
|
||||
}
|
||||
else
|
||||
{
|
||||
block_stop = m_blockchain_storage->get_current_blockchain_height() - 1;
|
||||
log::info(logcat, "Using block height of source blockchain: {}", block_stop);
|
||||
}
|
||||
log::info(logcat, "Storing blocks raw data...");
|
||||
if (!BlocksdatFile::open_writer(output_file, block_stop))
|
||||
{
|
||||
log::error(logcat, "failed to open raw file for write");
|
||||
return false;
|
||||
}
|
||||
for (m_cur_height = block_start; m_cur_height <= block_stop; ++m_cur_height)
|
||||
{
|
||||
// this method's height refers to 0-based height (genesis block = height 0)
|
||||
crypto::hash hash = m_blockchain_storage->get_block_id_by_height(m_cur_height);
|
||||
write_block(hash);
|
||||
if (m_cur_height % NUM_BLOCKS_PER_CHUNK == 0) {
|
||||
num_blocks_written += NUM_BLOCKS_PER_CHUNK;
|
||||
log::info(logcat, "Storing blocks raw data...");
|
||||
if (!BlocksdatFile::open_writer(output_file, block_stop)) {
|
||||
log::error(logcat, "failed to open raw file for write");
|
||||
return false;
|
||||
}
|
||||
if (m_cur_height % progress_interval == 0) {
|
||||
std::cout << refresh_string;
|
||||
std::cout << "block " << m_cur_height << "/" << block_stop << std::flush;
|
||||
for (m_cur_height = block_start; m_cur_height <= block_stop; ++m_cur_height) {
|
||||
// this method's height refers to 0-based height (genesis block = height 0)
|
||||
crypto::hash hash = m_blockchain_storage->get_block_id_by_height(m_cur_height);
|
||||
write_block(hash);
|
||||
if (m_cur_height % NUM_BLOCKS_PER_CHUNK == 0) {
|
||||
num_blocks_written += NUM_BLOCKS_PER_CHUNK;
|
||||
}
|
||||
if (m_cur_height % progress_interval == 0) {
|
||||
std::cout << refresh_string;
|
||||
std::cout << "block " << m_cur_height << "/" << block_stop << std::flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
// print message for last block, which may not have been printed yet due to progress_interval
|
||||
std::cout << refresh_string;
|
||||
std::cout << "block " << m_cur_height-1 << "/" << block_stop << "\n";
|
||||
// print message for last block, which may not have been printed yet due to progress_interval
|
||||
std::cout << refresh_string;
|
||||
std::cout << "block " << m_cur_height - 1 << "/" << block_stop << "\n";
|
||||
|
||||
log::info(logcat, "Number of blocks exported: {}", num_blocks_written);
|
||||
log::info(logcat, "Number of blocks exported: {}", num_blocks_written);
|
||||
|
||||
return BlocksdatFile::close();
|
||||
return BlocksdatFile::close();
|
||||
}
|
||||
|
||||
|
|
|
@ -28,53 +28,47 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <boost/iostreams/stream_buffer.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <boost/iostreams/copy.hpp>
|
||||
#include <boost/iostreams/device/back_inserter.hpp>
|
||||
#include <boost/iostreams/filtering_streambuf.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <boost/iostreams/stream_buffer.hpp>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "blockchain_utilities.h"
|
||||
#include "common/command_line.h"
|
||||
#include "common/fs.h"
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
#include "cryptonote_basic/cryptonote_boost_serialization.h"
|
||||
#include "cryptonote_core/blockchain.h"
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <boost/iostreams/copy.hpp>
|
||||
#include <atomic>
|
||||
|
||||
#include "common/command_line.h"
|
||||
#include "common/fs.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "blockchain_utilities.h"
|
||||
|
||||
|
||||
using namespace cryptonote;
|
||||
|
||||
class BlocksdatFile {
|
||||
public:
|
||||
bool store_blockchain_raw(
|
||||
cryptonote::Blockchain* cs,
|
||||
cryptonote::tx_memory_pool* txp,
|
||||
fs::path& output_file,
|
||||
uint64_t use_block_height = 0);
|
||||
|
||||
class BlocksdatFile
|
||||
{
|
||||
public:
|
||||
protected:
|
||||
Blockchain* m_blockchain_storage;
|
||||
|
||||
bool store_blockchain_raw(cryptonote::Blockchain* cs, cryptonote::tx_memory_pool* txp,
|
||||
fs::path& output_file, uint64_t use_block_height=0);
|
||||
std::ofstream* m_raw_data_file;
|
||||
|
||||
protected:
|
||||
// open export file for write
|
||||
bool open_writer(const fs::path& file_path, uint64_t block_stop);
|
||||
bool initialize_file(uint64_t block_stop);
|
||||
bool close();
|
||||
void write_block(const crypto::hash& block_hash);
|
||||
|
||||
Blockchain* m_blockchain_storage;
|
||||
|
||||
std::ofstream * m_raw_data_file;
|
||||
|
||||
// open export file for write
|
||||
bool open_writer(const fs::path& file_path, uint64_t block_stop);
|
||||
bool initialize_file(uint64_t block_stop);
|
||||
bool close();
|
||||
void write_block(const crypto::hash &block_hash);
|
||||
|
||||
private:
|
||||
|
||||
uint64_t m_cur_height; // tracks current height during export
|
||||
std::vector<crypto::hash> m_hashes;
|
||||
private:
|
||||
uint64_t m_cur_height; // tracks current height during export
|
||||
std::vector<crypto::hash> m_hashes;
|
||||
};
|
||||
|
|
|
@ -27,483 +27,494 @@
|
|||
// 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.
|
||||
|
||||
#include "bootstrap_serialization.h"
|
||||
#include "serialization/binary_utils.h" // dump_binary(), parse_binary()
|
||||
|
||||
#include "bootstrap_file.h"
|
||||
|
||||
#include "bootstrap_serialization.h"
|
||||
#include "common/fs-format.h"
|
||||
#include "serialization/binary_utils.h" // dump_binary(), parse_binary()
|
||||
|
||||
using namespace cryptonote;
|
||||
using namespace blockchain_utils;
|
||||
|
||||
namespace
|
||||
{
|
||||
// This number was picked by taking the leading 4 bytes from this output:
|
||||
// echo Oxen bootstrap file | sha1sum
|
||||
const uint32_t blockchain_raw_magic = 0x28721586;
|
||||
const uint32_t header_size = 1024;
|
||||
namespace {
|
||||
// This number was picked by taking the leading 4 bytes from this output:
|
||||
// echo Oxen bootstrap file | sha1sum
|
||||
const uint32_t blockchain_raw_magic = 0x28721586;
|
||||
const uint32_t header_size = 1024;
|
||||
|
||||
std::string refresh_string = "\r \r";
|
||||
auto logcat = log::Cat("bcutil");
|
||||
}
|
||||
std::string refresh_string = "\r \r";
|
||||
auto logcat = log::Cat("bcutil");
|
||||
} // namespace
|
||||
|
||||
|
||||
|
||||
bool BootstrapFile::open_writer(const fs::path& file_path)
|
||||
{
|
||||
const auto dir_path = file_path.parent_path();
|
||||
if (!dir_path.empty())
|
||||
{
|
||||
if (fs::exists(dir_path))
|
||||
{
|
||||
if (!fs::is_directory(dir_path))
|
||||
{
|
||||
log::error(logcat, "export directory path is a file: {}", dir_path);
|
||||
return false;
|
||||
}
|
||||
bool BootstrapFile::open_writer(const fs::path& file_path) {
|
||||
const auto dir_path = file_path.parent_path();
|
||||
if (!dir_path.empty()) {
|
||||
if (fs::exists(dir_path)) {
|
||||
if (!fs::is_directory(dir_path)) {
|
||||
log::error(logcat, "export directory path is a file: {}", dir_path);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!fs::create_directory(dir_path)) {
|
||||
log::error(logcat, "Failed to create directory {}", dir_path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_raw_data_file = new std::ofstream();
|
||||
|
||||
bool do_initialize_file = false;
|
||||
uint64_t num_blocks = 0;
|
||||
|
||||
if (!fs::exists(file_path)) {
|
||||
log::debug(logcat, "creating file");
|
||||
do_initialize_file = true;
|
||||
num_blocks = 0;
|
||||
} else {
|
||||
num_blocks = count_blocks(file_path.string());
|
||||
log::debug(
|
||||
logcat,
|
||||
"appending to existing file with height: {} total blocks: {}",
|
||||
num_blocks - 1,
|
||||
num_blocks);
|
||||
}
|
||||
m_height = num_blocks;
|
||||
|
||||
if (do_initialize_file)
|
||||
m_raw_data_file->open(
|
||||
file_path.string(), std::ios_base::binary | std::ios_base::out | std::ios::trunc);
|
||||
else
|
||||
{
|
||||
if (!fs::create_directory(dir_path))
|
||||
{
|
||||
log::error(logcat, "Failed to create directory {}", dir_path);
|
||||
m_raw_data_file->open(
|
||||
file_path.string(),
|
||||
std::ios_base::binary | std::ios_base::out | std::ios::app | std::ios::ate);
|
||||
|
||||
if (m_raw_data_file->fail())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_raw_data_file = new std::ofstream();
|
||||
m_output_stream =
|
||||
new boost::iostreams::stream<boost::iostreams::back_insert_device<buffer_type>>(
|
||||
m_buffer);
|
||||
if (m_output_stream == nullptr)
|
||||
return false;
|
||||
|
||||
bool do_initialize_file = false;
|
||||
uint64_t num_blocks = 0;
|
||||
if (do_initialize_file)
|
||||
initialize_file();
|
||||
|
||||
if (! fs::exists(file_path))
|
||||
{
|
||||
log::debug(logcat, "creating file");
|
||||
do_initialize_file = true;
|
||||
num_blocks = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
num_blocks = count_blocks(file_path.string());
|
||||
log::debug(logcat, "appending to existing file with height: {} total blocks: {}", num_blocks-1, num_blocks);
|
||||
}
|
||||
m_height = num_blocks;
|
||||
|
||||
if (do_initialize_file)
|
||||
m_raw_data_file->open(file_path.string(), std::ios_base::binary | std::ios_base::out | std::ios::trunc);
|
||||
else
|
||||
m_raw_data_file->open(file_path.string(), std::ios_base::binary | std::ios_base::out | std::ios::app | std::ios::ate);
|
||||
|
||||
if (m_raw_data_file->fail())
|
||||
return false;
|
||||
|
||||
m_output_stream = new boost::iostreams::stream<boost::iostreams::back_insert_device<buffer_type>>(m_buffer);
|
||||
if (m_output_stream == nullptr)
|
||||
return false;
|
||||
|
||||
if (do_initialize_file)
|
||||
initialize_file();
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BootstrapFile::initialize_file() {
|
||||
const uint32_t file_magic = blockchain_raw_magic;
|
||||
|
||||
bool BootstrapFile::initialize_file()
|
||||
{
|
||||
const uint32_t file_magic = blockchain_raw_magic;
|
||||
|
||||
std::string blob;
|
||||
try {
|
||||
blob = serialization::dump_binary(file_magic);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Error in serialization of file magic: "s + e.what());
|
||||
}
|
||||
*m_raw_data_file << blob;
|
||||
|
||||
bootstrap::file_info bfi;
|
||||
bfi.major_version = 0;
|
||||
bfi.minor_version = 1;
|
||||
bfi.header_size = header_size;
|
||||
|
||||
bootstrap::blocks_info bbi;
|
||||
bbi.block_first = 0;
|
||||
bbi.block_last = 0;
|
||||
bbi.block_last_pos = 0;
|
||||
|
||||
buffer_type buffer2;
|
||||
boost::iostreams::stream<boost::iostreams::back_insert_device<buffer_type>> output_stream_header(buffer2);
|
||||
|
||||
uint32_t bd_size = 0;
|
||||
|
||||
std::string bd = t_serializable_object_to_blob(bfi);
|
||||
log::debug(logcat, "bootstrap::file_info size: {}", bd.size());
|
||||
bd_size = bd.size();
|
||||
|
||||
try {
|
||||
blob = serialization::dump_binary(bd_size);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Error in serialization of bootstrap::file_info size: "s + e.what());
|
||||
}
|
||||
output_stream_header << blob;
|
||||
output_stream_header << bd;
|
||||
|
||||
bd = t_serializable_object_to_blob(bbi);
|
||||
log::debug(logcat, "bootstrap::blocks_info size: {}", bd.size());
|
||||
bd_size = bd.size();
|
||||
|
||||
try {
|
||||
blob = serialization::dump_binary(bd_size);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Error in serialization of bootstrap::blocks_info size: "s + e.what());
|
||||
}
|
||||
output_stream_header << blob;
|
||||
output_stream_header << bd;
|
||||
|
||||
output_stream_header.flush();
|
||||
output_stream_header << std::string(header_size-buffer2.size(), 0); // fill in rest with null bytes
|
||||
output_stream_header.flush();
|
||||
std::copy(buffer2.begin(), buffer2.end(), std::ostreambuf_iterator<char>(*m_raw_data_file));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BootstrapFile::flush_chunk()
|
||||
{
|
||||
m_output_stream->flush();
|
||||
|
||||
uint32_t chunk_size = m_buffer.size();
|
||||
// log::trace(logcat, "chunk_size {}", chunk_size);
|
||||
if (chunk_size > BUFFER_SIZE)
|
||||
{
|
||||
log::warning(logcat, "WARNING: chunk_size {} > BUFFER_SIZE {}", chunk_size, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
std::string blob;
|
||||
try {
|
||||
blob = serialization::dump_binary(chunk_size);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Error in serialization of chunk size: "s + e.what());
|
||||
}
|
||||
*m_raw_data_file << blob;
|
||||
|
||||
if (m_max_chunk < chunk_size)
|
||||
{
|
||||
m_max_chunk = chunk_size;
|
||||
}
|
||||
long pos_before = m_raw_data_file->tellp();
|
||||
std::copy(m_buffer.begin(), m_buffer.end(), std::ostreambuf_iterator<char>(*m_raw_data_file));
|
||||
m_raw_data_file->flush();
|
||||
long pos_after = m_raw_data_file->tellp();
|
||||
long num_chars_written = pos_after - pos_before;
|
||||
if (static_cast<unsigned long>(num_chars_written) != chunk_size)
|
||||
{
|
||||
log::error(logcat, "Error writing chunk: height: {} chunk_size: {} num chars written: {}", m_cur_height, chunk_size, num_chars_written);
|
||||
throw std::runtime_error("Error writing chunk");
|
||||
}
|
||||
|
||||
m_buffer.clear();
|
||||
delete m_output_stream;
|
||||
m_output_stream = new boost::iostreams::stream<boost::iostreams::back_insert_device<buffer_type>>(m_buffer);
|
||||
log::debug(logcat, "flushed chunk: chunk_size: {}", chunk_size);
|
||||
}
|
||||
|
||||
void BootstrapFile::write_block(block& block)
|
||||
{
|
||||
bootstrap::block_package bp;
|
||||
bp.block = block;
|
||||
|
||||
std::vector<transaction> txs;
|
||||
|
||||
uint64_t block_height = var::get<txin_gen>(block.miner_tx.vin.front()).height;
|
||||
|
||||
|
||||
// now add all regular transactions
|
||||
for (const auto& tx_id : block.tx_hashes)
|
||||
{
|
||||
if (!tx_id)
|
||||
{
|
||||
throw std::runtime_error("Aborting: null txid");
|
||||
}
|
||||
transaction tx = m_blockchain_storage->get_db().get_tx(tx_id);
|
||||
|
||||
txs.push_back(tx);
|
||||
}
|
||||
|
||||
// these non-coinbase txs will be serialized using this structure
|
||||
bp.txs = txs;
|
||||
|
||||
// These three attributes are currently necessary for a fast import that adds blocks without verification.
|
||||
bool include_extra_block_data = true;
|
||||
if (include_extra_block_data)
|
||||
{
|
||||
size_t block_weight = m_blockchain_storage->get_db().get_block_weight(block_height);
|
||||
difficulty_type cumulative_difficulty = m_blockchain_storage->get_db().get_block_cumulative_difficulty(block_height);
|
||||
uint64_t coins_generated = m_blockchain_storage->get_db().get_block_already_generated_coins(block_height);
|
||||
|
||||
bp.block_weight = block_weight;
|
||||
bp.cumulative_difficulty = cumulative_difficulty;
|
||||
bp.coins_generated = coins_generated;
|
||||
}
|
||||
|
||||
std::string bd = t_serializable_object_to_blob(bp);
|
||||
m_output_stream->write((const char*)bd.data(), bd.size());
|
||||
}
|
||||
|
||||
bool BootstrapFile::close()
|
||||
{
|
||||
if (m_raw_data_file->fail())
|
||||
return false;
|
||||
|
||||
m_raw_data_file->flush();
|
||||
delete m_output_stream;
|
||||
delete m_raw_data_file;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_memory_pool* _tx_pool, fs::path& output_file, uint64_t requested_block_stop)
|
||||
{
|
||||
uint64_t num_blocks_written = 0;
|
||||
m_max_chunk = 0;
|
||||
m_blockchain_storage = _blockchain_storage;
|
||||
m_tx_pool = _tx_pool;
|
||||
uint64_t progress_interval = 100;
|
||||
log::info(logcat, "Storing blocks raw data...");
|
||||
if (!BootstrapFile::open_writer(output_file))
|
||||
{
|
||||
log::error(logcat, "failed to open raw file for write");
|
||||
return false;
|
||||
}
|
||||
block b;
|
||||
|
||||
// block_start, block_stop use 0-based height. m_height uses 1-based height. So to resume export
|
||||
// from last exported block, block_start doesn't need to add 1 here, as it's already at the next
|
||||
// height.
|
||||
uint64_t block_start = m_height;
|
||||
uint64_t block_stop = 0;
|
||||
log::info(logcat, "source blockchain height: {}", m_blockchain_storage->get_current_blockchain_height()-1);
|
||||
if ((requested_block_stop > 0) && (requested_block_stop < m_blockchain_storage->get_current_blockchain_height()))
|
||||
{
|
||||
log::info(logcat, "Using requested block height: {}", requested_block_stop);
|
||||
block_stop = requested_block_stop;
|
||||
}
|
||||
else
|
||||
{
|
||||
block_stop = m_blockchain_storage->get_current_blockchain_height() - 1;
|
||||
log::info(logcat, "Using block height of source blockchain: {}", block_stop);
|
||||
}
|
||||
for (m_cur_height = block_start; m_cur_height <= block_stop; ++m_cur_height)
|
||||
{
|
||||
// this method's height refers to 0-based height (genesis block = height 0)
|
||||
crypto::hash hash = m_blockchain_storage->get_block_id_by_height(m_cur_height);
|
||||
m_blockchain_storage->get_block_by_hash(hash, b);
|
||||
write_block(b);
|
||||
if (m_cur_height % NUM_BLOCKS_PER_CHUNK == 0) {
|
||||
flush_chunk();
|
||||
num_blocks_written += NUM_BLOCKS_PER_CHUNK;
|
||||
}
|
||||
if (m_cur_height % progress_interval == 0) {
|
||||
std::cout << refresh_string;
|
||||
std::cout << "block " << m_cur_height << "/" << block_stop << "\r" << std::flush;
|
||||
}
|
||||
}
|
||||
// NOTE: use of NUM_BLOCKS_PER_CHUNK is a placeholder in case multi-block chunks are later supported.
|
||||
if (m_cur_height % NUM_BLOCKS_PER_CHUNK != 0)
|
||||
{
|
||||
flush_chunk();
|
||||
}
|
||||
// print message for last block, which may not have been printed yet due to progress_interval
|
||||
std::cout << refresh_string;
|
||||
std::cout << "block " << m_cur_height-1 << "/" << block_stop << "\n";
|
||||
|
||||
log::info(logcat, "Number of blocks exported: {}", num_blocks_written);
|
||||
if (num_blocks_written > 0)
|
||||
log::info(logcat, "Largest chunk: {} bytes", m_max_chunk);
|
||||
|
||||
return BootstrapFile::close();
|
||||
}
|
||||
|
||||
uint64_t BootstrapFile::seek_to_first_chunk(fs::ifstream& import_file)
|
||||
{
|
||||
uint32_t file_magic;
|
||||
|
||||
std::string str1;
|
||||
char buf1[2048];
|
||||
import_file.read(buf1, sizeof(file_magic));
|
||||
if (! import_file)
|
||||
throw std::runtime_error("Error reading expected number of bytes");
|
||||
str1.assign(buf1, sizeof(file_magic));
|
||||
|
||||
try {
|
||||
serialization::parse_binary(str1, file_magic);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Error in deserialization of file_magic: "s + e.what());
|
||||
}
|
||||
|
||||
if (file_magic != blockchain_raw_magic)
|
||||
{
|
||||
log::error(logcat, "bootstrap file not recognized");
|
||||
throw std::runtime_error("Aborting");
|
||||
}
|
||||
else
|
||||
log::info(logcat, "bootstrap file recognized");
|
||||
|
||||
uint32_t buflen_file_info;
|
||||
|
||||
import_file.read(buf1, sizeof(buflen_file_info));
|
||||
str1.assign(buf1, sizeof(buflen_file_info));
|
||||
if (! import_file)
|
||||
throw std::runtime_error("Error reading expected number of bytes");
|
||||
try {
|
||||
serialization::parse_binary(str1, buflen_file_info);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Error in deserialization of buflen_file_info: "s + e.what());
|
||||
}
|
||||
log::info(logcat, "bootstrap::file_info size: {}", buflen_file_info);
|
||||
|
||||
if (buflen_file_info > sizeof(buf1))
|
||||
throw std::runtime_error("Error: bootstrap::file_info size exceeds buffer size");
|
||||
import_file.read(buf1, buflen_file_info);
|
||||
if (! import_file)
|
||||
throw std::runtime_error("Error reading expected number of bytes");
|
||||
str1.assign(buf1, buflen_file_info);
|
||||
bootstrap::file_info bfi;
|
||||
try {
|
||||
serialization::parse_binary(str1, bfi);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Error in deserialization of bootstrap::file_info: "s + e.what());
|
||||
}
|
||||
log::info(logcat, "bootstrap file v{}.{}", unsigned(bfi.major_version), unsigned(bfi.minor_version));
|
||||
log::info(logcat, "bootstrap magic size: {}", sizeof(file_magic));
|
||||
log::info(logcat, "bootstrap header size: {}", bfi.header_size);
|
||||
|
||||
uint64_t full_header_size = sizeof(file_magic) + bfi.header_size;
|
||||
import_file.seekg(full_header_size);
|
||||
|
||||
return full_header_size;
|
||||
}
|
||||
|
||||
uint64_t BootstrapFile::count_bytes(fs::ifstream& import_file, uint64_t blocks, uint64_t& h, bool& quit)
|
||||
{
|
||||
uint64_t bytes_read = 0;
|
||||
uint32_t chunk_size;
|
||||
char buf1[sizeof(chunk_size)];
|
||||
std::string str1;
|
||||
h = 0;
|
||||
while (1)
|
||||
{
|
||||
import_file.read(buf1, sizeof(chunk_size));
|
||||
if (!import_file) {
|
||||
std::cout << refresh_string;
|
||||
log::debug(logcat, "End of file reached");
|
||||
quit = true;
|
||||
break;
|
||||
}
|
||||
bytes_read += sizeof(chunk_size);
|
||||
str1.assign(buf1, sizeof(chunk_size));
|
||||
std::string blob;
|
||||
try {
|
||||
serialization::parse_binary(str1, chunk_size);
|
||||
blob = serialization::dump_binary(file_magic);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Error in deserialization of chunk_size: "s + e.what());
|
||||
throw std::runtime_error("Error in serialization of file magic: "s + e.what());
|
||||
}
|
||||
log::debug(logcat, "chunk_size: {}", chunk_size);
|
||||
*m_raw_data_file << blob;
|
||||
|
||||
if (chunk_size > BUFFER_SIZE)
|
||||
{
|
||||
std::cout << refresh_string;
|
||||
log::warning(logcat, "WARNING: chunk_size {} > BUFFER_SIZE {} height: {}, offset {}", chunk_size, BUFFER_SIZE, h-1, bytes_read);
|
||||
throw std::runtime_error("Aborting: chunk size exceeds buffer size");
|
||||
bootstrap::file_info bfi;
|
||||
bfi.major_version = 0;
|
||||
bfi.minor_version = 1;
|
||||
bfi.header_size = header_size;
|
||||
|
||||
bootstrap::blocks_info bbi;
|
||||
bbi.block_first = 0;
|
||||
bbi.block_last = 0;
|
||||
bbi.block_last_pos = 0;
|
||||
|
||||
buffer_type buffer2;
|
||||
boost::iostreams::stream<boost::iostreams::back_insert_device<buffer_type>>
|
||||
output_stream_header(buffer2);
|
||||
|
||||
uint32_t bd_size = 0;
|
||||
|
||||
std::string bd = t_serializable_object_to_blob(bfi);
|
||||
log::debug(logcat, "bootstrap::file_info size: {}", bd.size());
|
||||
bd_size = bd.size();
|
||||
|
||||
try {
|
||||
blob = serialization::dump_binary(bd_size);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error(
|
||||
"Error in serialization of bootstrap::file_info size: "s + e.what());
|
||||
}
|
||||
if (chunk_size > CHUNK_SIZE_WARNING_THRESHOLD)
|
||||
{
|
||||
std::cout << refresh_string;
|
||||
log::debug(logcat, "NOTE: chunk_size {} > {} height: {}, offset {}", chunk_size, CHUNK_SIZE_WARNING_THRESHOLD, h-1, bytes_read);
|
||||
output_stream_header << blob;
|
||||
output_stream_header << bd;
|
||||
|
||||
bd = t_serializable_object_to_blob(bbi);
|
||||
log::debug(logcat, "bootstrap::blocks_info size: {}", bd.size());
|
||||
bd_size = bd.size();
|
||||
|
||||
try {
|
||||
blob = serialization::dump_binary(bd_size);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error(
|
||||
"Error in serialization of bootstrap::blocks_info size: "s + e.what());
|
||||
}
|
||||
else if (chunk_size <= 0) {
|
||||
std::cout << refresh_string;
|
||||
log::debug(logcat, "ERROR: chunk_size {} <= 0 height: {}, offset {}", chunk_size, h-1, bytes_read);
|
||||
throw std::runtime_error("Aborting");
|
||||
}
|
||||
// skip to next expected block size value
|
||||
import_file.seekg(chunk_size, std::ios_base::cur);
|
||||
if (! import_file) {
|
||||
std::cout << refresh_string;
|
||||
log::error(logcat, "ERROR: unexpected end of file: bytes read before error: {} of chunk_size {}", import_file.gcount(), chunk_size);
|
||||
throw std::runtime_error("Aborting");
|
||||
}
|
||||
bytes_read += chunk_size;
|
||||
h += NUM_BLOCKS_PER_CHUNK;
|
||||
if (h >= blocks)
|
||||
break;
|
||||
}
|
||||
return bytes_read;
|
||||
output_stream_header << blob;
|
||||
output_stream_header << bd;
|
||||
|
||||
output_stream_header.flush();
|
||||
output_stream_header << std::string(
|
||||
header_size - buffer2.size(), 0); // fill in rest with null bytes
|
||||
output_stream_header.flush();
|
||||
std::copy(buffer2.begin(), buffer2.end(), std::ostreambuf_iterator<char>(*m_raw_data_file));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t BootstrapFile::count_blocks(const fs::path& import_file_path)
|
||||
{
|
||||
std::streampos dummy_pos;
|
||||
uint64_t dummy_height = 0;
|
||||
return count_blocks(import_file_path, dummy_pos, dummy_height);
|
||||
void BootstrapFile::flush_chunk() {
|
||||
m_output_stream->flush();
|
||||
|
||||
uint32_t chunk_size = m_buffer.size();
|
||||
// log::trace(logcat, "chunk_size {}", chunk_size);
|
||||
if (chunk_size > BUFFER_SIZE) {
|
||||
log::warning(logcat, "WARNING: chunk_size {} > BUFFER_SIZE {}", chunk_size, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
std::string blob;
|
||||
try {
|
||||
blob = serialization::dump_binary(chunk_size);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Error in serialization of chunk size: "s + e.what());
|
||||
}
|
||||
*m_raw_data_file << blob;
|
||||
|
||||
if (m_max_chunk < chunk_size) {
|
||||
m_max_chunk = chunk_size;
|
||||
}
|
||||
long pos_before = m_raw_data_file->tellp();
|
||||
std::copy(m_buffer.begin(), m_buffer.end(), std::ostreambuf_iterator<char>(*m_raw_data_file));
|
||||
m_raw_data_file->flush();
|
||||
long pos_after = m_raw_data_file->tellp();
|
||||
long num_chars_written = pos_after - pos_before;
|
||||
if (static_cast<unsigned long>(num_chars_written) != chunk_size) {
|
||||
log::error(
|
||||
logcat,
|
||||
"Error writing chunk: height: {} chunk_size: {} num chars written: {}",
|
||||
m_cur_height,
|
||||
chunk_size,
|
||||
num_chars_written);
|
||||
throw std::runtime_error("Error writing chunk");
|
||||
}
|
||||
|
||||
m_buffer.clear();
|
||||
delete m_output_stream;
|
||||
m_output_stream =
|
||||
new boost::iostreams::stream<boost::iostreams::back_insert_device<buffer_type>>(
|
||||
m_buffer);
|
||||
log::debug(logcat, "flushed chunk: chunk_size: {}", chunk_size);
|
||||
}
|
||||
|
||||
void BootstrapFile::write_block(block& block) {
|
||||
bootstrap::block_package bp;
|
||||
bp.block = block;
|
||||
|
||||
std::vector<transaction> txs;
|
||||
|
||||
uint64_t block_height = var::get<txin_gen>(block.miner_tx.vin.front()).height;
|
||||
|
||||
// now add all regular transactions
|
||||
for (const auto& tx_id : block.tx_hashes) {
|
||||
if (!tx_id) {
|
||||
throw std::runtime_error("Aborting: null txid");
|
||||
}
|
||||
transaction tx = m_blockchain_storage->get_db().get_tx(tx_id);
|
||||
|
||||
txs.push_back(tx);
|
||||
}
|
||||
|
||||
// these non-coinbase txs will be serialized using this structure
|
||||
bp.txs = txs;
|
||||
|
||||
// These three attributes are currently necessary for a fast import that adds blocks without
|
||||
// verification.
|
||||
bool include_extra_block_data = true;
|
||||
if (include_extra_block_data) {
|
||||
size_t block_weight = m_blockchain_storage->get_db().get_block_weight(block_height);
|
||||
difficulty_type cumulative_difficulty =
|
||||
m_blockchain_storage->get_db().get_block_cumulative_difficulty(block_height);
|
||||
uint64_t coins_generated =
|
||||
m_blockchain_storage->get_db().get_block_already_generated_coins(block_height);
|
||||
|
||||
bp.block_weight = block_weight;
|
||||
bp.cumulative_difficulty = cumulative_difficulty;
|
||||
bp.coins_generated = coins_generated;
|
||||
}
|
||||
|
||||
std::string bd = t_serializable_object_to_blob(bp);
|
||||
m_output_stream->write((const char*)bd.data(), bd.size());
|
||||
}
|
||||
|
||||
bool BootstrapFile::close() {
|
||||
if (m_raw_data_file->fail())
|
||||
return false;
|
||||
|
||||
m_raw_data_file->flush();
|
||||
delete m_output_stream;
|
||||
delete m_raw_data_file;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BootstrapFile::store_blockchain_raw(
|
||||
Blockchain* _blockchain_storage,
|
||||
tx_memory_pool* _tx_pool,
|
||||
fs::path& output_file,
|
||||
uint64_t requested_block_stop) {
|
||||
uint64_t num_blocks_written = 0;
|
||||
m_max_chunk = 0;
|
||||
m_blockchain_storage = _blockchain_storage;
|
||||
m_tx_pool = _tx_pool;
|
||||
uint64_t progress_interval = 100;
|
||||
log::info(logcat, "Storing blocks raw data...");
|
||||
if (!BootstrapFile::open_writer(output_file)) {
|
||||
log::error(logcat, "failed to open raw file for write");
|
||||
return false;
|
||||
}
|
||||
block b;
|
||||
|
||||
// block_start, block_stop use 0-based height. m_height uses 1-based height. So to resume export
|
||||
// from last exported block, block_start doesn't need to add 1 here, as it's already at the next
|
||||
// height.
|
||||
uint64_t block_start = m_height;
|
||||
uint64_t block_stop = 0;
|
||||
log::info(
|
||||
logcat,
|
||||
"source blockchain height: {}",
|
||||
m_blockchain_storage->get_current_blockchain_height() - 1);
|
||||
if ((requested_block_stop > 0) &&
|
||||
(requested_block_stop < m_blockchain_storage->get_current_blockchain_height())) {
|
||||
log::info(logcat, "Using requested block height: {}", requested_block_stop);
|
||||
block_stop = requested_block_stop;
|
||||
} else {
|
||||
block_stop = m_blockchain_storage->get_current_blockchain_height() - 1;
|
||||
log::info(logcat, "Using block height of source blockchain: {}", block_stop);
|
||||
}
|
||||
for (m_cur_height = block_start; m_cur_height <= block_stop; ++m_cur_height) {
|
||||
// this method's height refers to 0-based height (genesis block = height 0)
|
||||
crypto::hash hash = m_blockchain_storage->get_block_id_by_height(m_cur_height);
|
||||
m_blockchain_storage->get_block_by_hash(hash, b);
|
||||
write_block(b);
|
||||
if (m_cur_height % NUM_BLOCKS_PER_CHUNK == 0) {
|
||||
flush_chunk();
|
||||
num_blocks_written += NUM_BLOCKS_PER_CHUNK;
|
||||
}
|
||||
if (m_cur_height % progress_interval == 0) {
|
||||
std::cout << refresh_string;
|
||||
std::cout << "block " << m_cur_height << "/" << block_stop << "\r" << std::flush;
|
||||
}
|
||||
}
|
||||
// NOTE: use of NUM_BLOCKS_PER_CHUNK is a placeholder in case multi-block chunks are later
|
||||
// supported.
|
||||
if (m_cur_height % NUM_BLOCKS_PER_CHUNK != 0) {
|
||||
flush_chunk();
|
||||
}
|
||||
// print message for last block, which may not have been printed yet due to progress_interval
|
||||
std::cout << refresh_string;
|
||||
std::cout << "block " << m_cur_height - 1 << "/" << block_stop << "\n";
|
||||
|
||||
log::info(logcat, "Number of blocks exported: {}", num_blocks_written);
|
||||
if (num_blocks_written > 0)
|
||||
log::info(logcat, "Largest chunk: {} bytes", m_max_chunk);
|
||||
|
||||
return BootstrapFile::close();
|
||||
}
|
||||
|
||||
uint64_t BootstrapFile::seek_to_first_chunk(fs::ifstream& import_file) {
|
||||
uint32_t file_magic;
|
||||
|
||||
std::string str1;
|
||||
char buf1[2048];
|
||||
import_file.read(buf1, sizeof(file_magic));
|
||||
if (!import_file)
|
||||
throw std::runtime_error("Error reading expected number of bytes");
|
||||
str1.assign(buf1, sizeof(file_magic));
|
||||
|
||||
try {
|
||||
serialization::parse_binary(str1, file_magic);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Error in deserialization of file_magic: "s + e.what());
|
||||
}
|
||||
|
||||
if (file_magic != blockchain_raw_magic) {
|
||||
log::error(logcat, "bootstrap file not recognized");
|
||||
throw std::runtime_error("Aborting");
|
||||
} else
|
||||
log::info(logcat, "bootstrap file recognized");
|
||||
|
||||
uint32_t buflen_file_info;
|
||||
|
||||
import_file.read(buf1, sizeof(buflen_file_info));
|
||||
str1.assign(buf1, sizeof(buflen_file_info));
|
||||
if (!import_file)
|
||||
throw std::runtime_error("Error reading expected number of bytes");
|
||||
try {
|
||||
serialization::parse_binary(str1, buflen_file_info);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Error in deserialization of buflen_file_info: "s + e.what());
|
||||
}
|
||||
log::info(logcat, "bootstrap::file_info size: {}", buflen_file_info);
|
||||
|
||||
if (buflen_file_info > sizeof(buf1))
|
||||
throw std::runtime_error("Error: bootstrap::file_info size exceeds buffer size");
|
||||
import_file.read(buf1, buflen_file_info);
|
||||
if (!import_file)
|
||||
throw std::runtime_error("Error reading expected number of bytes");
|
||||
str1.assign(buf1, buflen_file_info);
|
||||
bootstrap::file_info bfi;
|
||||
try {
|
||||
serialization::parse_binary(str1, bfi);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Error in deserialization of bootstrap::file_info: "s + e.what());
|
||||
}
|
||||
log::info(
|
||||
logcat,
|
||||
"bootstrap file v{}.{}",
|
||||
unsigned(bfi.major_version),
|
||||
unsigned(bfi.minor_version));
|
||||
log::info(logcat, "bootstrap magic size: {}", sizeof(file_magic));
|
||||
log::info(logcat, "bootstrap header size: {}", bfi.header_size);
|
||||
|
||||
uint64_t full_header_size = sizeof(file_magic) + bfi.header_size;
|
||||
import_file.seekg(full_header_size);
|
||||
|
||||
return full_header_size;
|
||||
}
|
||||
|
||||
uint64_t BootstrapFile::count_bytes(
|
||||
fs::ifstream& import_file, uint64_t blocks, uint64_t& h, bool& quit) {
|
||||
uint64_t bytes_read = 0;
|
||||
uint32_t chunk_size;
|
||||
char buf1[sizeof(chunk_size)];
|
||||
std::string str1;
|
||||
h = 0;
|
||||
while (1) {
|
||||
import_file.read(buf1, sizeof(chunk_size));
|
||||
if (!import_file) {
|
||||
std::cout << refresh_string;
|
||||
log::debug(logcat, "End of file reached");
|
||||
quit = true;
|
||||
break;
|
||||
}
|
||||
bytes_read += sizeof(chunk_size);
|
||||
str1.assign(buf1, sizeof(chunk_size));
|
||||
try {
|
||||
serialization::parse_binary(str1, chunk_size);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Error in deserialization of chunk_size: "s + e.what());
|
||||
}
|
||||
log::debug(logcat, "chunk_size: {}", chunk_size);
|
||||
|
||||
if (chunk_size > BUFFER_SIZE) {
|
||||
std::cout << refresh_string;
|
||||
log::warning(
|
||||
logcat,
|
||||
"WARNING: chunk_size {} > BUFFER_SIZE {} height: {}, offset {}",
|
||||
chunk_size,
|
||||
BUFFER_SIZE,
|
||||
h - 1,
|
||||
bytes_read);
|
||||
throw std::runtime_error("Aborting: chunk size exceeds buffer size");
|
||||
}
|
||||
if (chunk_size > CHUNK_SIZE_WARNING_THRESHOLD) {
|
||||
std::cout << refresh_string;
|
||||
log::debug(
|
||||
logcat,
|
||||
"NOTE: chunk_size {} > {} height: {}, offset {}",
|
||||
chunk_size,
|
||||
CHUNK_SIZE_WARNING_THRESHOLD,
|
||||
h - 1,
|
||||
bytes_read);
|
||||
} else if (chunk_size <= 0) {
|
||||
std::cout << refresh_string;
|
||||
log::debug(
|
||||
logcat,
|
||||
"ERROR: chunk_size {} <= 0 height: {}, offset {}",
|
||||
chunk_size,
|
||||
h - 1,
|
||||
bytes_read);
|
||||
throw std::runtime_error("Aborting");
|
||||
}
|
||||
// skip to next expected block size value
|
||||
import_file.seekg(chunk_size, std::ios_base::cur);
|
||||
if (!import_file) {
|
||||
std::cout << refresh_string;
|
||||
log::error(
|
||||
logcat,
|
||||
"ERROR: unexpected end of file: bytes read before error: {} of chunk_size {}",
|
||||
import_file.gcount(),
|
||||
chunk_size);
|
||||
throw std::runtime_error("Aborting");
|
||||
}
|
||||
bytes_read += chunk_size;
|
||||
h += NUM_BLOCKS_PER_CHUNK;
|
||||
if (h >= blocks)
|
||||
break;
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
uint64_t BootstrapFile::count_blocks(const fs::path& import_file_path) {
|
||||
std::streampos dummy_pos;
|
||||
uint64_t dummy_height = 0;
|
||||
return count_blocks(import_file_path, dummy_pos, dummy_height);
|
||||
}
|
||||
|
||||
// If seek_height is non-zero on entry, return a stream position <= this height when finished.
|
||||
// And return the actual height corresponding to this position. Allows the caller to locate its
|
||||
// starting position without having to reread the entire file again.
|
||||
uint64_t BootstrapFile::count_blocks(const fs::path& import_file_path, std::streampos &start_pos, uint64_t& seek_height)
|
||||
{
|
||||
if (std::error_code ec; !fs::exists(import_file_path, ec))
|
||||
{
|
||||
log::error(logcat, "bootstrap file not found: {}", import_file_path);
|
||||
throw std::runtime_error("Aborting");
|
||||
}
|
||||
fs::ifstream import_file{import_file_path, std::ios::binary};
|
||||
|
||||
uint64_t start_height = seek_height;
|
||||
uint64_t h = 0;
|
||||
if (import_file.fail())
|
||||
{
|
||||
log::error(logcat, "import_file.open() fail");
|
||||
throw std::runtime_error("Aborting");
|
||||
}
|
||||
|
||||
uint64_t full_header_size; // 4 byte magic + length of header structures
|
||||
full_header_size = seek_to_first_chunk(import_file);
|
||||
|
||||
log::info(logcat, "Scanning blockchain from bootstrap file...");
|
||||
bool quit = false;
|
||||
uint64_t bytes_read = 0, blocks;
|
||||
int progress_interval = 10;
|
||||
|
||||
while (! quit)
|
||||
{
|
||||
if (start_height && h + progress_interval >= start_height - 1)
|
||||
{
|
||||
start_height = 0;
|
||||
start_pos = import_file.tellg();
|
||||
seek_height = h;
|
||||
uint64_t BootstrapFile::count_blocks(
|
||||
const fs::path& import_file_path, std::streampos& start_pos, uint64_t& seek_height) {
|
||||
if (std::error_code ec; !fs::exists(import_file_path, ec)) {
|
||||
log::error(logcat, "bootstrap file not found: {}", import_file_path);
|
||||
throw std::runtime_error("Aborting");
|
||||
}
|
||||
bytes_read += count_bytes(import_file, progress_interval, blocks, quit);
|
||||
h += blocks;
|
||||
std::cout << "\r" << "block height: " << h-1 <<
|
||||
" \r" <<
|
||||
std::flush;
|
||||
fs::ifstream import_file{import_file_path, std::ios::binary};
|
||||
|
||||
// std::cout << refresh_string;
|
||||
log::debug(logcat, "Number bytes scanned: {}", bytes_read);
|
||||
}
|
||||
uint64_t start_height = seek_height;
|
||||
uint64_t h = 0;
|
||||
if (import_file.fail()) {
|
||||
log::error(logcat, "import_file.open() fail");
|
||||
throw std::runtime_error("Aborting");
|
||||
}
|
||||
|
||||
import_file.close();
|
||||
uint64_t full_header_size; // 4 byte magic + length of header structures
|
||||
full_header_size = seek_to_first_chunk(import_file);
|
||||
|
||||
std::cout << "\nDone scanning bootstrap file";
|
||||
std::cout << "\nFull header length: " << full_header_size << " bytes";
|
||||
std::cout << "\nScanned for blocks: " << bytes_read << " bytes";
|
||||
std::cout << "\nTotal: " << full_header_size + bytes_read << " bytes";
|
||||
std::cout << "\nNumber of blocks: " << h;
|
||||
std::cout << std::endl;
|
||||
log::info(logcat, "Scanning blockchain from bootstrap file...");
|
||||
bool quit = false;
|
||||
uint64_t bytes_read = 0, blocks;
|
||||
int progress_interval = 10;
|
||||
|
||||
// NOTE: h is the number of blocks.
|
||||
// Note that a block's stored height is zero-based, but parts of the code use
|
||||
// one-based height.
|
||||
return h;
|
||||
while (!quit) {
|
||||
if (start_height && h + progress_interval >= start_height - 1) {
|
||||
start_height = 0;
|
||||
start_pos = import_file.tellg();
|
||||
seek_height = h;
|
||||
}
|
||||
bytes_read += count_bytes(import_file, progress_interval, blocks, quit);
|
||||
h += blocks;
|
||||
std::cout << "\r"
|
||||
<< "block height: " << h - 1 << " \r" << std::flush;
|
||||
|
||||
// std::cout << refresh_string;
|
||||
log::debug(logcat, "Number bytes scanned: {}", bytes_read);
|
||||
}
|
||||
|
||||
import_file.close();
|
||||
|
||||
std::cout << "\nDone scanning bootstrap file";
|
||||
std::cout << "\nFull header length: " << full_header_size << " bytes";
|
||||
std::cout << "\nScanned for blocks: " << bytes_read << " bytes";
|
||||
std::cout << "\nTotal: " << full_header_size + bytes_read << " bytes";
|
||||
std::cout << "\nNumber of blocks: " << h;
|
||||
std::cout << std::endl;
|
||||
|
||||
// NOTE: h is the number of blocks.
|
||||
// Note that a block's stored height is zero-based, but parts of the code use
|
||||
// one-based height.
|
||||
return h;
|
||||
}
|
||||
|
|
|
@ -28,62 +28,57 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <boost/iostreams/stream_buffer.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <boost/iostreams/copy.hpp>
|
||||
#include <boost/iostreams/device/back_inserter.hpp>
|
||||
#include <boost/iostreams/filtering_streambuf.hpp>
|
||||
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
#include "cryptonote_core/blockchain.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <boost/iostreams/stream_buffer.hpp>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <boost/iostreams/copy.hpp>
|
||||
#include <atomic>
|
||||
|
||||
#include "common/command_line.h"
|
||||
#include "common/fs.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "blockchain_utilities.h"
|
||||
|
||||
#include "common/command_line.h"
|
||||
#include "common/fs.h"
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
#include "cryptonote_core/blockchain.h"
|
||||
#include "version.h"
|
||||
|
||||
using namespace cryptonote;
|
||||
|
||||
class BootstrapFile {
|
||||
public:
|
||||
uint64_t count_bytes(fs::ifstream& import_file, uint64_t blocks, uint64_t& h, bool& quit);
|
||||
uint64_t count_blocks(
|
||||
const fs::path& dir_path, std::streampos& start_pos, uint64_t& seek_height);
|
||||
uint64_t count_blocks(const fs::path& dir_path);
|
||||
uint64_t seek_to_first_chunk(fs::ifstream& import_file);
|
||||
|
||||
class BootstrapFile
|
||||
{
|
||||
public:
|
||||
bool store_blockchain_raw(
|
||||
cryptonote::Blockchain* cs,
|
||||
cryptonote::tx_memory_pool* txp,
|
||||
fs::path& output_file,
|
||||
uint64_t use_block_height = 0);
|
||||
|
||||
uint64_t count_bytes(fs::ifstream& import_file, uint64_t blocks, uint64_t& h, bool& quit);
|
||||
uint64_t count_blocks(const fs::path& dir_path, std::streampos& start_pos, uint64_t& seek_height);
|
||||
uint64_t count_blocks(const fs::path& dir_path);
|
||||
uint64_t seek_to_first_chunk(fs::ifstream& import_file);
|
||||
protected:
|
||||
Blockchain* m_blockchain_storage;
|
||||
|
||||
bool store_blockchain_raw(cryptonote::Blockchain* cs, cryptonote::tx_memory_pool* txp,
|
||||
fs::path& output_file, uint64_t use_block_height=0);
|
||||
tx_memory_pool* m_tx_pool;
|
||||
typedef std::vector<char> buffer_type;
|
||||
std::ofstream* m_raw_data_file;
|
||||
buffer_type m_buffer;
|
||||
boost::iostreams::stream<boost::iostreams::back_insert_device<buffer_type>>* m_output_stream;
|
||||
|
||||
protected:
|
||||
// open export file for write
|
||||
bool open_writer(const fs::path& file_path);
|
||||
bool initialize_file();
|
||||
bool close();
|
||||
void write_block(block& block);
|
||||
void flush_chunk();
|
||||
|
||||
Blockchain* m_blockchain_storage;
|
||||
|
||||
tx_memory_pool* m_tx_pool;
|
||||
typedef std::vector<char> buffer_type;
|
||||
std::ofstream * m_raw_data_file;
|
||||
buffer_type m_buffer;
|
||||
boost::iostreams::stream<boost::iostreams::back_insert_device<buffer_type>>* m_output_stream;
|
||||
|
||||
// open export file for write
|
||||
bool open_writer(const fs::path& file_path);
|
||||
bool initialize_file();
|
||||
bool close();
|
||||
void write_block(block& block);
|
||||
void flush_chunk();
|
||||
|
||||
private:
|
||||
|
||||
uint64_t m_height;
|
||||
uint64_t m_cur_height; // tracks current height during export
|
||||
uint32_t m_max_chunk;
|
||||
private:
|
||||
uint64_t m_height;
|
||||
uint64_t m_cur_height; // tracks current height during export
|
||||
uint32_t m_max_chunk;
|
||||
};
|
||||
|
|
|
@ -31,58 +31,49 @@
|
|||
#include "cryptonote_basic/cryptonote_boost_serialization.h"
|
||||
#include "cryptonote_basic/difficulty.h"
|
||||
|
||||
namespace cryptonote { namespace bootstrap {
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
namespace bootstrap
|
||||
{
|
||||
struct file_info {
|
||||
uint8_t major_version;
|
||||
uint8_t minor_version;
|
||||
uint32_t header_size;
|
||||
|
||||
struct file_info
|
||||
{
|
||||
uint8_t major_version;
|
||||
uint8_t minor_version;
|
||||
uint32_t header_size;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(major_version);
|
||||
FIELD(minor_version);
|
||||
VARINT_FIELD(header_size);
|
||||
END_SERIALIZE()
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct blocks_info
|
||||
{
|
||||
// block heights of file's first and last blocks, zero-based indexes
|
||||
uint64_t block_first;
|
||||
uint64_t block_last;
|
||||
struct blocks_info {
|
||||
// block heights of file's first and last blocks, zero-based indexes
|
||||
uint64_t block_first;
|
||||
uint64_t block_last;
|
||||
|
||||
// file position, for directly reading last block
|
||||
uint64_t block_last_pos;
|
||||
// file position, for directly reading last block
|
||||
uint64_t block_last_pos;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(block_first);
|
||||
VARINT_FIELD(block_last);
|
||||
VARINT_FIELD(block_last_pos);
|
||||
END_SERIALIZE()
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct block_package
|
||||
{
|
||||
cryptonote::block block;
|
||||
std::vector<transaction> txs;
|
||||
size_t block_weight;
|
||||
difficulty_type cumulative_difficulty;
|
||||
uint64_t coins_generated;
|
||||
struct block_package {
|
||||
cryptonote::block block;
|
||||
std::vector<transaction> txs;
|
||||
size_t block_weight;
|
||||
difficulty_type cumulative_difficulty;
|
||||
uint64_t coins_generated;
|
||||
|
||||
BEGIN_SERIALIZE()
|
||||
BEGIN_SERIALIZE()
|
||||
FIELD(block)
|
||||
FIELD(txs)
|
||||
VARINT_FIELD(block_weight)
|
||||
VARINT_FIELD(cumulative_difficulty)
|
||||
VARINT_FIELD(coins_generated)
|
||||
END_SERIALIZE()
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}} // namespace cryptonote::bootstrap
|
||||
|
|
|
@ -2,16 +2,18 @@
|
|||
extern "C" {
|
||||
#include <sodium.h>
|
||||
}
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <oxenc/hex.h>
|
||||
#include <oxenc/base32z.h>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <oxenc/hex.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "common/fs.h"
|
||||
|
||||
std::string_view arg0;
|
||||
|
@ -91,7 +93,8 @@ std::array<unsigned char, crypto_core_ed25519_BYTES> pubkey_from_privkey(ustring
|
|||
return pubkey;
|
||||
}
|
||||
template <size_t N, std::enable_if_t<(N >= 32), int> = 0>
|
||||
std::array<unsigned char, crypto_core_ed25519_BYTES> pubkey_from_privkey(const std::array<unsigned char, N>& privkey) {
|
||||
std::array<unsigned char, crypto_core_ed25519_BYTES> pubkey_from_privkey(
|
||||
const std::array<unsigned char, N>& privkey) {
|
||||
return pubkey_from_privkey(ustring_view{privkey.data(), 32});
|
||||
}
|
||||
|
||||
|
@ -117,7 +120,11 @@ int generate(bool ed25519, std::list<std::string_view> args) {
|
|||
overwrite = true;
|
||||
|
||||
if (!overwrite && fs::exists(fs::u8path(filename)))
|
||||
return error(2, filename + " to generate already exists, pass `--overwrite' if you want to overwrite it");
|
||||
return error(
|
||||
2,
|
||||
filename +
|
||||
" to generate already exists, pass `--overwrite' if you want to overwrite "
|
||||
"it");
|
||||
|
||||
std::array<unsigned char, crypto_sign_PUBLICKEYBYTES> pubkey;
|
||||
std::array<unsigned char, crypto_sign_SECRETKEYBYTES> seckey;
|
||||
|
@ -147,18 +154,20 @@ int generate(bool ed25519, std::list<std::string_view> args) {
|
|||
out.write(reinterpret_cast<const char*>(privkey.data()), privkey.size());
|
||||
|
||||
if (!out.good())
|
||||
return error(2, "Failed to write to output file '" + filename + "': " + std::strerror(errno));
|
||||
return error(
|
||||
2, "Failed to write to output file '" + filename + "': " + std::strerror(errno));
|
||||
|
||||
std::cout << "Generated SN " << (ed25519 ? "Ed25519 secret key" : "legacy private key") << " in " << filename << "\n";
|
||||
std::cout << "Generated SN " << (ed25519 ? "Ed25519 secret key" : "legacy private key")
|
||||
<< " in " << filename << "\n";
|
||||
|
||||
if (ed25519) {
|
||||
std::array<unsigned char, crypto_scalarmult_curve25519_BYTES> x_pubkey;
|
||||
if (0 != crypto_sign_ed25519_pk_to_curve25519(x_pubkey.data(), pubkey.data()))
|
||||
return error(14, "Internal error: unable to convert Ed25519 pubkey to X25519 pubkey");
|
||||
std::cout <<
|
||||
"Public key: " << oxenc::to_hex(pubkey.begin(), pubkey.end()) <<
|
||||
"\nX25519 pubkey: " << oxenc::to_hex(x_pubkey.begin(), x_pubkey.end()) <<
|
||||
"\nLokinet address: " << oxenc::to_base32z(pubkey.begin(), pubkey.end()) << ".snode\n";
|
||||
std::cout << "Public key: " << oxenc::to_hex(pubkey.begin(), pubkey.end())
|
||||
<< "\nX25519 pubkey: " << oxenc::to_hex(x_pubkey.begin(), x_pubkey.end())
|
||||
<< "\nLokinet address: " << oxenc::to_base32z(pubkey.begin(), pubkey.end())
|
||||
<< ".snode\n";
|
||||
} else {
|
||||
std::cout << "Public key: " << oxenc::to_hex(pubkey.begin(), pubkey.end()) << "\n";
|
||||
}
|
||||
|
@ -204,10 +213,15 @@ int show(std::list<std::string_view> args) {
|
|||
ed25519 = true;
|
||||
}
|
||||
if (!legacy && !ed25519)
|
||||
return error(2, "Could not autodetect key type from " + std::to_string(size) + "-byte file; check the file or pass the --ed25519 or --legacy argument");
|
||||
return error(
|
||||
2,
|
||||
"Could not autodetect key type from " + std::to_string(size) +
|
||||
"-byte file; check the file or pass the --ed25519 or --legacy argument");
|
||||
|
||||
if (size < 32)
|
||||
return error(2, "File size (" + std::to_string(size) + " bytes) is too small to be a secret key");
|
||||
return error(
|
||||
2,
|
||||
"File size (" + std::to_string(size) + " bytes) is too small to be a secret key");
|
||||
|
||||
std::array<unsigned char, crypto_core_ed25519_BYTES> pubkey;
|
||||
std::array<unsigned char, crypto_scalarmult_curve25519_BYTES> x_pubkey;
|
||||
|
@ -219,9 +233,10 @@ int show(std::list<std::string_view> args) {
|
|||
if (legacy) {
|
||||
pubkey = pubkey_from_privkey(seckey);
|
||||
|
||||
std::cout << filename.u8string() << " (legacy SN keypair)" << "\n==========" <<
|
||||
"\nPrivate key: " << oxenc::to_hex(seckey.begin(), seckey.begin() + 32) <<
|
||||
"\nPublic key: " << oxenc::to_hex(pubkey.begin(), pubkey.end()) << "\n\n";
|
||||
std::cout << filename.u8string() << " (legacy SN keypair)"
|
||||
<< "\n=========="
|
||||
<< "\nPrivate key: " << oxenc::to_hex(seckey.begin(), seckey.begin() + 32)
|
||||
<< "\nPublic key: " << oxenc::to_hex(pubkey.begin(), pubkey.end()) << "\n\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -233,17 +248,27 @@ int show(std::list<std::string_view> args) {
|
|||
|
||||
ustring_view privkey{privkey_signhash.data(), 32};
|
||||
pubkey = pubkey_from_privkey(privkey);
|
||||
if (size >= 64 && ustring_view{pubkey.data(), pubkey.size()} != ustring_view{seckey.data() + 32, 32})
|
||||
return error(13, "Error: derived pubkey (" + oxenc::to_hex(pubkey.begin(), pubkey.end()) + ")"
|
||||
" != embedded pubkey (" + oxenc::to_hex(seckey.begin() + 32, seckey.end()) + ")");
|
||||
if (size >= 64 &&
|
||||
ustring_view{pubkey.data(), pubkey.size()} != ustring_view{seckey.data() + 32, 32})
|
||||
return error(
|
||||
13,
|
||||
"Error: derived pubkey (" + oxenc::to_hex(pubkey.begin(), pubkey.end()) +
|
||||
")"
|
||||
" != embedded pubkey (" +
|
||||
oxenc::to_hex(seckey.begin() + 32, seckey.end()) + ")");
|
||||
if (0 != crypto_sign_ed25519_pk_to_curve25519(x_pubkey.data(), pubkey.data()))
|
||||
return error(14, "Unable to convert Ed25519 pubkey to X25519 pubkey; is this a really valid secret key?");
|
||||
return error(
|
||||
14,
|
||||
"Unable to convert Ed25519 pubkey to X25519 pubkey; is this a really valid secret "
|
||||
"key?");
|
||||
|
||||
std::cout << filename << " (Ed25519 SN keypair)" << "\n==========" <<
|
||||
"\nSecret key: " << oxenc::to_hex(seckey.begin(), seckey.begin() + 32) <<
|
||||
"\nPublic key: " << oxenc::to_hex(pubkey.begin(), pubkey.end()) <<
|
||||
"\nX25519 pubkey: " << oxenc::to_hex(x_pubkey.begin(), x_pubkey.end()) <<
|
||||
"\nLokinet address: " << oxenc::to_base32z(pubkey.begin(), pubkey.end()) << ".snode\n\n";
|
||||
std::cout << filename << " (Ed25519 SN keypair)"
|
||||
<< "\n=========="
|
||||
<< "\nSecret key: " << oxenc::to_hex(seckey.begin(), seckey.begin() + 32)
|
||||
<< "\nPublic key: " << oxenc::to_hex(pubkey.begin(), pubkey.end())
|
||||
<< "\nX25519 pubkey: " << oxenc::to_hex(x_pubkey.begin(), x_pubkey.end())
|
||||
<< "\nLokinet address: " << oxenc::to_base32z(pubkey.begin(), pubkey.end())
|
||||
<< ".snode\n\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -298,23 +323,33 @@ int restore(bool ed25519, std::list<std::string_view> args) {
|
|||
if (ed25519) {
|
||||
std::array<unsigned char, crypto_scalarmult_curve25519_BYTES> x_pubkey;
|
||||
if (0 != crypto_sign_ed25519_pk_to_curve25519(x_pubkey.data(), pubkey.data()))
|
||||
return error(14, "Unable to convert Ed25519 pubkey to X25519 pubkey; is this a really valid secret key?");
|
||||
std::cout << "X25519 pubkey: " << oxenc::to_hex(x_pubkey.begin(), x_pubkey.end()) <<
|
||||
"\nLokinet address: " << oxenc::to_base32z(pubkey.begin(), pubkey.end()) << ".snode";
|
||||
return error(
|
||||
14,
|
||||
"Unable to convert Ed25519 pubkey to X25519 pubkey; is this a really valid "
|
||||
"secret key?");
|
||||
std::cout << "X25519 pubkey: " << oxenc::to_hex(x_pubkey.begin(), x_pubkey.end())
|
||||
<< "\nLokinet address: " << oxenc::to_base32z(pubkey.begin(), pubkey.end())
|
||||
<< ".snode";
|
||||
}
|
||||
|
||||
if (pubkey_expected) {
|
||||
if (*pubkey_expected != pubkey)
|
||||
return error(2, "Derived pubkey (" + oxenc::to_hex(pubkey.begin(), pubkey.end()) + ") doesn't match "
|
||||
"provided pubkey (" + oxenc::to_hex(pubkey_expected->begin(), pubkey_expected->end()) + ")");
|
||||
return error(
|
||||
2,
|
||||
"Derived pubkey (" + oxenc::to_hex(pubkey.begin(), pubkey.end()) +
|
||||
") doesn't match "
|
||||
"provided pubkey (" +
|
||||
oxenc::to_hex(pubkey_expected->begin(), pubkey_expected->end()) + ")");
|
||||
} else {
|
||||
|
||||
if (ed25519 && filename.size() >= 4 && filename.substr(filename.size() - 4) == "/key") {
|
||||
std::cout << "\n\n\x1b[31;1m"
|
||||
"Warning: You are trying to restore a file named 'key' using the 'restore'\n"
|
||||
"command, which is intended for the key_ed25519 key file; for old service nodes\n"
|
||||
"with both key files you want to use 'restore-legacy' to restore the old\n"
|
||||
"(pre-Loki 8.x) pubkey.\x1b[0m\n";
|
||||
"Warning: You are trying to restore a file named 'key' using the "
|
||||
"'restore'\n"
|
||||
"command, which is intended for the key_ed25519 key file; for old service "
|
||||
"nodes\n"
|
||||
"with both key files you want to use 'restore-legacy' to restore the old\n"
|
||||
"(pre-Loki 8.x) pubkey.\x1b[0m\n";
|
||||
}
|
||||
|
||||
std::cout << "\nIs this correct? Press Enter to continue, Ctrl-C to cancel.\n";
|
||||
|
@ -328,7 +363,11 @@ int restore(bool ed25519, std::list<std::string_view> args) {
|
|||
|
||||
auto filepath = fs::u8path(filename);
|
||||
if (!overwrite && fs::exists(filepath))
|
||||
return error(2, filename + " to generate already exists, pass `--overwrite' if you want to overwrite it");
|
||||
return error(
|
||||
2,
|
||||
filename +
|
||||
" to generate already exists, pass `--overwrite' if you want to overwrite "
|
||||
"it");
|
||||
|
||||
fs::ofstream out{filepath, std::ios::trunc | std::ios::binary};
|
||||
if (!out.good())
|
||||
|
@ -339,13 +378,13 @@ int restore(bool ed25519, std::list<std::string_view> args) {
|
|||
out.write(reinterpret_cast<const char*>(seed.data()), seed.size());
|
||||
|
||||
if (!out.good())
|
||||
return error(2, "Failed to write to output file '" + filename + "': " + std::strerror(errno));
|
||||
return error(
|
||||
2, "Failed to write to output file '" + filename + "': " + std::strerror(errno));
|
||||
|
||||
std::cout << "Saved secret key to " << filename << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
arg0 = argv[0];
|
||||
if (argc < 2)
|
||||
|
|
|
@ -1,22 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "cryptonote_config.h"
|
||||
#include <string_view>
|
||||
|
||||
namespace blocks
|
||||
{
|
||||
#include "cryptonote_config.h"
|
||||
|
||||
template <cryptonote::network_type Network> std::string_view checkpoint_data() { return ""sv; }
|
||||
template<> std::string_view checkpoint_data<cryptonote::network_type::MAINNET>();
|
||||
template<> std::string_view checkpoint_data<cryptonote::network_type::DEVNET>();
|
||||
template<> std::string_view checkpoint_data<cryptonote::network_type::TESTNET>();
|
||||
namespace blocks {
|
||||
|
||||
inline std::string_view GetCheckpointsData(cryptonote::network_type network)
|
||||
{
|
||||
if (network == cryptonote::network_type::MAINNET) return checkpoint_data<cryptonote::network_type::MAINNET>();
|
||||
if (network == cryptonote::network_type::TESTNET) return checkpoint_data<cryptonote::network_type::TESTNET>();
|
||||
if (network == cryptonote::network_type::DEVNET) return checkpoint_data<cryptonote::network_type::DEVNET>();
|
||||
template <cryptonote::network_type Network>
|
||||
std::string_view checkpoint_data() {
|
||||
return ""sv;
|
||||
}
|
||||
template <>
|
||||
std::string_view checkpoint_data<cryptonote::network_type::MAINNET>();
|
||||
template <>
|
||||
std::string_view checkpoint_data<cryptonote::network_type::DEVNET>();
|
||||
template <>
|
||||
std::string_view checkpoint_data<cryptonote::network_type::TESTNET>();
|
||||
|
||||
inline std::string_view GetCheckpointsData(cryptonote::network_type network) {
|
||||
if (network == cryptonote::network_type::MAINNET)
|
||||
return checkpoint_data<cryptonote::network_type::MAINNET>();
|
||||
if (network == cryptonote::network_type::TESTNET)
|
||||
return checkpoint_data<cryptonote::network_type::TESTNET>();
|
||||
if (network == cryptonote::network_type::DEVNET)
|
||||
return checkpoint_data<cryptonote::network_type::DEVNET>();
|
||||
return ""sv;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace blocks
|
||||
|
|
|
@ -31,296 +31,298 @@
|
|||
|
||||
#include "checkpoints.h"
|
||||
|
||||
#include "epee/string_tools.h"
|
||||
#include "epee/storages/portable_storage_template_helper.h" // epee json include
|
||||
#include "epee/serialization/keyvalue_serialization.h"
|
||||
#include "cryptonote_core/service_node_rules.h"
|
||||
#include <vector>
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
|
||||
#include "common/oxen.h"
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "common/file.h"
|
||||
#include "common/fs-format.h"
|
||||
#include "common/hex.h"
|
||||
#include "common/oxen.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "cryptonote_core/service_node_rules.h"
|
||||
#include "epee/serialization/keyvalue_serialization.h"
|
||||
#include "epee/storages/portable_storage_template_helper.h" // epee json include
|
||||
#include "epee/string_tools.h"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
namespace cryptonote {
|
||||
|
||||
static auto logcat = log::Cat("checkpoints");
|
||||
static auto logcat = log::Cat("checkpoints");
|
||||
|
||||
bool checkpoint_t::check(crypto::hash const &hash) const
|
||||
{
|
||||
bool checkpoint_t::check(crypto::hash const& hash) const {
|
||||
bool result = block_hash == hash;
|
||||
if (result)
|
||||
log::info(logcat, "CHECKPOINT PASSED FOR HEIGHT {} {}", height, block_hash);
|
||||
log::info(logcat, "CHECKPOINT PASSED FOR HEIGHT {} {}", height, block_hash);
|
||||
else
|
||||
log::warning(logcat, "CHECKPOINT FAILED FOR HEIGHT {}. EXPECTED HASH {}GIVEN HASH: {}", height, block_hash, hash);
|
||||
log::warning(
|
||||
logcat,
|
||||
"CHECKPOINT FAILED FOR HEIGHT {}. EXPECTED HASH {}GIVEN HASH: {}",
|
||||
height,
|
||||
block_hash,
|
||||
hash);
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
||||
height_to_hash const HARDCODED_MAINNET_CHECKPOINTS[] =
|
||||
{
|
||||
{0, "08ff156d993012b0bdf2816c4bee47c9bbc7930593b70ee02574edddf15ee933"},
|
||||
{1, "647997953a5ea9b5ab329c2291d4cbb08eed587c287e451eeeb2c79bab9b940f"},
|
||||
{10, "4a7cd8b9bff380d48d6f3533a5e0509f8589cc77d18218b3f7218846e77738fc"},
|
||||
{100, "01b8d33a50713ff837f8ad7146021b8e3060e0316b5e4afc407e46cdb50b6760"},
|
||||
{1000, "5e3b0a1f931885bc0ab1d6ecdc625816576feae29e2f9ac94c5ccdbedb1465ac"},
|
||||
{86535, "52b7c5a60b97bf1efbf0d63a0aa1a313e8f0abe4627eb354b0c5a73cb1f4391e"},
|
||||
{97407, "504af73abbaba85a14ddc16634658bf4dcc241dc288b1eaad09e216836b71023"},
|
||||
{98552, "2058d5c675bd91284f4996435593499c9ab84a5a0f569f57a86cde2e815e57da"},
|
||||
{144650, "a1ab207afc790675070ecd7aac874eb0691eb6349ea37c44f8f58697a5d6cbc4"},
|
||||
{266284, "c42801a37a41e3e9f934a266063483646072a94bfc7269ace178e93c91414b1f"},
|
||||
{301187, "e23e4cf3a2fe3e9f0ffced5cc76426e5bdffd3aad822268f4ad63d82cb958559"},
|
||||
};
|
||||
height_to_hash const HARDCODED_MAINNET_CHECKPOINTS[] = {
|
||||
{0, "08ff156d993012b0bdf2816c4bee47c9bbc7930593b70ee02574edddf15ee933"},
|
||||
{1, "647997953a5ea9b5ab329c2291d4cbb08eed587c287e451eeeb2c79bab9b940f"},
|
||||
{10, "4a7cd8b9bff380d48d6f3533a5e0509f8589cc77d18218b3f7218846e77738fc"},
|
||||
{100, "01b8d33a50713ff837f8ad7146021b8e3060e0316b5e4afc407e46cdb50b6760"},
|
||||
{1000, "5e3b0a1f931885bc0ab1d6ecdc625816576feae29e2f9ac94c5ccdbedb1465ac"},
|
||||
{86535, "52b7c5a60b97bf1efbf0d63a0aa1a313e8f0abe4627eb354b0c5a73cb1f4391e"},
|
||||
{97407, "504af73abbaba85a14ddc16634658bf4dcc241dc288b1eaad09e216836b71023"},
|
||||
{98552, "2058d5c675bd91284f4996435593499c9ab84a5a0f569f57a86cde2e815e57da"},
|
||||
{144650, "a1ab207afc790675070ecd7aac874eb0691eb6349ea37c44f8f58697a5d6cbc4"},
|
||||
{266284, "c42801a37a41e3e9f934a266063483646072a94bfc7269ace178e93c91414b1f"},
|
||||
{301187, "e23e4cf3a2fe3e9f0ffced5cc76426e5bdffd3aad822268f4ad63d82cb958559"},
|
||||
};
|
||||
|
||||
crypto::hash get_newest_hardcoded_checkpoint(cryptonote::network_type nettype, uint64_t *height)
|
||||
{
|
||||
crypto::hash get_newest_hardcoded_checkpoint(cryptonote::network_type nettype, uint64_t* height) {
|
||||
crypto::hash result{};
|
||||
*height = 0;
|
||||
if (nettype != network_type::MAINNET && nettype != network_type::TESTNET)
|
||||
return result;
|
||||
return result;
|
||||
|
||||
if (nettype == network_type::MAINNET)
|
||||
{
|
||||
uint64_t last_index = oxen::array_count(HARDCODED_MAINNET_CHECKPOINTS) - 1;
|
||||
height_to_hash const &entry = HARDCODED_MAINNET_CHECKPOINTS[last_index];
|
||||
if (nettype == network_type::MAINNET) {
|
||||
uint64_t last_index = oxen::array_count(HARDCODED_MAINNET_CHECKPOINTS) - 1;
|
||||
height_to_hash const& entry = HARDCODED_MAINNET_CHECKPOINTS[last_index];
|
||||
|
||||
if (tools::hex_to_type(entry.hash, result))
|
||||
*height = entry.height;
|
||||
if (tools::hex_to_type(entry.hash, result))
|
||||
*height = entry.height;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
bool load_checkpoints_from_json(const fs::path& json_hashfile_fullpath, std::vector<height_to_hash>& checkpoint_hashes)
|
||||
{
|
||||
if (std::error_code ec; !fs::exists(json_hashfile_fullpath, ec))
|
||||
{
|
||||
log::debug(logcat, "Blockchain checkpoints file not found");
|
||||
return true;
|
||||
bool load_checkpoints_from_json(
|
||||
const fs::path& json_hashfile_fullpath, std::vector<height_to_hash>& checkpoint_hashes) {
|
||||
if (std::error_code ec; !fs::exists(json_hashfile_fullpath, ec)) {
|
||||
log::debug(logcat, "Blockchain checkpoints file not found");
|
||||
return true;
|
||||
}
|
||||
|
||||
height_to_hash_json hashes;
|
||||
if (std::string contents;
|
||||
!tools::slurp_file(json_hashfile_fullpath, contents) ||
|
||||
!epee::serialization::load_t_from_json(hashes, contents))
|
||||
{
|
||||
log::error(logcat, "Error loading checkpoints from {}", json_hashfile_fullpath);
|
||||
return false;
|
||||
if (std::string contents; !tools::slurp_file(json_hashfile_fullpath, contents) ||
|
||||
!epee::serialization::load_t_from_json(hashes, contents)) {
|
||||
log::error(logcat, "Error loading checkpoints from {}", json_hashfile_fullpath);
|
||||
return false;
|
||||
}
|
||||
|
||||
checkpoint_hashes = std::move(hashes.hashlines);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool checkpoints::get_checkpoint(uint64_t height, checkpoint_t &checkpoint) const
|
||||
{
|
||||
try
|
||||
{
|
||||
auto guard = db_rtxn_guard(m_db);
|
||||
return m_db->get_block_checkpoint(height, checkpoint);
|
||||
bool checkpoints::get_checkpoint(uint64_t height, checkpoint_t& checkpoint) const {
|
||||
try {
|
||||
auto guard = db_rtxn_guard(m_db);
|
||||
return m_db->get_block_checkpoint(height, checkpoint);
|
||||
} catch (const std::exception& e) {
|
||||
log::error(
|
||||
logcat,
|
||||
"Get block checkpoint from DB failed at height: {}, what = {}",
|
||||
height,
|
||||
e.what());
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
log::error(logcat, "Get block checkpoint from DB failed at height: {}, what = {}", height, e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str)
|
||||
{
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str) {
|
||||
crypto::hash h{};
|
||||
bool r = tools::hex_to_type(hash_str, h);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to parse checkpoint hash string into binary representation!");
|
||||
bool r = tools::hex_to_type(hash_str, h);
|
||||
CHECK_AND_ASSERT_MES(
|
||||
r, false, "Failed to parse checkpoint hash string into binary representation!");
|
||||
|
||||
checkpoint_t checkpoint = {};
|
||||
if (get_checkpoint(height, checkpoint))
|
||||
{
|
||||
crypto::hash const &curr_hash = checkpoint.block_hash;
|
||||
CHECK_AND_ASSERT_MES(h == curr_hash, false, "Checkpoint at given height already exists, and hash for new checkpoint was different!");
|
||||
}
|
||||
else
|
||||
{
|
||||
checkpoint.type = checkpoint_type::hardcoded;
|
||||
checkpoint.height = height;
|
||||
checkpoint.block_hash = h;
|
||||
r = update_checkpoint(checkpoint);
|
||||
if (get_checkpoint(height, checkpoint)) {
|
||||
crypto::hash const& curr_hash = checkpoint.block_hash;
|
||||
CHECK_AND_ASSERT_MES(
|
||||
h == curr_hash,
|
||||
false,
|
||||
"Checkpoint at given height already exists, and hash for new checkpoint was "
|
||||
"different!");
|
||||
} else {
|
||||
checkpoint.type = checkpoint_type::hardcoded;
|
||||
checkpoint.height = height;
|
||||
checkpoint.block_hash = h;
|
||||
r = update_checkpoint(checkpoint);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
bool checkpoints::update_checkpoint(checkpoint_t const &checkpoint)
|
||||
{
|
||||
}
|
||||
bool checkpoints::update_checkpoint(checkpoint_t const& checkpoint) {
|
||||
// NOTE(oxen): Assumes checkpoint is valid
|
||||
bool result = true;
|
||||
bool result = true;
|
||||
bool batch_started = false;
|
||||
try
|
||||
{
|
||||
batch_started = m_db->batch_start();
|
||||
m_db->update_block_checkpoint(checkpoint);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
log::error(logcat, "Failed to add checkpoint with hash: {} at height: {}, what = {}", checkpoint.block_hash, checkpoint.height, e.what());
|
||||
result = false;
|
||||
try {
|
||||
batch_started = m_db->batch_start();
|
||||
m_db->update_block_checkpoint(checkpoint);
|
||||
} catch (const std::exception& e) {
|
||||
log::error(
|
||||
logcat,
|
||||
"Failed to add checkpoint with hash: {} at height: {}, what = {}",
|
||||
checkpoint.block_hash,
|
||||
checkpoint.height,
|
||||
e.what());
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (batch_started)
|
||||
m_db->batch_stop();
|
||||
m_db->batch_stop();
|
||||
return result;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void checkpoints::block_add(const block_add_info& info)
|
||||
{
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void checkpoints::block_add(const block_add_info& info) {
|
||||
uint64_t const height = get_block_height(info.block);
|
||||
if (height < service_nodes::CHECKPOINT_STORE_PERSISTENTLY_INTERVAL || info.block.major_version < hf::hf12_checkpointing)
|
||||
return;
|
||||
if (height < service_nodes::CHECKPOINT_STORE_PERSISTENTLY_INTERVAL ||
|
||||
info.block.major_version < hf::hf12_checkpointing)
|
||||
return;
|
||||
|
||||
uint64_t end_cull_height = 0;
|
||||
{
|
||||
checkpoint_t immutable_checkpoint;
|
||||
if (m_db->get_immutable_checkpoint(&immutable_checkpoint, height + 1))
|
||||
end_cull_height = immutable_checkpoint.height;
|
||||
checkpoint_t immutable_checkpoint;
|
||||
if (m_db->get_immutable_checkpoint(&immutable_checkpoint, height + 1))
|
||||
end_cull_height = immutable_checkpoint.height;
|
||||
}
|
||||
uint64_t start_cull_height = (end_cull_height < service_nodes::CHECKPOINT_STORE_PERSISTENTLY_INTERVAL)
|
||||
? 0
|
||||
: end_cull_height - service_nodes::CHECKPOINT_STORE_PERSISTENTLY_INTERVAL;
|
||||
uint64_t start_cull_height =
|
||||
(end_cull_height < service_nodes::CHECKPOINT_STORE_PERSISTENTLY_INTERVAL)
|
||||
? 0
|
||||
: end_cull_height - service_nodes::CHECKPOINT_STORE_PERSISTENTLY_INTERVAL;
|
||||
|
||||
if ((start_cull_height % service_nodes::CHECKPOINT_INTERVAL) > 0)
|
||||
start_cull_height += (service_nodes::CHECKPOINT_INTERVAL - (start_cull_height % service_nodes::CHECKPOINT_INTERVAL));
|
||||
start_cull_height +=
|
||||
(service_nodes::CHECKPOINT_INTERVAL -
|
||||
(start_cull_height % service_nodes::CHECKPOINT_INTERVAL));
|
||||
|
||||
m_last_cull_height = std::max(m_last_cull_height, start_cull_height);
|
||||
auto guard = db_wtxn_guard(m_db);
|
||||
for (; m_last_cull_height < end_cull_height; m_last_cull_height += service_nodes::CHECKPOINT_INTERVAL)
|
||||
{
|
||||
if (m_last_cull_height % service_nodes::CHECKPOINT_STORE_PERSISTENTLY_INTERVAL == 0)
|
||||
continue;
|
||||
auto guard = db_wtxn_guard(m_db);
|
||||
for (; m_last_cull_height < end_cull_height;
|
||||
m_last_cull_height += service_nodes::CHECKPOINT_INTERVAL) {
|
||||
if (m_last_cull_height % service_nodes::CHECKPOINT_STORE_PERSISTENTLY_INTERVAL == 0)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
m_db->remove_block_checkpoint(m_last_cull_height);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
log::error(logcat, "Pruning block checkpoint on block added failed non-trivially at height: {}, what = {}", m_last_cull_height, e.what());
|
||||
}
|
||||
try {
|
||||
m_db->remove_block_checkpoint(m_last_cull_height);
|
||||
} catch (const std::exception& e) {
|
||||
log::error(
|
||||
logcat,
|
||||
"Pruning block checkpoint on block added failed non-trivially at height: {}, "
|
||||
"what = {}",
|
||||
m_last_cull_height,
|
||||
e.what());
|
||||
}
|
||||
}
|
||||
|
||||
if (info.checkpoint)
|
||||
update_checkpoint(*info.checkpoint);
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void checkpoints::blockchain_detached(uint64_t height)
|
||||
{
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
void checkpoints::blockchain_detached(uint64_t height) {
|
||||
m_last_cull_height = std::min(m_last_cull_height, height);
|
||||
|
||||
checkpoint_t top_checkpoint;
|
||||
auto guard = db_wtxn_guard(m_db);
|
||||
if (m_db->get_top_checkpoint(top_checkpoint))
|
||||
{
|
||||
uint64_t start_height = top_checkpoint.height;
|
||||
for (size_t delete_height = start_height;
|
||||
delete_height >= height && delete_height >= service_nodes::CHECKPOINT_INTERVAL;
|
||||
delete_height -= service_nodes::CHECKPOINT_INTERVAL)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_db->remove_block_checkpoint(delete_height);
|
||||
if (m_db->get_top_checkpoint(top_checkpoint)) {
|
||||
uint64_t start_height = top_checkpoint.height;
|
||||
for (size_t delete_height = start_height;
|
||||
delete_height >= height && delete_height >= service_nodes::CHECKPOINT_INTERVAL;
|
||||
delete_height -= service_nodes::CHECKPOINT_INTERVAL) {
|
||||
try {
|
||||
m_db->remove_block_checkpoint(delete_height);
|
||||
} catch (const std::exception& e) {
|
||||
log::error(
|
||||
logcat,
|
||||
"Remove block checkpoint on detach failed non-trivially at height: {}, "
|
||||
"what = {}",
|
||||
delete_height,
|
||||
e.what());
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
log::error(logcat, "Remove block checkpoint on detach failed non-trivially at height: {}, what = {}", delete_height, e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool checkpoints::is_in_checkpoint_zone(uint64_t height) const
|
||||
{
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool checkpoints::is_in_checkpoint_zone(uint64_t height) const {
|
||||
uint64_t top_checkpoint_height = 0;
|
||||
checkpoint_t top_checkpoint;
|
||||
if (m_db->get_top_checkpoint(top_checkpoint))
|
||||
top_checkpoint_height = top_checkpoint.height;
|
||||
top_checkpoint_height = top_checkpoint.height;
|
||||
|
||||
return height <= top_checkpoint_height;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool checkpoints::check_block(uint64_t height, const crypto::hash& h, bool* is_a_checkpoint, bool *service_node_checkpoint) const
|
||||
{
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool checkpoints::check_block(
|
||||
uint64_t height,
|
||||
const crypto::hash& h,
|
||||
bool* is_a_checkpoint,
|
||||
bool* service_node_checkpoint) const {
|
||||
checkpoint_t checkpoint;
|
||||
bool found = get_checkpoint(height, checkpoint);
|
||||
if (is_a_checkpoint) *is_a_checkpoint = found;
|
||||
if (service_node_checkpoint) *service_node_checkpoint = false;
|
||||
if (is_a_checkpoint)
|
||||
*is_a_checkpoint = found;
|
||||
if (service_node_checkpoint)
|
||||
*service_node_checkpoint = false;
|
||||
|
||||
if(!found)
|
||||
return true;
|
||||
if (!found)
|
||||
return true;
|
||||
|
||||
bool result = checkpoint.check(h);
|
||||
if (service_node_checkpoint)
|
||||
*service_node_checkpoint = (checkpoint.type == checkpoint_type::service_node);
|
||||
*service_node_checkpoint = (checkpoint.type == checkpoint_type::service_node);
|
||||
|
||||
return result;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool checkpoints::is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height, bool *service_node_checkpoint)
|
||||
{
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool checkpoints::is_alternative_block_allowed(
|
||||
uint64_t blockchain_height, uint64_t block_height, bool* service_node_checkpoint) {
|
||||
if (service_node_checkpoint)
|
||||
*service_node_checkpoint = false;
|
||||
*service_node_checkpoint = false;
|
||||
|
||||
if (0 == block_height)
|
||||
return false;
|
||||
return false;
|
||||
|
||||
{
|
||||
std::vector<checkpoint_t> const first_checkpoint = m_db->get_checkpoints_range(0, blockchain_height, 1);
|
||||
if (first_checkpoint.empty() || blockchain_height < first_checkpoint[0].height)
|
||||
return true;
|
||||
std::vector<checkpoint_t> const first_checkpoint =
|
||||
m_db->get_checkpoints_range(0, blockchain_height, 1);
|
||||
if (first_checkpoint.empty() || blockchain_height < first_checkpoint[0].height)
|
||||
return true;
|
||||
}
|
||||
|
||||
checkpoint_t immutable_checkpoint;
|
||||
uint64_t immutable_height = 0;
|
||||
if (m_db->get_immutable_checkpoint(&immutable_checkpoint, blockchain_height))
|
||||
{
|
||||
immutable_height = immutable_checkpoint.height;
|
||||
if (service_node_checkpoint)
|
||||
*service_node_checkpoint = (immutable_checkpoint.type == checkpoint_type::service_node);
|
||||
if (m_db->get_immutable_checkpoint(&immutable_checkpoint, blockchain_height)) {
|
||||
immutable_height = immutable_checkpoint.height;
|
||||
if (service_node_checkpoint)
|
||||
*service_node_checkpoint = (immutable_checkpoint.type == checkpoint_type::service_node);
|
||||
}
|
||||
|
||||
m_immutable_height = std::max(immutable_height, m_immutable_height);
|
||||
bool result = block_height > m_immutable_height;
|
||||
bool result = block_height > m_immutable_height;
|
||||
return result;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
uint64_t checkpoints::get_max_height() const
|
||||
{
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
uint64_t checkpoints::get_max_height() const {
|
||||
uint64_t result = 0;
|
||||
checkpoint_t top_checkpoint;
|
||||
if (m_db->get_top_checkpoint(top_checkpoint))
|
||||
result = top_checkpoint.height;
|
||||
result = top_checkpoint.height;
|
||||
|
||||
return result;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool checkpoints::init(network_type nettype, BlockchainDB *db)
|
||||
{
|
||||
*this = {};
|
||||
m_db = db;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool checkpoints::init(network_type nettype, BlockchainDB* db) {
|
||||
*this = {};
|
||||
m_db = db;
|
||||
m_nettype = nettype;
|
||||
|
||||
if (db->is_read_only())
|
||||
return true;
|
||||
return true;
|
||||
|
||||
if (nettype == network_type::MAINNET)
|
||||
{
|
||||
for (size_t i = 0; i < oxen::array_count(HARDCODED_MAINNET_CHECKPOINTS); ++i)
|
||||
{
|
||||
height_to_hash const &checkpoint = HARDCODED_MAINNET_CHECKPOINTS[i];
|
||||
bool added = add_checkpoint(checkpoint.height, checkpoint.hash);
|
||||
CHECK_AND_ASSERT(added, false);
|
||||
}
|
||||
if (nettype == network_type::MAINNET) {
|
||||
for (size_t i = 0; i < oxen::array_count(HARDCODED_MAINNET_CHECKPOINTS); ++i) {
|
||||
height_to_hash const& checkpoint = HARDCODED_MAINNET_CHECKPOINTS[i];
|
||||
bool added = add_checkpoint(checkpoint.height, checkpoint.hash);
|
||||
CHECK_AND_ASSERT(added, false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace cryptonote
|
||||
|
|
|
@ -31,91 +31,85 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
#include "common/fs.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
#include "cryptonote_config.h"
|
||||
#include "cryptonote_core/service_node_voting.h"
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
#include "common/fs.h"
|
||||
|
||||
namespace cryptonote {
|
||||
constexpr std::string_view JSON_HASH_FILE_NAME = "checkpoints.json"sv;
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
constexpr std::string_view JSON_HASH_FILE_NAME = "checkpoints.json"sv;
|
||||
|
||||
enum struct checkpoint_type
|
||||
{
|
||||
enum struct checkpoint_type {
|
||||
hardcoded,
|
||||
service_node,
|
||||
count,
|
||||
};
|
||||
};
|
||||
|
||||
struct checkpoint_t
|
||||
{
|
||||
uint8_t version = 0;
|
||||
checkpoint_type type;
|
||||
uint64_t height;
|
||||
crypto::hash block_hash;
|
||||
std::vector<service_nodes::quorum_signature> signatures; // Only service node checkpoints use signatures
|
||||
uint64_t prev_height; // TODO(doyle): Unused
|
||||
struct checkpoint_t {
|
||||
uint8_t version = 0;
|
||||
checkpoint_type type;
|
||||
uint64_t height;
|
||||
crypto::hash block_hash;
|
||||
std::vector<service_nodes::quorum_signature>
|
||||
signatures; // Only service node checkpoints use signatures
|
||||
uint64_t prev_height; // TODO(doyle): Unused
|
||||
|
||||
bool check (crypto::hash const &block_hash) const;
|
||||
static char const *type_to_string(checkpoint_type type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case checkpoint_type::hardcoded: return "Hardcoded";
|
||||
case checkpoint_type::service_node: return "ServiceNode";
|
||||
default: assert(false); return "XXUnhandledVersion";
|
||||
}
|
||||
bool check(crypto::hash const& block_hash) const;
|
||||
static char const* type_to_string(checkpoint_type type) {
|
||||
switch (type) {
|
||||
case checkpoint_type::hardcoded: return "Hardcoded";
|
||||
case checkpoint_type::service_node: return "ServiceNode";
|
||||
default: assert(false); return "XXUnhandledVersion";
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_SERIALIZE()
|
||||
FIELD(version)
|
||||
ENUM_FIELD(type, type < checkpoint_type::count);
|
||||
FIELD(height)
|
||||
FIELD(block_hash)
|
||||
FIELD(signatures)
|
||||
FIELD(prev_height)
|
||||
FIELD(version)
|
||||
ENUM_FIELD(type, type < checkpoint_type::count);
|
||||
FIELD(height)
|
||||
FIELD(block_hash)
|
||||
FIELD(signatures)
|
||||
FIELD(prev_height)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
};
|
||||
|
||||
struct height_to_hash
|
||||
{
|
||||
uint64_t height; //!< the height of the checkpoint
|
||||
std::string hash; //!< the hash for the checkpoint
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(height)
|
||||
KV_SERIALIZE(hash)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
struct height_to_hash {
|
||||
uint64_t height; //!< the height of the checkpoint
|
||||
std::string hash; //!< the hash for the checkpoint
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(height)
|
||||
KV_SERIALIZE(hash)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief struct for loading many checkpoints from json
|
||||
*/
|
||||
struct height_to_hash_json {
|
||||
std::vector<height_to_hash> hashlines; //!< the checkpoint lines from the file
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(hashlines)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
/**
|
||||
* @brief struct for loading many checkpoints from json
|
||||
*/
|
||||
struct height_to_hash_json {
|
||||
std::vector<height_to_hash> hashlines; //!< the checkpoint lines from the file
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(hashlines)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
crypto::hash get_newest_hardcoded_checkpoint(cryptonote::network_type nettype, uint64_t *height);
|
||||
bool load_checkpoints_from_json (const fs::path& json_hashfile_fullpath, std::vector<height_to_hash>& checkpoint_hashes);
|
||||
crypto::hash get_newest_hardcoded_checkpoint(cryptonote::network_type nettype, uint64_t* height);
|
||||
bool load_checkpoints_from_json(
|
||||
const fs::path& json_hashfile_fullpath, std::vector<height_to_hash>& checkpoint_hashes);
|
||||
|
||||
/**
|
||||
* @brief A container for blockchain checkpoints
|
||||
*
|
||||
* A checkpoint is a pre-defined hash for the block at a given height.
|
||||
* Some of these are compiled-in, while others can be loaded at runtime
|
||||
* either from a json file or via DNS from a checkpoint-hosting server.
|
||||
*/
|
||||
class checkpoints
|
||||
{
|
||||
/**
|
||||
* @brief A container for blockchain checkpoints
|
||||
*
|
||||
* A checkpoint is a pre-defined hash for the block at a given height.
|
||||
* Some of these are compiled-in, while others can be loaded at runtime
|
||||
* either from a json file or via DNS from a checkpoint-hosting server.
|
||||
*/
|
||||
class checkpoints {
|
||||
public:
|
||||
void block_add(const block_add_info& info);
|
||||
void blockchain_detached(uint64_t height);
|
||||
|
||||
bool get_checkpoint(uint64_t height, checkpoint_t &checkpoint) const;
|
||||
bool get_checkpoint(uint64_t height, checkpoint_t& checkpoint) const;
|
||||
/**
|
||||
* @brief adds a checkpoint to the container
|
||||
*
|
||||
|
@ -128,7 +122,7 @@ namespace cryptonote
|
|||
*/
|
||||
bool add_checkpoint(uint64_t height, const std::string& hash_str);
|
||||
|
||||
bool update_checkpoint(checkpoint_t const &checkpoint);
|
||||
bool update_checkpoint(checkpoint_t const& checkpoint);
|
||||
|
||||
/**
|
||||
* @brief checks if there is a checkpoint in the future
|
||||
|
@ -153,13 +147,18 @@ namespace cryptonote
|
|||
* @param height the height to be checked
|
||||
* @param h the hash to be checked
|
||||
* @param blockchain the blockchain to query ancestor blocks from the current height
|
||||
* @param is_a_checkpoint optional return-by-pointer if there is a checkpoint at the given height
|
||||
* @param is_a_checkpoint optional return-by-pointer if there is a checkpoint at the given
|
||||
* height
|
||||
*
|
||||
* @return true if there is no checkpoint at the given height,
|
||||
* true if the passed parameters match the stored checkpoint,
|
||||
* false otherwise
|
||||
*/
|
||||
bool check_block(uint64_t height, const crypto::hash& h, bool *is_a_checkpoint = nullptr, bool *service_node_checkpoint = nullptr) const;
|
||||
bool check_block(
|
||||
uint64_t height,
|
||||
const crypto::hash& h,
|
||||
bool* is_a_checkpoint = nullptr,
|
||||
bool* service_node_checkpoint = nullptr) const;
|
||||
|
||||
/**
|
||||
* @brief checks if alternate chain blocks should be kept for a given height and updates
|
||||
|
@ -176,7 +175,10 @@ namespace cryptonote
|
|||
* @return true if alternate blocks are allowed given the parameters,
|
||||
* otherwise false
|
||||
*/
|
||||
bool is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height, bool *service_node_checkpoint = nullptr);
|
||||
bool is_alternative_block_allowed(
|
||||
uint64_t blockchain_height,
|
||||
uint64_t block_height,
|
||||
bool* service_node_checkpoint = nullptr);
|
||||
|
||||
/**
|
||||
* @brief gets the highest checkpoint height
|
||||
|
@ -191,13 +193,13 @@ namespace cryptonote
|
|||
*
|
||||
* @return true unless adding a checkpoint fails
|
||||
*/
|
||||
bool init(network_type nettype, class BlockchainDB *db);
|
||||
bool init(network_type nettype, class BlockchainDB* db);
|
||||
|
||||
private:
|
||||
network_type m_nettype = network_type::UNDEFINED;
|
||||
uint64_t m_last_cull_height = 0;
|
||||
uint64_t m_immutable_height = 0;
|
||||
BlockchainDB *m_db;
|
||||
};
|
||||
BlockchainDB* m_db;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace cryptonote
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2017-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -26,114 +26,111 @@
|
|||
// 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.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "aligned.h"
|
||||
|
||||
static inline int is_power_of_2(size_t n) { return n && (n & (n-1)) == 0; }
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static inline int is_power_of_2(size_t n) {
|
||||
return n && (n & (n - 1)) == 0;
|
||||
}
|
||||
|
||||
#define MAGIC 0xaa0817161500ff81
|
||||
#define MAGIC_FREED 0xaa0817161500ff82
|
||||
|
||||
static void local_abort(const char *msg)
|
||||
{
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
static void local_abort(const char* msg) {
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
#ifdef NDEBUG
|
||||
_exit(1);
|
||||
_exit(1);
|
||||
#else
|
||||
abort();
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint64_t magic;
|
||||
void *raw;
|
||||
size_t bytes;
|
||||
size_t align;
|
||||
typedef struct {
|
||||
uint64_t magic;
|
||||
void* raw;
|
||||
size_t bytes;
|
||||
size_t align;
|
||||
} control;
|
||||
|
||||
void *aligned_malloc(size_t bytes, size_t align)
|
||||
{
|
||||
void *raw, *ptr;
|
||||
control *ctrl;
|
||||
void* aligned_malloc(size_t bytes, size_t align) {
|
||||
void *raw, *ptr;
|
||||
control* ctrl;
|
||||
|
||||
if (!is_power_of_2(align))
|
||||
return NULL;
|
||||
if (bytes > (size_t)-1 - align)
|
||||
return NULL;
|
||||
if (bytes + align > (size_t)-1 - sizeof(control))
|
||||
return NULL;
|
||||
if (!is_power_of_2(align))
|
||||
return NULL;
|
||||
if (bytes > (size_t)-1 - align)
|
||||
return NULL;
|
||||
if (bytes + align > (size_t)-1 - sizeof(control))
|
||||
return NULL;
|
||||
|
||||
raw = malloc(bytes + sizeof(control) + align);
|
||||
if (!raw)
|
||||
return NULL;
|
||||
ptr = (void*)(((uintptr_t)raw + align + sizeof(control) - 1) & ~(align-1));
|
||||
ctrl = ((control*)ptr) - 1;
|
||||
ctrl->magic = MAGIC;
|
||||
ctrl->raw = raw;
|
||||
ctrl->bytes = bytes;
|
||||
ctrl->align = align;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *aligned_realloc(void *ptr, size_t bytes, size_t align)
|
||||
{
|
||||
void *raw, *ptr2;
|
||||
control *ctrl, *ctrl2;
|
||||
|
||||
if (!ptr)
|
||||
return aligned_malloc(bytes, align);
|
||||
if (!bytes)
|
||||
{
|
||||
aligned_free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
if (!is_power_of_2(align))
|
||||
return NULL;
|
||||
|
||||
ctrl = ((control*)ptr) - 1;
|
||||
if (ctrl->magic == MAGIC_FREED)
|
||||
local_abort("Double free detected");
|
||||
if (ctrl->magic != MAGIC)
|
||||
local_abort("Freeing unallocated memory");
|
||||
if (ctrl->align != align)
|
||||
return NULL;
|
||||
if (ctrl->bytes >= bytes)
|
||||
raw = malloc(bytes + sizeof(control) + align);
|
||||
if (!raw)
|
||||
return NULL;
|
||||
ptr = (void*)(((uintptr_t)raw + align + sizeof(control) - 1) & ~(align - 1));
|
||||
ctrl = ((control*)ptr) - 1;
|
||||
ctrl->magic = MAGIC;
|
||||
ctrl->raw = raw;
|
||||
ctrl->bytes = bytes;
|
||||
ctrl->align = align;
|
||||
return ptr;
|
||||
|
||||
if (ctrl->bytes > (size_t)-1 - ctrl->align)
|
||||
return NULL;
|
||||
if (ctrl->bytes + ctrl->align > (size_t)-1 - sizeof(control))
|
||||
return NULL;
|
||||
|
||||
raw = malloc(bytes + sizeof(control) + ctrl->align);
|
||||
if (!raw)
|
||||
return NULL;
|
||||
ptr2 = (void*)(((uintptr_t)raw + ctrl->align + sizeof(control) - 1) & ~(ctrl->align-1));
|
||||
memcpy(ptr2, ptr, ctrl->bytes);
|
||||
ctrl2 = ((control*)ptr2) - 1;
|
||||
ctrl2->magic = MAGIC;
|
||||
ctrl2->raw = raw;
|
||||
ctrl2->bytes = bytes;
|
||||
ctrl2->align = ctrl->align;
|
||||
ctrl->magic = MAGIC_FREED;
|
||||
free(ctrl->raw);
|
||||
return ptr2;
|
||||
}
|
||||
|
||||
void aligned_free(void *ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
control *ctrl = ((control*)ptr) - 1;
|
||||
if (ctrl->magic == MAGIC_FREED)
|
||||
local_abort("Double free detected");
|
||||
if (ctrl->magic != MAGIC)
|
||||
local_abort("Freeing unallocated memory");
|
||||
ctrl->magic = MAGIC_FREED;
|
||||
free(ctrl->raw);
|
||||
void* aligned_realloc(void* ptr, size_t bytes, size_t align) {
|
||||
void *raw, *ptr2;
|
||||
control *ctrl, *ctrl2;
|
||||
|
||||
if (!ptr)
|
||||
return aligned_malloc(bytes, align);
|
||||
if (!bytes) {
|
||||
aligned_free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
if (!is_power_of_2(align))
|
||||
return NULL;
|
||||
|
||||
ctrl = ((control*)ptr) - 1;
|
||||
if (ctrl->magic == MAGIC_FREED)
|
||||
local_abort("Double free detected");
|
||||
if (ctrl->magic != MAGIC)
|
||||
local_abort("Freeing unallocated memory");
|
||||
if (ctrl->align != align)
|
||||
return NULL;
|
||||
if (ctrl->bytes >= bytes)
|
||||
return ptr;
|
||||
|
||||
if (ctrl->bytes > (size_t)-1 - ctrl->align)
|
||||
return NULL;
|
||||
if (ctrl->bytes + ctrl->align > (size_t)-1 - sizeof(control))
|
||||
return NULL;
|
||||
|
||||
raw = malloc(bytes + sizeof(control) + ctrl->align);
|
||||
if (!raw)
|
||||
return NULL;
|
||||
ptr2 = (void*)(((uintptr_t)raw + ctrl->align + sizeof(control) - 1) & ~(ctrl->align - 1));
|
||||
memcpy(ptr2, ptr, ctrl->bytes);
|
||||
ctrl2 = ((control*)ptr2) - 1;
|
||||
ctrl2->magic = MAGIC;
|
||||
ctrl2->raw = raw;
|
||||
ctrl2->bytes = bytes;
|
||||
ctrl2->align = ctrl->align;
|
||||
ctrl->magic = MAGIC_FREED;
|
||||
free(ctrl->raw);
|
||||
return ptr2;
|
||||
}
|
||||
|
||||
void aligned_free(void* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
control* ctrl = ((control*)ptr) - 1;
|
||||
if (ctrl->magic == MAGIC_FREED)
|
||||
local_abort("Double free detected");
|
||||
if (ctrl->magic != MAGIC)
|
||||
local_abort("Freeing unallocated memory");
|
||||
ctrl->magic = MAGIC_FREED;
|
||||
free(ctrl->raw);
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -26,15 +26,17 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void *aligned_malloc(size_t bytes, size_t align);
|
||||
void *aligned_realloc(void *ptr, size_t bytes, size_t align);
|
||||
void aligned_free(void *ptr);
|
||||
#include <stddef.h>
|
||||
|
||||
void* aligned_malloc(size_t bytes, size_t align);
|
||||
void* aligned_realloc(void* ptr, size_t bytes, size_t align);
|
||||
void aligned_free(void* ptr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2017-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -25,56 +25,50 @@
|
|||
// 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.
|
||||
//
|
||||
//
|
||||
// Most of this file is originally copyright (c) 2017 Raymond Chen, Microsoft
|
||||
// This algorithm is adapted from Raymond Chen's code:
|
||||
// https://blogs.msdn.microsoft.com/oldnewthing/20170109-00/?p=95145
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "logging/oxen_logger.h"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
namespace tools {
|
||||
|
||||
template<typename F>
|
||||
void apply_permutation(std::vector<size_t> permutation, const F &swap)
|
||||
{
|
||||
//sanity check
|
||||
for (size_t n = 0; n < permutation.size(); ++n)
|
||||
if (std::find(permutation.begin(), permutation.end(), n) == permutation.end())
|
||||
{
|
||||
log::error(globallogcat, "Bad permutation");
|
||||
throw std::runtime_error("Bad permutation");
|
||||
return;
|
||||
template <typename F>
|
||||
void apply_permutation(std::vector<size_t> permutation, const F& swap) {
|
||||
// sanity check
|
||||
for (size_t n = 0; n < permutation.size(); ++n)
|
||||
if (std::find(permutation.begin(), permutation.end(), n) == permutation.end()) {
|
||||
log::error(globallogcat, "Bad permutation");
|
||||
throw std::runtime_error("Bad permutation");
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < permutation.size(); ++i) {
|
||||
size_t current = i;
|
||||
while (i != permutation[current]) {
|
||||
size_t next = permutation[current];
|
||||
swap(current, next);
|
||||
permutation[current] = current;
|
||||
current = next;
|
||||
}
|
||||
permutation[current] = current;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < permutation.size(); ++i)
|
||||
{
|
||||
size_t current = i;
|
||||
while (i != permutation[current])
|
||||
{
|
||||
size_t next = permutation[current];
|
||||
swap(current, next);
|
||||
permutation[current] = current;
|
||||
current = next;
|
||||
template <typename T>
|
||||
void apply_permutation(const std::vector<size_t>& permutation, std::vector<T>& v) {
|
||||
if (permutation.size() != v.size()) {
|
||||
log::error(globallogcat, "Mismatched vector sizes");
|
||||
throw std::runtime_error("Mismatched vector sizes");
|
||||
return;
|
||||
}
|
||||
permutation[current] = current;
|
||||
}
|
||||
apply_permutation(permutation, [&v](size_t i0, size_t i1) { std::swap(v[i0], v[i1]); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void apply_permutation(const std::vector<size_t> &permutation, std::vector<T> &v)
|
||||
{
|
||||
if (permutation.size() != v.size())
|
||||
{
|
||||
log::error(globallogcat, "Mismatched vector sizes");
|
||||
throw std::runtime_error("Mismatched vector sizes");
|
||||
return;
|
||||
}
|
||||
apply_permutation(permutation, [&v](size_t i0, size_t i1){ std::swap(v[i0], v[i1]); });
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -25,202 +25,201 @@
|
|||
// 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.
|
||||
//
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#include "base58.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "crypto/hash.h"
|
||||
#include "epee/int-util.h"
|
||||
#include "varint.h"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
using namespace std::literals;
|
||||
namespace base58
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr std::string_view alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"sv;
|
||||
constexpr size_t full_block_size = 8;
|
||||
constexpr std::array<uint8_t, full_block_size + 1> encoded_block_sizes = {0, 2, 3, 5, 6, 7, 9, 10, 11};
|
||||
constexpr size_t full_encoded_block_size = encoded_block_sizes.back();
|
||||
constexpr std::array<int8_t, full_encoded_block_size + 1> decoded_block_sizes = {0, -1, 1, 2, -1, 3, 4, 5, -1, 6, 7, 8};
|
||||
constexpr size_t addr_checksum_size = 4;
|
||||
namespace tools {
|
||||
using namespace std::literals;
|
||||
namespace base58 {
|
||||
namespace {
|
||||
constexpr std::string_view alphabet =
|
||||
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"sv;
|
||||
constexpr size_t full_block_size = 8;
|
||||
constexpr std::array<uint8_t, full_block_size + 1> encoded_block_sizes = {
|
||||
0, 2, 3, 5, 6, 7, 9, 10, 11};
|
||||
constexpr size_t full_encoded_block_size = encoded_block_sizes.back();
|
||||
constexpr std::array<int8_t, full_encoded_block_size + 1> decoded_block_sizes = {
|
||||
0, -1, 1, 2, -1, 3, 4, 5, -1, 6, 7, 8};
|
||||
constexpr size_t addr_checksum_size = 4;
|
||||
|
||||
struct reverse_alphabet_table
|
||||
{
|
||||
std::array<int8_t, 256> from_b58_lut;
|
||||
constexpr reverse_alphabet_table() noexcept : from_b58_lut{}
|
||||
{
|
||||
for (size_t i = 0; i < from_b58_lut.size(); ++i)
|
||||
from_b58_lut[i] = -1;
|
||||
for (size_t i = 0; i < alphabet.size(); i++)
|
||||
from_b58_lut[alphabet[i]] = i;
|
||||
struct reverse_alphabet_table {
|
||||
std::array<int8_t, 256> from_b58_lut;
|
||||
constexpr reverse_alphabet_table() noexcept : from_b58_lut{} {
|
||||
for (size_t i = 0; i < from_b58_lut.size(); ++i)
|
||||
from_b58_lut[i] = -1;
|
||||
for (size_t i = 0; i < alphabet.size(); i++)
|
||||
from_b58_lut[alphabet[i]] = i;
|
||||
}
|
||||
|
||||
constexpr int8_t operator[](char letter) const {
|
||||
return from_b58_lut[static_cast<unsigned char>(letter)];
|
||||
}
|
||||
} constexpr reverse_alphabet;
|
||||
|
||||
uint64_t uint_8be_to_64(const uint8_t* data, size_t size) {
|
||||
assert(1 <= size && size <= sizeof(uint64_t));
|
||||
|
||||
uint64_t res = 0;
|
||||
memcpy(reinterpret_cast<uint8_t*>(&res) + sizeof(uint64_t) - size, data, size);
|
||||
return SWAP64BE(res);
|
||||
}
|
||||
|
||||
constexpr int8_t operator[](char letter) const
|
||||
{
|
||||
return from_b58_lut[static_cast<unsigned char>(letter)];
|
||||
}
|
||||
} constexpr reverse_alphabet;
|
||||
void uint_64_to_8be(uint64_t num, size_t size, uint8_t* data) {
|
||||
assert(1 <= size && size <= sizeof(uint64_t));
|
||||
|
||||
uint64_t uint_8be_to_64(const uint8_t* data, size_t size)
|
||||
{
|
||||
assert(1 <= size && size <= sizeof(uint64_t));
|
||||
|
||||
uint64_t res = 0;
|
||||
memcpy(reinterpret_cast<uint8_t*>(&res) + sizeof(uint64_t) - size, data, size);
|
||||
return SWAP64BE(res);
|
||||
}
|
||||
|
||||
void uint_64_to_8be(uint64_t num, size_t size, uint8_t* data)
|
||||
{
|
||||
assert(1 <= size && size <= sizeof(uint64_t));
|
||||
|
||||
uint64_t num_be = SWAP64BE(num);
|
||||
memcpy(data, reinterpret_cast<uint8_t*>(&num_be) + sizeof(uint64_t) - size, size);
|
||||
}
|
||||
|
||||
void encode_block(const char* block, size_t size, char* res)
|
||||
{
|
||||
assert(1 <= size && size <= full_block_size);
|
||||
|
||||
uint64_t num = uint_8be_to_64(reinterpret_cast<const uint8_t*>(block), size);
|
||||
int i = static_cast<int>(encoded_block_sizes[size]) - 1;
|
||||
while (0 < num)
|
||||
{
|
||||
uint64_t remainder = num % alphabet.size();
|
||||
num /= alphabet.size();
|
||||
res[i] = alphabet[remainder];
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
bool decode_block(const char* block, size_t size, char* res)
|
||||
{
|
||||
assert(1 <= size && size <= full_encoded_block_size);
|
||||
|
||||
int res_size = decoded_block_sizes[size];
|
||||
if (res_size <= 0)
|
||||
return false; // Invalid block size
|
||||
|
||||
uint64_t res_num = 0;
|
||||
uint64_t order = 1;
|
||||
for (size_t i = size - 1; i < size; --i)
|
||||
{
|
||||
auto digit = reverse_alphabet[block[i]];
|
||||
if (digit < 0)
|
||||
return false; // Invalid symbol
|
||||
|
||||
uint64_t product_hi;
|
||||
uint64_t tmp = res_num + mul128(order, digit, &product_hi);
|
||||
if (tmp < res_num || 0 != product_hi)
|
||||
return false; // Overflow
|
||||
|
||||
res_num = tmp;
|
||||
order *= alphabet.size(); // Never overflows, 58^10 < 2^64
|
||||
uint64_t num_be = SWAP64BE(num);
|
||||
memcpy(data, reinterpret_cast<uint8_t*>(&num_be) + sizeof(uint64_t) - size, size);
|
||||
}
|
||||
|
||||
if (static_cast<size_t>(res_size) < full_block_size && (UINT64_C(1) << (8 * res_size)) <= res_num)
|
||||
return false; // Overflow
|
||||
void encode_block(const char* block, size_t size, char* res) {
|
||||
assert(1 <= size && size <= full_block_size);
|
||||
|
||||
uint_64_to_8be(res_num, res_size, reinterpret_cast<uint8_t*>(res));
|
||||
uint64_t num = uint_8be_to_64(reinterpret_cast<const uint8_t*>(block), size);
|
||||
int i = static_cast<int>(encoded_block_sizes[size]) - 1;
|
||||
while (0 < num) {
|
||||
uint64_t remainder = num % alphabet.size();
|
||||
num /= alphabet.size();
|
||||
res[i] = alphabet[remainder];
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
bool decode_block(const char* block, size_t size, char* res) {
|
||||
assert(1 <= size && size <= full_encoded_block_size);
|
||||
|
||||
int res_size = decoded_block_sizes[size];
|
||||
if (res_size <= 0)
|
||||
return false; // Invalid block size
|
||||
|
||||
uint64_t res_num = 0;
|
||||
uint64_t order = 1;
|
||||
for (size_t i = size - 1; i < size; --i) {
|
||||
auto digit = reverse_alphabet[block[i]];
|
||||
if (digit < 0)
|
||||
return false; // Invalid symbol
|
||||
|
||||
uint64_t product_hi;
|
||||
uint64_t tmp = res_num + mul128(order, digit, &product_hi);
|
||||
if (tmp < res_num || 0 != product_hi)
|
||||
return false; // Overflow
|
||||
|
||||
res_num = tmp;
|
||||
order *= alphabet.size(); // Never overflows, 58^10 < 2^64
|
||||
}
|
||||
|
||||
if (static_cast<size_t>(res_size) < full_block_size &&
|
||||
(UINT64_C(1) << (8 * res_size)) <= res_num)
|
||||
return false; // Overflow
|
||||
|
||||
uint_64_to_8be(res_num, res_size, reinterpret_cast<uint8_t*>(res));
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string encode(std::string_view data) {
|
||||
if (data.empty())
|
||||
return std::string();
|
||||
|
||||
size_t full_block_count = data.size() / full_block_size;
|
||||
size_t last_block_size = data.size() % full_block_size;
|
||||
size_t res_size =
|
||||
full_block_count * full_encoded_block_size + encoded_block_sizes[last_block_size];
|
||||
|
||||
std::string res(res_size, alphabet[0]);
|
||||
for (size_t i = 0; i < full_block_count; ++i) {
|
||||
encode_block(
|
||||
data.data() + i * full_block_size,
|
||||
full_block_size,
|
||||
&res[i * full_encoded_block_size]);
|
||||
}
|
||||
|
||||
if (0 < last_block_size) {
|
||||
encode_block(
|
||||
data.data() + full_block_count * full_block_size,
|
||||
last_block_size,
|
||||
&res[full_block_count * full_encoded_block_size]);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool decode(std::string_view enc, std::string& data) {
|
||||
if (enc.empty()) {
|
||||
data.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t full_block_count = enc.size() / full_encoded_block_size;
|
||||
size_t last_block_size = enc.size() % full_encoded_block_size;
|
||||
int8_t last_block_decoded_size = decoded_block_sizes[last_block_size];
|
||||
if (last_block_decoded_size < 0)
|
||||
return false; // Invalid enc length
|
||||
size_t data_size = full_block_count * full_block_size + last_block_decoded_size;
|
||||
|
||||
data.resize(data_size, 0);
|
||||
for (size_t i = 0; i < full_block_count; ++i) {
|
||||
if (!decode_block(
|
||||
enc.data() + i * full_encoded_block_size,
|
||||
full_encoded_block_size,
|
||||
&data[i * full_block_size]))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0 < last_block_size) {
|
||||
if (!decode_block(
|
||||
enc.data() + full_block_count * full_encoded_block_size,
|
||||
last_block_size,
|
||||
&data[full_block_count * full_block_size]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::string encode(std::string_view data)
|
||||
{
|
||||
if (data.empty())
|
||||
return std::string();
|
||||
|
||||
size_t full_block_count = data.size() / full_block_size;
|
||||
size_t last_block_size = data.size() % full_block_size;
|
||||
size_t res_size = full_block_count * full_encoded_block_size + encoded_block_sizes[last_block_size];
|
||||
|
||||
std::string res(res_size, alphabet[0]);
|
||||
for (size_t i = 0; i < full_block_count; ++i)
|
||||
{
|
||||
encode_block(data.data() + i * full_block_size, full_block_size, &res[i * full_encoded_block_size]);
|
||||
}
|
||||
|
||||
if (0 < last_block_size)
|
||||
{
|
||||
encode_block(data.data() + full_block_count * full_block_size, last_block_size, &res[full_block_count * full_encoded_block_size]);
|
||||
}
|
||||
|
||||
return res;
|
||||
std::string encode_addr(uint64_t tag, std::string_view data) {
|
||||
std::string buf = get_varint_data(tag);
|
||||
buf += data;
|
||||
crypto::hash hash = crypto::cn_fast_hash(buf.data(), buf.size());
|
||||
const char* hash_data = reinterpret_cast<const char*>(&hash);
|
||||
buf.append(hash_data, addr_checksum_size);
|
||||
return encode(buf);
|
||||
}
|
||||
|
||||
bool decode(std::string_view enc, std::string& data)
|
||||
{
|
||||
if (enc.empty())
|
||||
{
|
||||
data.clear();
|
||||
bool decode_addr(std::string_view addr, uint64_t& tag, std::string& data) {
|
||||
std::string addr_data;
|
||||
bool r = decode(addr, addr_data);
|
||||
if (!r)
|
||||
return false;
|
||||
if (addr_data.size() <= addr_checksum_size)
|
||||
return false;
|
||||
|
||||
std::string checksum(addr_checksum_size, '\0');
|
||||
checksum = addr_data.substr(addr_data.size() - addr_checksum_size);
|
||||
|
||||
addr_data.resize(addr_data.size() - addr_checksum_size);
|
||||
crypto::hash hash = crypto::cn_fast_hash(addr_data.data(), addr_data.size());
|
||||
std::string expected_checksum(reinterpret_cast<const char*>(&hash), addr_checksum_size);
|
||||
if (expected_checksum != checksum)
|
||||
return false;
|
||||
|
||||
int read = tools::read_varint(addr_data.begin(), addr_data.end(), tag);
|
||||
if (read <= 0)
|
||||
return false;
|
||||
|
||||
data = addr_data.substr(read);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t full_block_count = enc.size() / full_encoded_block_size;
|
||||
size_t last_block_size = enc.size() % full_encoded_block_size;
|
||||
int8_t last_block_decoded_size = decoded_block_sizes[last_block_size];
|
||||
if (last_block_decoded_size < 0)
|
||||
return false; // Invalid enc length
|
||||
size_t data_size = full_block_count * full_block_size + last_block_decoded_size;
|
||||
|
||||
data.resize(data_size, 0);
|
||||
for (size_t i = 0; i < full_block_count; ++i)
|
||||
{
|
||||
if (!decode_block(enc.data() + i * full_encoded_block_size, full_encoded_block_size, &data[i * full_block_size]))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0 < last_block_size)
|
||||
{
|
||||
if (!decode_block(enc.data() + full_block_count * full_encoded_block_size, last_block_size,
|
||||
&data[full_block_count * full_block_size]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string encode_addr(uint64_t tag, std::string_view data)
|
||||
{
|
||||
std::string buf = get_varint_data(tag);
|
||||
buf += data;
|
||||
crypto::hash hash = crypto::cn_fast_hash(buf.data(), buf.size());
|
||||
const char* hash_data = reinterpret_cast<const char*>(&hash);
|
||||
buf.append(hash_data, addr_checksum_size);
|
||||
return encode(buf);
|
||||
}
|
||||
|
||||
bool decode_addr(std::string_view addr, uint64_t& tag, std::string& data)
|
||||
{
|
||||
std::string addr_data;
|
||||
bool r = decode(addr, addr_data);
|
||||
if (!r) return false;
|
||||
if (addr_data.size() <= addr_checksum_size) return false;
|
||||
|
||||
std::string checksum(addr_checksum_size, '\0');
|
||||
checksum = addr_data.substr(addr_data.size() - addr_checksum_size);
|
||||
|
||||
addr_data.resize(addr_data.size() - addr_checksum_size);
|
||||
crypto::hash hash = crypto::cn_fast_hash(addr_data.data(), addr_data.size());
|
||||
std::string expected_checksum(reinterpret_cast<const char*>(&hash), addr_checksum_size);
|
||||
if (expected_checksum != checksum) return false;
|
||||
|
||||
int read = tools::read_varint(addr_data.begin(), addr_data.end(), tag);
|
||||
if (read <= 0) return false;
|
||||
|
||||
data = addr_data.substr(read);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace base58
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -25,7 +25,7 @@
|
|||
// 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.
|
||||
//
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
|
@ -34,14 +34,10 @@
|
|||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace tools
|
||||
{
|
||||
namespace base58
|
||||
{
|
||||
namespace tools { namespace base58 {
|
||||
std::string encode(std::string_view data);
|
||||
bool decode(std::string_view enc, std::string& data);
|
||||
|
||||
std::string encode_addr(uint64_t tag, std::string_view data);
|
||||
bool decode_addr(std::string_view addr, uint64_t& tag, std::string& data);
|
||||
}
|
||||
}
|
||||
}} // namespace tools::base58
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -25,66 +25,60 @@
|
|||
// 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.
|
||||
//
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/archive/binary_iarchive.hpp>
|
||||
#include <boost/archive/portable_binary_oarchive.hpp>
|
||||
#include <boost/archive/portable_binary_iarchive.hpp>
|
||||
#include "epee/misc_log_ex.h"
|
||||
#include "fs.h"
|
||||
#include <boost/archive/portable_binary_oarchive.hpp>
|
||||
#include <fstream>
|
||||
|
||||
#include "epee/misc_log_ex.h"
|
||||
#include "fs.h"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
template <class T>
|
||||
bool serialize_obj_to_file(T& obj, const fs::path& file_path)
|
||||
{
|
||||
namespace tools {
|
||||
template <class T>
|
||||
bool serialize_obj_to_file(T& obj, const fs::path& file_path) {
|
||||
TRY_ENTRY();
|
||||
fs::ofstream data_file{file_path, std::ios::binary | std::ios::trunc};
|
||||
if (data_file.fail())
|
||||
return false;
|
||||
return false;
|
||||
|
||||
boost::archive::portable_binary_oarchive{data_file} << obj;
|
||||
|
||||
if (data_file.fail())
|
||||
return false;
|
||||
return false;
|
||||
|
||||
data_file.flush();
|
||||
|
||||
return true;
|
||||
CATCH_ENTRY_L0("serialize_obj_to_file", false);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool unserialize_obj_from_file(T& obj, const fs::path& file_path)
|
||||
{
|
||||
template <class T>
|
||||
bool unserialize_obj_from_file(T& obj, const fs::path& file_path) {
|
||||
TRY_ENTRY();
|
||||
|
||||
fs::ifstream data_file{file_path, std::ios_base::binary};
|
||||
if (data_file.fail())
|
||||
return false;
|
||||
try
|
||||
{
|
||||
// first try reading in portable mode
|
||||
boost::archive::portable_binary_iarchive{data_file} >> obj;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
// if failed, try reading in unportable mode
|
||||
auto unportable = file_path;
|
||||
unportable += ".unportable";
|
||||
fs::copy_file(file_path, unportable, fs::copy_options::overwrite_existing);
|
||||
data_file.close();
|
||||
data_file.open(file_path, std::ios_base::binary);
|
||||
if (data_file.fail())
|
||||
return false;
|
||||
boost::archive::binary_iarchive{data_file} >> obj;
|
||||
try {
|
||||
// first try reading in portable mode
|
||||
boost::archive::portable_binary_iarchive{data_file} >> obj;
|
||||
} catch (...) {
|
||||
// if failed, try reading in unportable mode
|
||||
auto unportable = file_path;
|
||||
unportable += ".unportable";
|
||||
fs::copy_file(file_path, unportable, fs::copy_options::overwrite_existing);
|
||||
data_file.close();
|
||||
data_file.open(file_path, std::ios_base::binary);
|
||||
if (data_file.fail())
|
||||
return false;
|
||||
boost::archive::binary_iarchive{data_file} >> obj;
|
||||
}
|
||||
return !data_file.fail();
|
||||
CATCH_ENTRY_L0("unserialize_obj_from_file", false);
|
||||
}
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -32,19 +32,18 @@
|
|||
|
||||
namespace tools {
|
||||
|
||||
uint64_t combinations_count(uint32_t k, uint32_t n)
|
||||
{
|
||||
if (k > n) {
|
||||
throw std::runtime_error("k must not be greater than n");
|
||||
}
|
||||
uint64_t combinations_count(uint32_t k, uint32_t n) {
|
||||
if (k > n) {
|
||||
throw std::runtime_error("k must not be greater than n");
|
||||
}
|
||||
|
||||
uint64_t c = 1;
|
||||
for (uint64_t i = 1; i <= k; ++i) {
|
||||
c *= n--;
|
||||
c /= i;
|
||||
}
|
||||
uint64_t c = 1;
|
||||
for (uint64_t i = 1; i <= k; ++i) {
|
||||
c *= n--;
|
||||
c /= i;
|
||||
}
|
||||
|
||||
return c;
|
||||
return c;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -30,69 +30,64 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace tools {
|
||||
|
||||
uint64_t combinations_count(uint32_t k, uint32_t n);
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
class Combinator {
|
||||
public:
|
||||
Combinator(const std::vector<T>& v) : origin(v) { }
|
||||
public:
|
||||
Combinator(const std::vector<T>& v) : origin(v) {}
|
||||
|
||||
std::vector<std::vector<T>> combine(size_t k);
|
||||
std::vector<std::vector<T>> combine(size_t k);
|
||||
|
||||
private:
|
||||
void doCombine(size_t from, size_t k);
|
||||
private:
|
||||
void doCombine(size_t from, size_t k);
|
||||
|
||||
std::vector<T> origin;
|
||||
std::vector<std::vector<T>> combinations;
|
||||
std::vector<size_t> current;
|
||||
std::vector<T> origin;
|
||||
std::vector<std::vector<T>> combinations;
|
||||
std::vector<size_t> current;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
std::vector<std::vector<T>> Combinator<T>::combine(size_t k)
|
||||
{
|
||||
if (k > origin.size())
|
||||
{
|
||||
throw std::runtime_error("k must be smaller than elements number");
|
||||
}
|
||||
|
||||
if (k == 0)
|
||||
{
|
||||
throw std::runtime_error("k must be greater than zero");
|
||||
}
|
||||
|
||||
combinations.clear();
|
||||
doCombine(0, k);
|
||||
return combinations;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Combinator<T>::doCombine(size_t from, size_t k)
|
||||
{
|
||||
current.push_back(0);
|
||||
|
||||
for (size_t i = from; i <= origin.size() - k; ++i)
|
||||
{
|
||||
current.back() = i;
|
||||
|
||||
if (k > 1) {
|
||||
doCombine(i + 1, k - 1);
|
||||
} else {
|
||||
std::vector<T> comb;
|
||||
for (auto ind: current) {
|
||||
comb.push_back(origin[ind]);
|
||||
}
|
||||
combinations.push_back(comb);
|
||||
template <typename T>
|
||||
std::vector<std::vector<T>> Combinator<T>::combine(size_t k) {
|
||||
if (k > origin.size()) {
|
||||
throw std::runtime_error("k must be smaller than elements number");
|
||||
}
|
||||
}
|
||||
|
||||
current.pop_back();
|
||||
if (k == 0) {
|
||||
throw std::runtime_error("k must be greater than zero");
|
||||
}
|
||||
|
||||
combinations.clear();
|
||||
doCombine(0, k);
|
||||
return combinations;
|
||||
}
|
||||
|
||||
} //namespace tools
|
||||
template <typename T>
|
||||
void Combinator<T>::doCombine(size_t from, size_t k) {
|
||||
current.push_back(0);
|
||||
|
||||
for (size_t i = from; i <= origin.size() - k; ++i) {
|
||||
current.back() = i;
|
||||
|
||||
if (k > 1) {
|
||||
doCombine(i + 1, k - 1);
|
||||
} else {
|
||||
std::vector<T> comb;
|
||||
for (auto ind : current) {
|
||||
comb.push_back(origin[ind]);
|
||||
}
|
||||
combinations.push_back(comb);
|
||||
}
|
||||
}
|
||||
|
||||
current.pop_back();
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -25,22 +25,22 @@
|
|||
// 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.
|
||||
//
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#include "command_line.h"
|
||||
|
||||
#include "common/i18n.h"
|
||||
#include "common/string_util.h"
|
||||
#ifdef HAVE_READLINE
|
||||
# include "epee/readline_buffer.h"
|
||||
#include "epee/readline_buffer.h"
|
||||
#endif
|
||||
#include <iostream>
|
||||
#ifdef _WIN32
|
||||
#include "windows.h"
|
||||
#endif
|
||||
|
||||
namespace command_line
|
||||
{
|
||||
namespace command_line {
|
||||
const arg_descriptor<bool> arg_help = {"help", "Produce help message"};
|
||||
const arg_descriptor<bool> arg_version = {"version", "Output version information"};
|
||||
|
||||
|
@ -51,80 +51,77 @@ const arg_descriptor<bool> arg_version = {"version", "Output version information
|
|||
#ifdef __linux__
|
||||
|
||||
extern "C" {
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
}
|
||||
std::pair<unsigned, unsigned> terminal_size() {
|
||||
struct winsize w;
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1)
|
||||
return {w.ws_col, w.ws_row};
|
||||
return {w.ws_col, w.ws_row};
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::pair<unsigned, unsigned> terminal_size() { return {0, 0}; }
|
||||
std::pair<unsigned, unsigned> terminal_size() {
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
std::pair<unsigned, unsigned> boost_option_sizes() {
|
||||
std::pair<unsigned, unsigned> result;
|
||||
std::pair<unsigned, unsigned> result;
|
||||
|
||||
result.first = std::max(
|
||||
terminal_size().first,
|
||||
boost::program_options::options_description::m_default_line_length);
|
||||
result.first = std::max(
|
||||
terminal_size().first,
|
||||
boost::program_options::options_description::m_default_line_length);
|
||||
|
||||
result.second = result.first - boost::program_options::options_description::m_default_line_length / 2;
|
||||
result.second =
|
||||
result.first - boost::program_options::options_description::m_default_line_length / 2;
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
void clear_screen()
|
||||
{
|
||||
void clear_screen() {
|
||||
#ifdef HAVE_READLINE
|
||||
rdln::clear_screen();
|
||||
rdln::clear_screen();
|
||||
#else
|
||||
std::cout << "\033[2K"; // clear whole line
|
||||
std::cout << "\033c"; // clear current screen and scrollback
|
||||
std::cout << "\033[2J"; // clear current screen only, scrollback is still around
|
||||
std::cout << "\033[3J"; // does nothing, should clear current screen and scrollback
|
||||
std::cout << "\033[1;1H"; // move cursor top/left
|
||||
std::cout << "\r \r" << std::flush; // erase odd chars if the ANSI codes were printed raw
|
||||
#ifdef _WIN32
|
||||
COORD coord{0, 0};
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (GetConsoleScreenBufferInfo(h, &csbi))
|
||||
{
|
||||
DWORD cbConSize = csbi.dwSize.X * csbi.dwSize.Y, w;
|
||||
FillConsoleOutputCharacter(h, (TCHAR)' ', cbConSize, coord, &w);
|
||||
if (GetConsoleScreenBufferInfo(h, &csbi))
|
||||
FillConsoleOutputAttribute(h, csbi.wAttributes, cbConSize, coord, &w);
|
||||
SetConsoleCursorPosition(h, coord);
|
||||
}
|
||||
#endif
|
||||
std::cout << "\033[2K"; // clear whole line
|
||||
std::cout << "\033c"; // clear current screen and scrollback
|
||||
std::cout << "\033[2J"; // clear current screen only, scrollback is still around
|
||||
std::cout << "\033[3J"; // does nothing, should clear current screen and scrollback
|
||||
std::cout << "\033[1;1H"; // move cursor top/left
|
||||
std::cout << "\r \r"
|
||||
<< std::flush; // erase odd chars if the ANSI codes were printed raw
|
||||
#ifdef _WIN32
|
||||
COORD coord{0, 0};
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (GetConsoleScreenBufferInfo(h, &csbi)) {
|
||||
DWORD cbConSize = csbi.dwSize.X * csbi.dwSize.Y, w;
|
||||
FillConsoleOutputCharacter(h, (TCHAR)' ', cbConSize, coord, &w);
|
||||
if (GetConsoleScreenBufferInfo(h, &csbi))
|
||||
FillConsoleOutputAttribute(h, csbi.wAttributes, cbConSize, coord, &w);
|
||||
SetConsoleCursorPosition(h, coord);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool handle_error_helper(const boost::program_options::options_description& desc, std::function<bool()> parser) {
|
||||
try
|
||||
{
|
||||
return parser();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "Failed to parse arguments: " << e.what() << std::endl;
|
||||
std::cerr << desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cerr << "Failed to parse arguments: unknown exception" << std::endl;
|
||||
std::cerr << desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
bool handle_error_helper(
|
||||
const boost::program_options::options_description& desc, std::function<bool()> parser) {
|
||||
try {
|
||||
return parser();
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Failed to parse arguments: " << e.what() << std::endl;
|
||||
std::cerr << desc << std::endl;
|
||||
return false;
|
||||
} catch (...) {
|
||||
std::cerr << "Failed to parse arguments: unknown exception" << std::endl;
|
||||
std::cerr << desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace command_line
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -25,74 +25,80 @@
|
|||
// 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.
|
||||
//
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <array>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
#include "common/string_util.h"
|
||||
#include "common/i18n.h"
|
||||
#include "common/format.h"
|
||||
#include "common/i18n.h"
|
||||
#include "common/string_util.h"
|
||||
#include "logging/oxen_logger.h"
|
||||
|
||||
namespace command_line
|
||||
{
|
||||
namespace log = oxen::log;
|
||||
namespace command_line {
|
||||
namespace log = oxen::log;
|
||||
|
||||
inline const char* tr(const char* str) { return i18n_translate(str, "command_line"); }
|
||||
inline const char* tr(const char* str) {
|
||||
return i18n_translate(str, "command_line");
|
||||
}
|
||||
|
||||
/// @return True if `str` is (case-insensitively) y, yes, a potentially translated yes, or any of
|
||||
/// the optional extra arguments passed in.
|
||||
template <typename S, typename... More>
|
||||
bool is_yes(const S& str, const More&... more) { return tools::string_iequal_any(str, "y", "yes", tr("yes"), more...); }
|
||||
/// @return True if `str` is (case-insensitively) n, no, or a potentially translated no, or any of
|
||||
/// the optional extra arguments passed in.
|
||||
template <typename S, typename... More>
|
||||
bool is_no(const S& str, const More&... more) { return tools::string_iequal_any(str, "n", "no", tr("no"), more...); }
|
||||
/// @return True if `str` is (case-insensitively) c, cancel, or a potentially translated cancel,
|
||||
/// or any of the optional extra arguments passed in.
|
||||
template <typename S, typename... More>
|
||||
bool is_cancel(const S& str, const More&... more) { return tools::string_iequal_any(str, "c", "cancel", tr("cancel"), more...); }
|
||||
/// @return True if `str` is (case-insensitively) b, back, or a potentially translated back, or
|
||||
/// any of the optional extra arguments passed in.
|
||||
template <typename S, typename... More>
|
||||
bool is_back(const S& str, const More&... more) { return tools::string_iequal_any(str, "b", "back", tr("back"), more...); }
|
||||
/// @return True if `str` is (case-insensitively) y, yes, a potentially translated yes, or any of
|
||||
/// the optional extra arguments passed in.
|
||||
template <typename S, typename... More>
|
||||
bool is_yes(const S& str, const More&... more) {
|
||||
return tools::string_iequal_any(str, "y", "yes", tr("yes"), more...);
|
||||
}
|
||||
/// @return True if `str` is (case-insensitively) n, no, or a potentially translated no, or any of
|
||||
/// the optional extra arguments passed in.
|
||||
template <typename S, typename... More>
|
||||
bool is_no(const S& str, const More&... more) {
|
||||
return tools::string_iequal_any(str, "n", "no", tr("no"), more...);
|
||||
}
|
||||
/// @return True if `str` is (case-insensitively) c, cancel, or a potentially translated cancel,
|
||||
/// or any of the optional extra arguments passed in.
|
||||
template <typename S, typename... More>
|
||||
bool is_cancel(const S& str, const More&... more) {
|
||||
return tools::string_iequal_any(str, "c", "cancel", tr("cancel"), more...);
|
||||
}
|
||||
/// @return True if `str` is (case-insensitively) b, back, or a potentially translated back, or
|
||||
/// any of the optional extra arguments passed in.
|
||||
template <typename S, typename... More>
|
||||
bool is_back(const S& str, const More&... more) {
|
||||
return tools::string_iequal_any(str, "b", "back", tr("back"), more...);
|
||||
}
|
||||
|
||||
template<typename T, bool required = false, bool dependent = false, int NUM_DEPS = 1>
|
||||
struct arg_descriptor;
|
||||
template <typename T, bool required = false, bool dependent = false, int NUM_DEPS = 1>
|
||||
struct arg_descriptor;
|
||||
|
||||
template<typename T>
|
||||
struct arg_descriptor<T, false>
|
||||
{
|
||||
template <typename T>
|
||||
struct arg_descriptor<T, false> {
|
||||
using value_type = T;
|
||||
|
||||
const char* name;
|
||||
const char* description;
|
||||
T default_value;
|
||||
bool not_use_default;
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct arg_descriptor<T, true>
|
||||
{
|
||||
template <typename T>
|
||||
struct arg_descriptor<T, true> {
|
||||
static_assert(!std::is_same_v<T, bool>, "Boolean switch can't be required");
|
||||
|
||||
using value_type = T;
|
||||
|
||||
const char* name;
|
||||
const char* description;
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct arg_descriptor<T, false, true>
|
||||
{
|
||||
template <typename T>
|
||||
struct arg_descriptor<T, false, true> {
|
||||
using value_type = T;
|
||||
|
||||
const char* name;
|
||||
|
@ -104,11 +110,10 @@ namespace command_line
|
|||
std::function<T(bool, bool, T)> depf;
|
||||
|
||||
bool not_use_default;
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T, int NUM_DEPS>
|
||||
struct arg_descriptor<T, false, true, NUM_DEPS>
|
||||
{
|
||||
template <typename T, int NUM_DEPS>
|
||||
struct arg_descriptor<T, false, true, NUM_DEPS> {
|
||||
using value_type = T;
|
||||
|
||||
const char* name;
|
||||
|
@ -116,200 +121,209 @@ namespace command_line
|
|||
|
||||
T default_value;
|
||||
|
||||
std::array<const arg_descriptor<bool, false> *, NUM_DEPS> ref;
|
||||
std::array<const arg_descriptor<bool, false>*, NUM_DEPS> ref;
|
||||
std::function<T(std::array<bool, NUM_DEPS>, bool, T)> depf;
|
||||
|
||||
bool not_use_default;
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
boost::program_options::typed_value<T, char>* make_semantic(const arg_descriptor<T, true>& /*arg*/)
|
||||
{
|
||||
template <typename T>
|
||||
boost::program_options::typed_value<T, char>* make_semantic(
|
||||
const arg_descriptor<T, true>& /*arg*/) {
|
||||
return boost::program_options::value<T>()->required();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
boost::program_options::typed_value<T, char>* make_semantic(const arg_descriptor<T, false>& arg)
|
||||
{
|
||||
template <typename T>
|
||||
boost::program_options::typed_value<T, char>* make_semantic(const arg_descriptor<T, false>& arg) {
|
||||
auto semantic = boost::program_options::value<T>();
|
||||
if (!arg.not_use_default)
|
||||
semantic->default_value(arg.default_value);
|
||||
semantic->default_value(arg.default_value);
|
||||
return semantic;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
namespace {
|
||||
template <typename T>
|
||||
std::string arg_stringify(const T& a) {
|
||||
return "{}"_format(a);
|
||||
return "{}"_format(a);
|
||||
}
|
||||
template <typename T>
|
||||
std::string arg_stringify(const std::vector<T>& v) {
|
||||
return "{{{}}}"_format(fmt::join(v, ","));
|
||||
return "{{{}}}"_format(fmt::join(v, ","));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template<typename T>
|
||||
boost::program_options::typed_value<T, char>* make_semantic(const arg_descriptor<T, false, true>& arg)
|
||||
{
|
||||
template <typename T>
|
||||
boost::program_options::typed_value<T, char>* make_semantic(
|
||||
const arg_descriptor<T, false, true>& arg) {
|
||||
auto semantic = boost::program_options::value<T>();
|
||||
if (!arg.not_use_default) {
|
||||
auto default_display = "{}, {} if '{}'"_format(
|
||||
arg_stringify(arg.depf(false, true, arg.default_value)),
|
||||
arg_stringify(arg.depf(true, true, arg.default_value)),
|
||||
arg.ref.name);
|
||||
semantic->default_value(arg.depf(arg.ref.default_value, true, arg.default_value), default_display);
|
||||
auto default_display = "{}, {} if '{}'"_format(
|
||||
arg_stringify(arg.depf(false, true, arg.default_value)),
|
||||
arg_stringify(arg.depf(true, true, arg.default_value)),
|
||||
arg.ref.name);
|
||||
semantic->default_value(
|
||||
arg.depf(arg.ref.default_value, true, arg.default_value), default_display);
|
||||
}
|
||||
return semantic;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, int NUM_DEPS>
|
||||
boost::program_options::typed_value<T, char>* make_semantic(const arg_descriptor<T, false, true, NUM_DEPS>& arg)
|
||||
{
|
||||
template <typename T, int NUM_DEPS>
|
||||
boost::program_options::typed_value<T, char>* make_semantic(
|
||||
const arg_descriptor<T, false, true, NUM_DEPS>& arg) {
|
||||
auto semantic = boost::program_options::value<T>();
|
||||
if (!arg.not_use_default) {
|
||||
std::array<bool, NUM_DEPS> depval;
|
||||
depval.fill(false);
|
||||
auto default_display = arg_stringify(arg.depf(depval, true, arg.default_value));
|
||||
for (size_t i = 0; i < depval.size(); ++i)
|
||||
{
|
||||
std::array<bool, NUM_DEPS> depval;
|
||||
depval.fill(false);
|
||||
depval[i] = true;
|
||||
fmt::format_to(std::back_inserter(default_display),
|
||||
", {} if '{}'",
|
||||
arg_stringify(arg.depf(depval, true, arg.default_value)),
|
||||
arg.ref[i]->name);
|
||||
}
|
||||
for (size_t i = 0; i < depval.size(); ++i)
|
||||
depval[i] = arg.ref[i]->default_value;
|
||||
semantic->default_value(arg.depf(depval, true, arg.default_value), default_display);
|
||||
auto default_display = arg_stringify(arg.depf(depval, true, arg.default_value));
|
||||
for (size_t i = 0; i < depval.size(); ++i) {
|
||||
depval.fill(false);
|
||||
depval[i] = true;
|
||||
fmt::format_to(
|
||||
std::back_inserter(default_display),
|
||||
", {} if '{}'",
|
||||
arg_stringify(arg.depf(depval, true, arg.default_value)),
|
||||
arg.ref[i]->name);
|
||||
}
|
||||
for (size_t i = 0; i < depval.size(); ++i)
|
||||
depval[i] = arg.ref[i]->default_value;
|
||||
semantic->default_value(arg.depf(depval, true, arg.default_value), default_display);
|
||||
}
|
||||
return semantic;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
boost::program_options::typed_value<T, char>* make_semantic(const arg_descriptor<T, false>& arg, const T& def)
|
||||
{
|
||||
template <typename T>
|
||||
boost::program_options::typed_value<T, char>* make_semantic(
|
||||
const arg_descriptor<T, false>& arg, const T& def) {
|
||||
auto semantic = boost::program_options::value<T>();
|
||||
if (!arg.not_use_default)
|
||||
semantic->default_value(def);
|
||||
semantic->default_value(def);
|
||||
return semantic;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
boost::program_options::typed_value<std::vector<T>, char>* make_semantic(const arg_descriptor<std::vector<T>, false>& /*arg*/)
|
||||
{
|
||||
auto semantic = boost::program_options::value< std::vector<T> >();
|
||||
template <typename T>
|
||||
boost::program_options::typed_value<std::vector<T>, char>* make_semantic(
|
||||
const arg_descriptor<std::vector<T>, false>& /*arg*/) {
|
||||
auto semantic = boost::program_options::value<std::vector<T>>();
|
||||
semantic->default_value(std::vector<T>(), "");
|
||||
return semantic;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, bool required, bool dependent, int NUM_DEPS>
|
||||
void add_arg(boost::program_options::options_description& description, const arg_descriptor<T, required, dependent, NUM_DEPS>& arg, bool unique = true)
|
||||
{
|
||||
if (0 != description.find_nothrow(arg.name, false))
|
||||
{
|
||||
if (!unique)
|
||||
log::error(globallogcat, "Argument already exists: {}", arg.name);
|
||||
return;
|
||||
template <typename T, bool required, bool dependent, int NUM_DEPS>
|
||||
void add_arg(
|
||||
boost::program_options::options_description& description,
|
||||
const arg_descriptor<T, required, dependent, NUM_DEPS>& arg,
|
||||
bool unique = true) {
|
||||
if (0 != description.find_nothrow(arg.name, false)) {
|
||||
if (!unique)
|
||||
log::error(globallogcat, "Argument already exists: {}", arg.name);
|
||||
return;
|
||||
}
|
||||
|
||||
description.add_options()(arg.name, make_semantic(arg), arg.description);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void add_arg(boost::program_options::options_description& description, const arg_descriptor<T, false>& arg, const T& def, bool unique = true)
|
||||
{
|
||||
if (0 != description.find_nothrow(arg.name, false))
|
||||
{
|
||||
if (!unique)
|
||||
log::error(globallogcat, "Argument already exists: {}", arg.name);
|
||||
return;
|
||||
template <typename T>
|
||||
void add_arg(
|
||||
boost::program_options::options_description& description,
|
||||
const arg_descriptor<T, false>& arg,
|
||||
const T& def,
|
||||
bool unique = true) {
|
||||
if (0 != description.find_nothrow(arg.name, false)) {
|
||||
if (!unique)
|
||||
log::error(globallogcat, "Argument already exists: {}", arg.name);
|
||||
return;
|
||||
}
|
||||
|
||||
description.add_options()(arg.name, make_semantic(arg, def), arg.description);
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void add_arg(boost::program_options::options_description& description, const arg_descriptor<bool, false>& arg, bool unique)
|
||||
{
|
||||
if (0 != description.find_nothrow(arg.name, false))
|
||||
{
|
||||
if (!unique)
|
||||
log::error(globallogcat, "Argument already exists: {}", arg.name);
|
||||
return;
|
||||
template <>
|
||||
inline void add_arg(
|
||||
boost::program_options::options_description& description,
|
||||
const arg_descriptor<bool, false>& arg,
|
||||
bool unique) {
|
||||
if (0 != description.find_nothrow(arg.name, false)) {
|
||||
if (!unique)
|
||||
log::error(globallogcat, "Argument already exists: {}", arg.name);
|
||||
return;
|
||||
}
|
||||
|
||||
description.add_options()(arg.name, boost::program_options::bool_switch(), arg.description);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename charT>
|
||||
boost::program_options::basic_parsed_options<charT> parse_command_line(int argc, const charT* const argv[],
|
||||
const boost::program_options::options_description& desc, bool allow_unregistered = false)
|
||||
{
|
||||
template <typename charT>
|
||||
boost::program_options::basic_parsed_options<charT> parse_command_line(
|
||||
int argc,
|
||||
const charT* const argv[],
|
||||
const boost::program_options::options_description& desc,
|
||||
bool allow_unregistered = false) {
|
||||
auto parser = boost::program_options::command_line_parser(argc, argv);
|
||||
parser.options(desc);
|
||||
if (allow_unregistered)
|
||||
{
|
||||
parser.allow_unregistered();
|
||||
if (allow_unregistered) {
|
||||
parser.allow_unregistered();
|
||||
}
|
||||
return parser.run();
|
||||
}
|
||||
}
|
||||
|
||||
bool handle_error_helper(const boost::program_options::options_description& desc, std::function<bool()> parser);
|
||||
bool handle_error_helper(
|
||||
const boost::program_options::options_description& desc, std::function<bool()> parser);
|
||||
|
||||
template<typename T, bool required, bool dependent, int NUM_DEPS>
|
||||
std::enable_if_t<!std::is_same_v<T, bool>, bool> has_arg(const boost::program_options::variables_map& vm, const arg_descriptor<T, required, dependent, NUM_DEPS>& arg)
|
||||
{
|
||||
template <typename T, bool required, bool dependent, int NUM_DEPS>
|
||||
std::enable_if_t<!std::is_same_v<T, bool>, bool> has_arg(
|
||||
const boost::program_options::variables_map& vm,
|
||||
const arg_descriptor<T, required, dependent, NUM_DEPS>& arg) {
|
||||
auto value = vm[arg.name];
|
||||
return !value.empty();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, bool required, bool dependent, int NUM_DEPS>
|
||||
bool is_arg_defaulted(const boost::program_options::variables_map& vm, const arg_descriptor<T, required, dependent, NUM_DEPS>& arg)
|
||||
{
|
||||
template <typename T, bool required, bool dependent, int NUM_DEPS>
|
||||
bool is_arg_defaulted(
|
||||
const boost::program_options::variables_map& vm,
|
||||
const arg_descriptor<T, required, dependent, NUM_DEPS>& arg) {
|
||||
return vm[arg.name].defaulted();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T get_arg(const boost::program_options::variables_map& vm, const arg_descriptor<T, false, true>& arg)
|
||||
{
|
||||
template <typename T>
|
||||
T get_arg(
|
||||
const boost::program_options::variables_map& vm,
|
||||
const arg_descriptor<T, false, true>& arg) {
|
||||
return arg.depf(get_arg(vm, arg.ref), is_arg_defaulted(vm, arg), vm[arg.name].template as<T>());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, int NUM_DEPS>
|
||||
T get_arg(const boost::program_options::variables_map& vm, const arg_descriptor<T, false, true, NUM_DEPS>& arg)
|
||||
{
|
||||
template <typename T, int NUM_DEPS>
|
||||
T get_arg(
|
||||
const boost::program_options::variables_map& vm,
|
||||
const arg_descriptor<T, false, true, NUM_DEPS>& arg) {
|
||||
std::array<bool, NUM_DEPS> depval;
|
||||
for (size_t i = 0; i < depval.size(); ++i)
|
||||
depval[i] = get_arg(vm, *arg.ref[i]);
|
||||
depval[i] = get_arg(vm, *arg.ref[i]);
|
||||
return arg.depf(depval, is_arg_defaulted(vm, arg), vm[arg.name].template as<T>());
|
||||
}
|
||||
|
||||
template<typename T, bool required>
|
||||
T get_arg(const boost::program_options::variables_map& vm, const arg_descriptor<T, required>& arg)
|
||||
{
|
||||
return vm[arg.name].template as<T>();
|
||||
}
|
||||
|
||||
template<bool dependent, int NUM_DEPS>
|
||||
inline bool has_arg(const boost::program_options::variables_map& vm, const arg_descriptor<bool, false, dependent, NUM_DEPS>& arg)
|
||||
{
|
||||
return get_arg(vm, arg);
|
||||
}
|
||||
|
||||
|
||||
extern const arg_descriptor<bool> arg_help;
|
||||
extern const arg_descriptor<bool> arg_version;
|
||||
|
||||
/// Returns the terminal width and height (in characters), if supported on this system and
|
||||
/// available. Returns {0,0} if not available or could not be determined.
|
||||
std::pair<unsigned, unsigned> terminal_size();
|
||||
|
||||
/// Returns the ideal line width and description width values for
|
||||
/// boost::program_options::options_description, using the terminal width (if available). Returns
|
||||
/// the boost defaults if terminal width isn't available.
|
||||
std::pair<unsigned, unsigned> boost_option_sizes();
|
||||
|
||||
// Clears the screen using readline, if available, otherwise trying some terminal escape hacks.
|
||||
void clear_screen();
|
||||
}
|
||||
|
||||
template <typename T, bool required>
|
||||
T get_arg(const boost::program_options::variables_map& vm, const arg_descriptor<T, required>& arg) {
|
||||
return vm[arg.name].template as<T>();
|
||||
}
|
||||
|
||||
template <bool dependent, int NUM_DEPS>
|
||||
inline bool has_arg(
|
||||
const boost::program_options::variables_map& vm,
|
||||
const arg_descriptor<bool, false, dependent, NUM_DEPS>& arg) {
|
||||
return get_arg(vm, arg);
|
||||
}
|
||||
|
||||
extern const arg_descriptor<bool> arg_help;
|
||||
extern const arg_descriptor<bool> arg_version;
|
||||
|
||||
/// Returns the terminal width and height (in characters), if supported on this system and
|
||||
/// available. Returns {0,0} if not available or could not be determined.
|
||||
std::pair<unsigned, unsigned> terminal_size();
|
||||
|
||||
/// Returns the ideal line width and description width values for
|
||||
/// boost::program_options::options_description, using the terminal width (if available). Returns
|
||||
/// the boost defaults if terminal width isn't available.
|
||||
std::pair<unsigned, unsigned> boost_option_sizes();
|
||||
|
||||
// Clears the screen using readline, if available, otherwise trying some terminal escape hacks.
|
||||
void clear_screen();
|
||||
} // namespace command_line
|
||||
|
|
|
@ -25,15 +25,14 @@
|
|||
// 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.
|
||||
//
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tools
|
||||
{
|
||||
struct login;
|
||||
class password_container;
|
||||
class t_http_connection;
|
||||
class threadpool;
|
||||
}
|
||||
namespace tools {
|
||||
struct login;
|
||||
class password_container;
|
||||
class t_http_connection;
|
||||
class threadpool;
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#include <fnmatch.h>
|
||||
#include <glob.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
#include <glob.h>
|
||||
#include <unistd.h>
|
||||
#include <fnmatch.h>
|
||||
|
||||
#if defined(HAVE_SYS_SELECT_H)
|
||||
#include <sys/select.h>
|
||||
|
@ -12,9 +13,8 @@
|
|||
|
||||
// Prior to GLIBC_2.14, memcpy was aliased to memmove.
|
||||
extern "C" void* memmove(void* a, const void* b, size_t c);
|
||||
//extern "C" void* memset(void* a, int b, long unsigned int c);
|
||||
extern "C" void* memcpy(void* a, const void* b, size_t c)
|
||||
{
|
||||
// extern "C" void* memset(void* a, int b, long unsigned int c);
|
||||
extern "C" void* memcpy(void* a, const void* b, size_t c) {
|
||||
return memmove(a, b, c);
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,7 @@ extern "C" void __chk_fail(void) __attribute__((__noreturn__));
|
|||
|
||||
extern "C" int64_t __udivmoddi4(uint64_t u, uint64_t v, uint64_t* rp);
|
||||
|
||||
extern "C" int64_t __wrap___divmoddi4(int64_t u, int64_t v, int64_t* rp)
|
||||
{
|
||||
extern "C" int64_t __wrap___divmoddi4(int64_t u, int64_t v, int64_t* rp) {
|
||||
int32_t c1 = 0, c2 = 0;
|
||||
int64_t uu = u, vv = v;
|
||||
int64_t w;
|
||||
|
@ -54,24 +53,20 @@ extern "C" int64_t __wrap___divmoddi4(int64_t u, int64_t v, int64_t* rp)
|
|||
#undef explicit_bzero
|
||||
/* Set LEN bytes of S to 0. The compiler will not delete a call to
|
||||
this function, even if S is dead after the call. */
|
||||
void
|
||||
explicit_bzero (void *s, size_t len)
|
||||
{
|
||||
memset (s, '\0', len);
|
||||
/* Compiler barrier. */
|
||||
asm volatile ("" ::: "memory");
|
||||
void explicit_bzero(void* s, size_t len) {
|
||||
memset(s, '\0', len);
|
||||
/* Compiler barrier. */
|
||||
asm volatile("" ::: "memory");
|
||||
}
|
||||
|
||||
// Redefine explicit_bzero_chk
|
||||
void
|
||||
__explicit_bzero_chk (void *dst, size_t len, size_t dstlen)
|
||||
{
|
||||
/* Inline __memset_chk to avoid a PLT reference to __memset_chk. */
|
||||
if (__glibc_unlikely (dstlen < len))
|
||||
__chk_fail ();
|
||||
memset (dst, '\0', len);
|
||||
/* Compiler barrier. */
|
||||
asm volatile ("" ::: "memory");
|
||||
void __explicit_bzero_chk(void* dst, size_t len, size_t dstlen) {
|
||||
/* Inline __memset_chk to avoid a PLT reference to __memset_chk. */
|
||||
if (__glibc_unlikely(dstlen < len))
|
||||
__chk_fail();
|
||||
memset(dst, '\0', len);
|
||||
/* Compiler barrier. */
|
||||
asm volatile("" ::: "memory");
|
||||
}
|
||||
/* libc-internal references use the hidden
|
||||
__explicit_bzero_chk_internal symbol. This is necessary if
|
||||
|
@ -80,7 +75,11 @@ __explicit_bzero_chk (void *dst, size_t len, size_t dstlen)
|
|||
#define strong_alias (__explicit_bzero_chk, __explicit_bzero_chk_internal)
|
||||
|
||||
#undef glob
|
||||
extern "C" int glob_old(const char * pattern, int flags, int (*errfunc) (const char *epath, int eerrno), glob_t *pglob);
|
||||
extern "C" int glob_old(
|
||||
const char* pattern,
|
||||
int flags,
|
||||
int (*errfunc)(const char* epath, int eerrno),
|
||||
glob_t* pglob);
|
||||
#ifdef __i386__
|
||||
__asm__(".symver glob_old,glob@GLIBC_2.0");
|
||||
#elif defined(__amd64__)
|
||||
|
@ -91,8 +90,10 @@ __asm(".symver glob_old,glob@GLIBC_2.4");
|
|||
__asm__(".symver glob_old,glob@GLIBC_2.17");
|
||||
#endif
|
||||
|
||||
extern "C" int __wrap_glob(const char * pattern, int flags, int (*errfunc) (const char *epath, int eerrno), glob_t *pglob)
|
||||
{
|
||||
extern "C" int __wrap_glob(
|
||||
const char* pattern,
|
||||
int flags,
|
||||
int (*errfunc)(const char* epath, int eerrno),
|
||||
glob_t* pglob) {
|
||||
return glob_old(pattern, flags, errfunc, pglob);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,49 +28,36 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct category final : std::error_category
|
||||
{
|
||||
virtual const char* name() const noexcept override final
|
||||
{
|
||||
return "common_category()";
|
||||
namespace {
|
||||
struct category final : std::error_category {
|
||||
virtual const char* name() const noexcept override final { return "common_category()"; }
|
||||
|
||||
virtual std::string message(int value) const override final {
|
||||
switch (common_error(value)) {
|
||||
case common_error::kInvalidArgument:
|
||||
return std::error_code{static_cast<int>(std::errc::invalid_argument), *this}
|
||||
.message();
|
||||
case common_error::kInvalidErrorCode:
|
||||
return "expect<T> was given an error value of zero";
|
||||
default: break;
|
||||
}
|
||||
return "Unknown basic_category() value";
|
||||
}
|
||||
|
||||
virtual std::string message(int value) const override final
|
||||
{
|
||||
switch (common_error(value))
|
||||
{
|
||||
case common_error::kInvalidArgument:
|
||||
return std::error_code{static_cast<int>(std::errc::invalid_argument), *this}.message();
|
||||
case common_error::kInvalidErrorCode:
|
||||
return "expect<T> was given an error value of zero";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "Unknown basic_category() value";
|
||||
virtual std::error_condition default_error_condition(int value) const noexcept override final {
|
||||
// maps specific errors to generic `std::errc` cases.
|
||||
switch (common_error(value)) {
|
||||
case common_error::kInvalidArgument:
|
||||
case common_error::kInvalidErrorCode: return std::errc::invalid_argument;
|
||||
default: break;
|
||||
}
|
||||
return std::error_condition{value, *this};
|
||||
}
|
||||
};
|
||||
|
||||
virtual std::error_condition default_error_condition(int value) const noexcept override final
|
||||
{
|
||||
// maps specific errors to generic `std::errc` cases.
|
||||
switch (common_error(value))
|
||||
{
|
||||
case common_error::kInvalidArgument:
|
||||
case common_error::kInvalidErrorCode:
|
||||
return std::errc::invalid_argument;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return std::error_condition{value, *this};
|
||||
}
|
||||
};
|
||||
const category instance{};
|
||||
} // namespace
|
||||
|
||||
const category instance{};
|
||||
}
|
||||
|
||||
std::error_category const& common_category() noexcept
|
||||
{
|
||||
std::error_category const& common_category() noexcept {
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,24 +28,19 @@
|
|||
|
||||
#include <system_error>
|
||||
|
||||
enum class common_error : int
|
||||
{
|
||||
enum class common_error : int {
|
||||
// 0 is reserved for no error, as per expect<T>
|
||||
kInvalidArgument = 1, //!< A function argument is invalid
|
||||
kInvalidErrorCode //!< Default `std::error_code` given to `expect<T>`
|
||||
kInvalidArgument = 1, //!< A function argument is invalid
|
||||
kInvalidErrorCode //!< Default `std::error_code` given to `expect<T>`
|
||||
};
|
||||
|
||||
std::error_category const& common_category() noexcept;
|
||||
|
||||
inline std::error_code make_error_code(::common_error value) noexcept
|
||||
{
|
||||
inline std::error_code make_error_code(::common_error value) noexcept {
|
||||
return std::error_code{static_cast<int>(value), common_category()};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct is_error_code_enum<::common_error>
|
||||
: true_type
|
||||
{};
|
||||
}
|
||||
namespace std {
|
||||
template <>
|
||||
struct is_error_code_enum<::common_error> : true_type {};
|
||||
} // namespace std
|
||||
|
|
|
@ -26,43 +26,38 @@
|
|||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "expect.h"
|
||||
#include "common/fs.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace detail
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::string generate_error(const char* msg, const char* file, unsigned line)
|
||||
{
|
||||
std::string error_msg{};
|
||||
if (msg)
|
||||
{
|
||||
error_msg.append(msg);
|
||||
if (file)
|
||||
error_msg.append(" (");
|
||||
}
|
||||
#include "common/fs.h"
|
||||
|
||||
namespace detail {
|
||||
namespace {
|
||||
std::string generate_error(const char* msg, const char* file, unsigned line) {
|
||||
std::string error_msg{};
|
||||
if (msg) {
|
||||
error_msg.append(msg);
|
||||
if (file)
|
||||
{
|
||||
error_msg.append("thrown at ");
|
||||
|
||||
// remove path, get just filename + extension
|
||||
error_msg.append(fs::path(file).filename().string());
|
||||
|
||||
error_msg.push_back(':');
|
||||
error_msg.append(std::to_string(line));
|
||||
}
|
||||
if (msg && file)
|
||||
error_msg.push_back(')');
|
||||
return error_msg;
|
||||
error_msg.append(" (");
|
||||
}
|
||||
}
|
||||
if (file) {
|
||||
error_msg.append("thrown at ");
|
||||
|
||||
void expect::throw_(std::error_code ec, const char* msg, const char* file, unsigned line)
|
||||
{
|
||||
if (msg || file)
|
||||
throw std::system_error{ec, generate_error(msg, file, line)};
|
||||
throw std::system_error{ec};
|
||||
// remove path, get just filename + extension
|
||||
error_msg.append(fs::path(file).filename().string());
|
||||
|
||||
error_msg.push_back(':');
|
||||
error_msg.append(std::to_string(line));
|
||||
}
|
||||
if (msg && file)
|
||||
error_msg.push_back(')');
|
||||
return error_msg;
|
||||
}
|
||||
} // detail
|
||||
} // namespace
|
||||
|
||||
void expect::throw_(std::error_code ec, const char* msg, const char* file, unsigned line) {
|
||||
if (msg || file)
|
||||
throw std::system_error{ec, generate_error(msg, file, line)};
|
||||
throw std::system_error{ec};
|
||||
}
|
||||
} // namespace detail
|
||||
|
|
|
@ -37,19 +37,17 @@
|
|||
|
||||
//! If precondition fails, return `::error::kInvalidArgument` in current scope.
|
||||
#define MONERO_PRECOND(...) \
|
||||
do \
|
||||
{ \
|
||||
if (!( __VA_ARGS__ )) \
|
||||
do { \
|
||||
if (!(__VA_ARGS__)) \
|
||||
return {::common_error::kInvalidArgument}; \
|
||||
} while (0)
|
||||
|
||||
//! Check `expect<void>` and return errors in current scope.
|
||||
#define MONERO_CHECK(...) \
|
||||
do \
|
||||
{ \
|
||||
const ::expect<void> result = __VA_ARGS__ ; \
|
||||
if (!result) \
|
||||
return result.error(); \
|
||||
#define MONERO_CHECK(...) \
|
||||
do { \
|
||||
const ::expect<void> result = __VA_ARGS__; \
|
||||
if (!result) \
|
||||
return result.error(); \
|
||||
} while (0)
|
||||
|
||||
/*! Get `T` from `expect<T>` by `std::move` as-if by function call.
|
||||
|
@ -57,38 +55,34 @@
|
|||
|
||||
\throw std::system_error with `expect<T>::error()`, filename and line
|
||||
number when `expect<T>::has_error() == true`.*/
|
||||
#define MONERO_UNWRAP(...) \
|
||||
::detail::expect::unwrap( __VA_ARGS__ , nullptr, __FILE__ , __LINE__ )
|
||||
#define MONERO_UNWRAP(...) ::detail::expect::unwrap(__VA_ARGS__, nullptr, __FILE__, __LINE__)
|
||||
|
||||
/* \throw std::system_error with `code` and `msg` as part of the details. The
|
||||
filename and line number will automatically be injected into the explanation
|
||||
string. `code` can be any enum convertible to `std::error_code`. */
|
||||
#define MONERO_THROW(code, msg) \
|
||||
::detail::expect::throw_( code , msg , __FILE__ , __LINE__ )
|
||||
#define MONERO_THROW(code, msg) ::detail::expect::throw_(code, msg, __FILE__, __LINE__)
|
||||
|
||||
template <typename>
|
||||
class expect;
|
||||
|
||||
template<typename> class expect;
|
||||
namespace detail {
|
||||
struct expect {
|
||||
//! \throw std::system_error with `ec`, optional `msg` and/or optional `file` + `line`.
|
||||
static void throw_(std::error_code ec, const char* msg, const char* file, unsigned line);
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct expect
|
||||
{
|
||||
//! \throw std::system_error with `ec`, optional `msg` and/or optional `file` + `line`.
|
||||
static void throw_(std::error_code ec, const char* msg, const char* file, unsigned line);
|
||||
//! If `result.has_error()` call `throw_`. Otherwise, \return `*result` by move.
|
||||
template <typename T>
|
||||
static T unwrap(::expect<T>&& result, const char* error_msg, const char* file, unsigned line) {
|
||||
if (!result)
|
||||
throw_(result.error(), error_msg, file, line);
|
||||
return std::move(*result);
|
||||
}
|
||||
|
||||
//! If `result.has_error()` call `throw_`. Otherwise, \return `*result` by move.
|
||||
template<typename T>
|
||||
static T unwrap(::expect<T>&& result, const char* error_msg, const char* file, unsigned line)
|
||||
{
|
||||
if (!result)
|
||||
throw_(result.error(), error_msg, file, line);
|
||||
return std::move(*result);
|
||||
}
|
||||
|
||||
//! If `result.has_error()` call `throw_`.
|
||||
static void unwrap(::expect<void>&& result, const char* error_msg, const char* file, unsigned line);
|
||||
};
|
||||
}
|
||||
//! If `result.has_error()` call `throw_`.
|
||||
static void unwrap(
|
||||
::expect<void>&& result, const char* error_msg, const char* file, unsigned line);
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/*!
|
||||
`expect<T>` is a value or error implementation, similar to Rust std::result
|
||||
|
@ -105,8 +99,8 @@ namespace detail
|
|||
types have a `operator==` defined between them (i.e.
|
||||
`assert(expect<int>{100} == expect<short>{100});`). Comparisons can also be
|
||||
done against `std::error_code` objects or error code enums directly (i.e.
|
||||
`assert(expect<int>{make_error_code(common_error::kInvalidArgument)} == error::kInvalidArgument)`).
|
||||
Comparison of default constructed `std::error_code` will always fail.
|
||||
`assert(expect<int>{make_error_code(common_error::kInvalidArgument)} ==
|
||||
error::kInvalidArgument)`). Comparison of default constructed `std::error_code` will always fail.
|
||||
"Generic" comparisons can be done with `std::error_condition` via the `matches`
|
||||
method only (i.e.
|
||||
`assert(expect<int>{make_error_code{common_error::kInvalidErrorCode}.matches(std::errc::invalid_argument))`),
|
||||
|
@ -124,16 +118,13 @@ namespace detail
|
|||
|
||||
\note See `src/common/error.h` for creating a custom error enum.
|
||||
*/
|
||||
template<typename T>
|
||||
class expect
|
||||
{
|
||||
template <typename T>
|
||||
class expect {
|
||||
static_assert(std::is_nothrow_destructible<T>(), "T must have a nothrow destructor");
|
||||
|
||||
template<typename U>
|
||||
static constexpr bool is_convertible() noexcept
|
||||
{
|
||||
return std::is_constructible<T, U>() &&
|
||||
std::is_convertible<U, T>();
|
||||
template <typename U>
|
||||
static constexpr bool is_convertible() noexcept {
|
||||
return std::is_constructible<T, U>() && std::is_convertible<U, T>();
|
||||
}
|
||||
|
||||
// MEMBERS
|
||||
|
@ -141,32 +132,28 @@ class expect
|
|||
std::aligned_storage_t<sizeof(T), alignof(T)> storage_;
|
||||
// MEMBERS
|
||||
|
||||
T& get() noexcept
|
||||
{
|
||||
T& get() noexcept {
|
||||
assert(has_value());
|
||||
return *reinterpret_cast<T*>(std::addressof(storage_));
|
||||
}
|
||||
|
||||
T const& get() const noexcept
|
||||
{
|
||||
T const& get() const noexcept {
|
||||
assert(has_value());
|
||||
return *reinterpret_cast<T const*>(std::addressof(storage_));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
void store(U&& value) noexcept(std::is_nothrow_constructible<T, U>())
|
||||
{
|
||||
template <typename U>
|
||||
void store(U&& value) noexcept(std::is_nothrow_constructible<T, U>()) {
|
||||
new (std::addressof(storage_)) T{std::forward<U>(value)};
|
||||
code_ = std::error_code{};
|
||||
}
|
||||
|
||||
void maybe_throw() const
|
||||
{
|
||||
void maybe_throw() const {
|
||||
if (has_error())
|
||||
::detail::expect::throw_(error(), nullptr, nullptr, 0);
|
||||
}
|
||||
|
||||
public:
|
||||
public:
|
||||
using value_type = T;
|
||||
using error_type = std::error_code;
|
||||
|
||||
|
@ -175,62 +162,52 @@ public:
|
|||
/*! Store an error, `code`, in the `expect` object. If `code` creates a
|
||||
`std::error_code` object whose `.value() == 0`, then `error()` will be set
|
||||
to `::common_error::kInvalidErrorCode`. */
|
||||
expect(std::error_code const& code) noexcept
|
||||
: code_(code), storage_()
|
||||
{
|
||||
expect(std::error_code const& code) noexcept : code_(code), storage_() {
|
||||
if (!has_error())
|
||||
code_ = ::common_error::kInvalidErrorCode;
|
||||
}
|
||||
|
||||
//! Store a value, `val`, in the `expect` object.
|
||||
expect(T val) noexcept(std::is_nothrow_move_constructible<T>())
|
||||
: code_(), storage_()
|
||||
{
|
||||
expect(T val) noexcept(std::is_nothrow_move_constructible<T>()) : code_(), storage_() {
|
||||
store(std::move(val));
|
||||
}
|
||||
|
||||
expect(expect const& src) noexcept(std::is_nothrow_copy_constructible<T>())
|
||||
: code_(src.error()), storage_()
|
||||
{
|
||||
expect(expect const& src) noexcept(std::is_nothrow_copy_constructible<T>()) :
|
||||
code_(src.error()), storage_() {
|
||||
if (src.has_value())
|
||||
store(src.get());
|
||||
}
|
||||
|
||||
//! Copy conversion from `U` to `T`.
|
||||
template<typename U, typename = std::enable_if_t<is_convertible<U const&>()>>
|
||||
expect(expect<U> const& src) noexcept(std::is_nothrow_constructible<T, U const&>())
|
||||
: code_(src.error()), storage_()
|
||||
{
|
||||
template <typename U, typename = std::enable_if_t<is_convertible<U const&>()>>
|
||||
expect(expect<U> const& src) noexcept(std::is_nothrow_constructible<T, U const&>()) :
|
||||
code_(src.error()), storage_() {
|
||||
if (src.has_value())
|
||||
store(*src);
|
||||
}
|
||||
|
||||
expect(expect&& src) noexcept(std::is_nothrow_move_constructible<T>())
|
||||
: code_(src.error()), storage_()
|
||||
{
|
||||
expect(expect&& src) noexcept(std::is_nothrow_move_constructible<T>()) :
|
||||
code_(src.error()), storage_() {
|
||||
if (src.has_value())
|
||||
store(std::move(src.get()));
|
||||
}
|
||||
|
||||
//! Move conversion from `U` to `T`.
|
||||
template<typename U, typename = std::enable_if_t<is_convertible<U>()>>
|
||||
expect(expect<U>&& src) noexcept(std::is_nothrow_constructible<T, U>())
|
||||
: code_(src.error()), storage_()
|
||||
{
|
||||
template <typename U, typename = std::enable_if_t<is_convertible<U>()>>
|
||||
expect(expect<U>&& src) noexcept(std::is_nothrow_constructible<T, U>()) :
|
||||
code_(src.error()), storage_() {
|
||||
if (src.has_value())
|
||||
store(std::move(*src));
|
||||
}
|
||||
|
||||
~expect() noexcept
|
||||
{
|
||||
~expect() noexcept {
|
||||
if (has_value())
|
||||
get().~T();
|
||||
}
|
||||
|
||||
expect& operator=(expect const& src) noexcept(std::is_nothrow_copy_constructible<T>() && std::is_nothrow_copy_assignable<T>())
|
||||
{
|
||||
if (this != std::addressof(src))
|
||||
{
|
||||
expect& operator=(expect const& src) noexcept(
|
||||
std::is_nothrow_copy_constructible<T>() && std::is_nothrow_copy_assignable<T>()) {
|
||||
if (this != std::addressof(src)) {
|
||||
if (has_value() && src.has_value())
|
||||
get() = src.get();
|
||||
else if (has_value())
|
||||
|
@ -244,10 +221,9 @@ public:
|
|||
|
||||
/*! Move `src` into `this`. If `src.has_value() && addressof(src) != this`
|
||||
then `src.value() will be in a "moved from state". */
|
||||
expect& operator=(expect&& src) noexcept(std::is_nothrow_move_constructible<T>() && std::is_nothrow_move_assignable<T>())
|
||||
{
|
||||
if (this != std::addressof(src))
|
||||
{
|
||||
expect& operator=(expect&& src) noexcept(
|
||||
std::is_nothrow_move_constructible<T>() && std::is_nothrow_move_assignable<T>()) {
|
||||
if (this != std::addressof(src)) {
|
||||
if (has_value() && src.has_value())
|
||||
get() = std::move(src.get());
|
||||
else if (has_value())
|
||||
|
@ -272,23 +248,20 @@ public:
|
|||
std::error_code error() const noexcept { return code_; }
|
||||
|
||||
//! \return Value if `has_value()` otherwise \throw `std::system_error{error()}`.
|
||||
T& value() &
|
||||
{
|
||||
T& value() & {
|
||||
maybe_throw();
|
||||
return get();
|
||||
}
|
||||
|
||||
//! \return Value if `has_value()` otherwise \throw `std::system_error{error()}`.
|
||||
T const& value() const &
|
||||
{
|
||||
T const& value() const& {
|
||||
maybe_throw();
|
||||
return get();
|
||||
}
|
||||
|
||||
/*! Same as other overloads, but expressions such as `foo(bar().value())`
|
||||
will automatically perform moves with no copies. */
|
||||
T&& value() &&
|
||||
{
|
||||
T&& value() && {
|
||||
maybe_throw();
|
||||
return std::move(get());
|
||||
}
|
||||
|
@ -302,57 +275,45 @@ public:
|
|||
//! \return Value, \pre `has_value()`.
|
||||
T const& operator*() const noexcept { return get(); }
|
||||
|
||||
/*!
|
||||
/*!
|
||||
\note This function is `noexcept` when `U == T` is `noexcept`.
|
||||
\return True if `has_value() == rhs.has_value()` and if values or errors are equal.
|
||||
*/
|
||||
template<typename U>
|
||||
bool equal(expect<U> const& rhs) const noexcept(noexcept(*std::declval<expect<T>>() == *rhs))
|
||||
{
|
||||
return has_value() && rhs.has_value() ?
|
||||
get() == *rhs : error() == rhs.error();
|
||||
template <typename U>
|
||||
bool equal(expect<U> const& rhs) const noexcept(noexcept(*std::declval<expect<T>>() == *rhs)) {
|
||||
return has_value() && rhs.has_value() ? get() == *rhs : error() == rhs.error();
|
||||
}
|
||||
|
||||
//! \return False if `has_value()`, otherwise `error() == rhs`.
|
||||
bool equal(std::error_code const& rhs) const noexcept
|
||||
{
|
||||
return has_error() && error() == rhs;
|
||||
}
|
||||
bool equal(std::error_code const& rhs) const noexcept { return has_error() && error() == rhs; }
|
||||
|
||||
/*!
|
||||
\note This function is `noexcept` when `U == T` is `noexcept`.
|
||||
\return False if `has_error()`, otherwise `value() == rhs`.
|
||||
*/
|
||||
template<typename U, typename = std::enable_if_t<!std::is_constructible_v<std::error_code, U>>>
|
||||
bool equal(U const& rhs) const noexcept(noexcept(*std::declval<expect<T>>() == rhs))
|
||||
{
|
||||
template <typename U, typename = std::enable_if_t<!std::is_constructible_v<std::error_code, U>>>
|
||||
bool equal(U const& rhs) const noexcept(noexcept(*std::declval<expect<T>>() == rhs)) {
|
||||
return has_value() && get() == rhs;
|
||||
}
|
||||
|
||||
//! \return False if `has_value()`, otherwise `error() == rhs`.
|
||||
bool matches(std::error_condition const& rhs) const noexcept
|
||||
{
|
||||
bool matches(std::error_condition const& rhs) const noexcept {
|
||||
return has_error() && error() == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class expect<void>
|
||||
{
|
||||
template <>
|
||||
class expect<void> {
|
||||
std::error_code code_;
|
||||
|
||||
public:
|
||||
public:
|
||||
using value_type = void;
|
||||
using error_type = std::error_code;
|
||||
|
||||
//! Create a successful object.
|
||||
expect() noexcept
|
||||
: code_()
|
||||
{}
|
||||
expect() noexcept : code_() {}
|
||||
|
||||
expect(std::error_code const& code) noexcept
|
||||
: code_(code)
|
||||
{
|
||||
expect(std::error_code const& code) noexcept : code_(code) {
|
||||
if (!has_error())
|
||||
code_ = ::common_error::kInvalidErrorCode;
|
||||
}
|
||||
|
@ -371,75 +332,58 @@ public:
|
|||
std::error_code error() const noexcept { return code_; }
|
||||
|
||||
//! \return `error() == rhs.error()`.
|
||||
bool equal(expect const& rhs) const noexcept
|
||||
{
|
||||
return error() == rhs.error();
|
||||
}
|
||||
bool equal(expect const& rhs) const noexcept { return error() == rhs.error(); }
|
||||
|
||||
//! \return `has_error() && error() == rhs`.
|
||||
bool equal(std::error_code const& rhs) const noexcept
|
||||
{
|
||||
return has_error() && error() == rhs;
|
||||
}
|
||||
bool equal(std::error_code const& rhs) const noexcept { return has_error() && error() == rhs; }
|
||||
|
||||
//! \return False if `has_value()`, otherwise `error() == rhs`.
|
||||
bool matches(std::error_condition const& rhs) const noexcept
|
||||
{
|
||||
bool matches(std::error_condition const& rhs) const noexcept {
|
||||
return has_error() && error() == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
//! \return An `expect<void>` object with `!has_error()`.
|
||||
inline expect<void> success() noexcept { return expect<void>{}; }
|
||||
inline expect<void> success() noexcept {
|
||||
return expect<void>{};
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline
|
||||
bool operator==(expect<T> const& lhs, expect<U> const& rhs) noexcept(noexcept(lhs.equal(rhs)))
|
||||
{
|
||||
template <typename T, typename U>
|
||||
inline bool operator==(expect<T> const& lhs, expect<U> const& rhs) noexcept(
|
||||
noexcept(lhs.equal(rhs))) {
|
||||
return lhs.equal(rhs);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline
|
||||
bool operator==(expect<T> const& lhs, U const& rhs) noexcept(noexcept(lhs.equal(rhs)))
|
||||
{
|
||||
template <typename T, typename U>
|
||||
inline bool operator==(expect<T> const& lhs, U const& rhs) noexcept(noexcept(lhs.equal(rhs))) {
|
||||
return lhs.equal(rhs);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline
|
||||
bool operator==(T const& lhs, expect<U> const& rhs) noexcept(noexcept(rhs.equal(lhs)))
|
||||
{
|
||||
template <typename T, typename U>
|
||||
inline bool operator==(T const& lhs, expect<U> const& rhs) noexcept(noexcept(rhs.equal(lhs))) {
|
||||
return rhs.equal(lhs);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline
|
||||
bool operator!=(expect<T> const& lhs, expect<U> const& rhs) noexcept(noexcept(lhs.equal(rhs)))
|
||||
{
|
||||
template <typename T, typename U>
|
||||
inline bool operator!=(expect<T> const& lhs, expect<U> const& rhs) noexcept(
|
||||
noexcept(lhs.equal(rhs))) {
|
||||
return !lhs.equal(rhs);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline
|
||||
bool operator!=(expect<T> const& lhs, U const& rhs) noexcept(noexcept(lhs.equal(rhs)))
|
||||
{
|
||||
template <typename T, typename U>
|
||||
inline bool operator!=(expect<T> const& lhs, U const& rhs) noexcept(noexcept(lhs.equal(rhs))) {
|
||||
return !lhs.equal(rhs);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline
|
||||
bool operator!=(T const& lhs, expect<U> const& rhs) noexcept(noexcept(rhs.equal(lhs)))
|
||||
{
|
||||
template <typename T, typename U>
|
||||
inline bool operator!=(T const& lhs, expect<U> const& rhs) noexcept(noexcept(rhs.equal(lhs))) {
|
||||
return !rhs.equal(lhs);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
inline void expect::unwrap(::expect<void>&& result, const char* error_msg, const char* file, unsigned line)
|
||||
{
|
||||
if (!result)
|
||||
throw_(result.error(), error_msg, file, line);
|
||||
}
|
||||
namespace detail {
|
||||
inline void expect::unwrap(
|
||||
::expect<void>&& result, const char* error_msg, const char* file, unsigned line) {
|
||||
if (!result)
|
||||
throw_(result.error(), error_msg, file, line);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright (c) 2018-2020, The Loki Project
|
||||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -26,48 +26,51 @@
|
|||
// 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.
|
||||
//
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
//
|
||||
#include "file.h"
|
||||
#include "fs-format.h"
|
||||
#include "logging/oxen_logger.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
|
||||
#include "fs-format.h"
|
||||
#include "logging/oxen_logger.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include "epee/string_tools.h"
|
||||
#ifndef STRSAFE_NO_DEPRECATE
|
||||
#define STRSAFE_NO_DEPRECATE
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <strsafe.h>
|
||||
#else
|
||||
#include <sys/file.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/stat.h>
|
||||
#include <shlobj.h>
|
||||
#include <strsafe.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
#ifdef __GLIBC__
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <cstring>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
#include "cryptonote_config.h"
|
||||
|
||||
namespace tools {
|
||||
|
||||
static auto logcat = log::Cat("util");
|
||||
static auto logcat = log::Cat("util");
|
||||
|
||||
#ifndef _WIN32
|
||||
static int flock_exnb(int fd)
|
||||
{
|
||||
static int flock_exnb(int fd) {
|
||||
struct flock fl;
|
||||
int ret;
|
||||
|
||||
|
@ -78,253 +81,236 @@ namespace tools {
|
|||
fl.l_len = 0;
|
||||
ret = fcntl(fd, F_SETLK, &fl);
|
||||
if (ret < 0)
|
||||
log::error(logcat, "Error locking fd {}: {} ({})", fd, errno, strerror(errno));
|
||||
log::error(logcat, "Error locking fd {}: {} ({})", fd, errno, strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private_file::private_file() noexcept : m_handle(), m_filename() {}
|
||||
private_file::private_file() noexcept : m_handle(), m_filename() {}
|
||||
|
||||
private_file::private_file(std::FILE* handle, fs::path filename) noexcept
|
||||
: m_handle(handle), m_filename(std::move(filename)) {}
|
||||
private_file::private_file(std::FILE* handle, fs::path filename) noexcept :
|
||||
m_handle(handle), m_filename(std::move(filename)) {}
|
||||
|
||||
private_file private_file::create(fs::path name)
|
||||
{
|
||||
private_file private_file::create(fs::path name) {
|
||||
#ifdef WIN32
|
||||
struct close_handle
|
||||
{
|
||||
void operator()(HANDLE handle) const noexcept
|
||||
{
|
||||
CloseHandle(handle);
|
||||
}
|
||||
struct close_handle {
|
||||
void operator()(HANDLE handle) const noexcept { CloseHandle(handle); }
|
||||
};
|
||||
|
||||
std::unique_ptr<void, close_handle> process = nullptr;
|
||||
{
|
||||
HANDLE temp{};
|
||||
const bool fail = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, std::addressof(temp)) == 0;
|
||||
process.reset(temp);
|
||||
if (fail)
|
||||
return {};
|
||||
HANDLE temp{};
|
||||
const bool fail =
|
||||
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, std::addressof(temp)) == 0;
|
||||
process.reset(temp);
|
||||
if (fail)
|
||||
return {};
|
||||
}
|
||||
|
||||
DWORD sid_size = 0;
|
||||
GetTokenInformation(process.get(), TokenOwner, nullptr, 0, std::addressof(sid_size));
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
return {};
|
||||
return {};
|
||||
|
||||
std::unique_ptr<char[]> sid{new char[sid_size]};
|
||||
if (!GetTokenInformation(process.get(), TokenOwner, sid.get(), sid_size, std::addressof(sid_size)))
|
||||
return {};
|
||||
if (!GetTokenInformation(
|
||||
process.get(), TokenOwner, sid.get(), sid_size, std::addressof(sid_size)))
|
||||
return {};
|
||||
|
||||
const PSID psid = reinterpret_cast<const PTOKEN_OWNER>(sid.get())->Owner;
|
||||
const DWORD daclSize =
|
||||
sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - sizeof(DWORD);
|
||||
sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - sizeof(DWORD);
|
||||
|
||||
const std::unique_ptr<char[]> dacl{new char[daclSize]};
|
||||
if (!InitializeAcl(reinterpret_cast<PACL>(dacl.get()), daclSize, ACL_REVISION))
|
||||
return {};
|
||||
return {};
|
||||
|
||||
if (!AddAccessAllowedAce(reinterpret_cast<PACL>(dacl.get()), ACL_REVISION, (READ_CONTROL | FILE_GENERIC_READ | DELETE), psid))
|
||||
return {};
|
||||
if (!AddAccessAllowedAce(
|
||||
reinterpret_cast<PACL>(dacl.get()),
|
||||
ACL_REVISION,
|
||||
(READ_CONTROL | FILE_GENERIC_READ | DELETE),
|
||||
psid))
|
||||
return {};
|
||||
|
||||
SECURITY_DESCRIPTOR descriptor{};
|
||||
if (!InitializeSecurityDescriptor(std::addressof(descriptor), SECURITY_DESCRIPTOR_REVISION))
|
||||
return {};
|
||||
return {};
|
||||
|
||||
if (!SetSecurityDescriptorDacl(std::addressof(descriptor), true, reinterpret_cast<PACL>(dacl.get()), false))
|
||||
return {};
|
||||
if (!SetSecurityDescriptorDacl(
|
||||
std::addressof(descriptor), true, reinterpret_cast<PACL>(dacl.get()), false))
|
||||
return {};
|
||||
|
||||
SECURITY_ATTRIBUTES attributes{sizeof(SECURITY_ATTRIBUTES), std::addressof(descriptor), false};
|
||||
std::unique_ptr<void, close_handle> file{
|
||||
CreateFileW(
|
||||
name.c_str(),
|
||||
GENERIC_WRITE, FILE_SHARE_READ,
|
||||
std::addressof(attributes),
|
||||
CREATE_NEW, (FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE),
|
||||
nullptr
|
||||
)
|
||||
};
|
||||
if (file)
|
||||
{
|
||||
const int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file.get()), 0);
|
||||
if (0 <= fd)
|
||||
{
|
||||
file.release();
|
||||
std::FILE* real_file = _fdopen(fd, "w");
|
||||
if (!real_file)
|
||||
{
|
||||
_close(fd);
|
||||
std::unique_ptr<void, close_handle> file{CreateFileW(
|
||||
name.c_str(),
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_READ,
|
||||
std::addressof(attributes),
|
||||
CREATE_NEW,
|
||||
(FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE),
|
||||
nullptr)};
|
||||
if (file) {
|
||||
const int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file.get()), 0);
|
||||
if (0 <= fd) {
|
||||
file.release();
|
||||
std::FILE* real_file = _fdopen(fd, "w");
|
||||
if (!real_file) {
|
||||
_close(fd);
|
||||
}
|
||||
return {real_file, std::move(name)};
|
||||
}
|
||||
return {real_file, std::move(name)};
|
||||
}
|
||||
}
|
||||
#else
|
||||
const int fdr = open(name.c_str(), (O_RDONLY | O_CREAT), S_IRUSR);
|
||||
if (0 <= fdr)
|
||||
{
|
||||
struct stat rstats = {};
|
||||
if (fstat(fdr, std::addressof(rstats)) != 0)
|
||||
{
|
||||
close(fdr);
|
||||
return {};
|
||||
}
|
||||
fchmod(fdr, (S_IRUSR | S_IWUSR));
|
||||
const int fdw = open(name.c_str(), O_RDWR);
|
||||
fchmod(fdr, rstats.st_mode);
|
||||
close(fdr);
|
||||
|
||||
if (0 <= fdw)
|
||||
{
|
||||
struct stat wstats = {};
|
||||
if (fstat(fdw, std::addressof(wstats)) == 0 &&
|
||||
rstats.st_dev == wstats.st_dev && rstats.st_ino == wstats.st_ino &&
|
||||
flock_exnb(fdw) == 0 && ftruncate(fdw, 0) == 0)
|
||||
{
|
||||
std::FILE* file = fdopen(fdw, "w");
|
||||
if (file) return {file, std::move(name)};
|
||||
if (0 <= fdr) {
|
||||
struct stat rstats = {};
|
||||
if (fstat(fdr, std::addressof(rstats)) != 0) {
|
||||
close(fdr);
|
||||
return {};
|
||||
}
|
||||
fchmod(fdr, (S_IRUSR | S_IWUSR));
|
||||
const int fdw = open(name.c_str(), O_RDWR);
|
||||
fchmod(fdr, rstats.st_mode);
|
||||
close(fdr);
|
||||
|
||||
if (0 <= fdw) {
|
||||
struct stat wstats = {};
|
||||
if (fstat(fdw, std::addressof(wstats)) == 0 && rstats.st_dev == wstats.st_dev &&
|
||||
rstats.st_ino == wstats.st_ino && flock_exnb(fdw) == 0 && ftruncate(fdw, 0) == 0) {
|
||||
std::FILE* file = fdopen(fdw, "w");
|
||||
if (file)
|
||||
return {file, std::move(name)};
|
||||
}
|
||||
close(fdw);
|
||||
}
|
||||
close(fdw);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
private_file::~private_file() noexcept
|
||||
{
|
||||
private_file::~private_file() noexcept {
|
||||
std::error_code ignored;
|
||||
fs::remove(filename(), ignored);
|
||||
}
|
||||
}
|
||||
|
||||
file_locker::file_locker(const fs::path& filename)
|
||||
{
|
||||
file_locker::file_locker(const fs::path& filename) {
|
||||
#ifdef WIN32
|
||||
m_fd = INVALID_HANDLE_VALUE;
|
||||
m_fd = CreateFileW(filename.c_str(), GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (m_fd != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
OVERLAPPED ov;
|
||||
memset(&ov, 0, sizeof(ov));
|
||||
if (!LockFileEx(m_fd, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &ov))
|
||||
{
|
||||
log::error(logcat, "Failed to lock {}: {}", filename, std::error_code(GetLastError(), std::system_category()).message());
|
||||
CloseHandle(m_fd);
|
||||
m_fd = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log::error(logcat, "Failed to open {}: {}", filename, std::error_code(GetLastError(), std::system_category()).message());
|
||||
m_fd = CreateFileW(
|
||||
filename.c_str(), GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (m_fd != INVALID_HANDLE_VALUE) {
|
||||
OVERLAPPED ov;
|
||||
memset(&ov, 0, sizeof(ov));
|
||||
if (!LockFileEx(m_fd, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &ov)) {
|
||||
log::error(
|
||||
logcat,
|
||||
"Failed to lock {}: {}",
|
||||
filename,
|
||||
std::error_code(GetLastError(), std::system_category()).message());
|
||||
CloseHandle(m_fd);
|
||||
m_fd = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
} else {
|
||||
log::error(
|
||||
logcat,
|
||||
"Failed to open {}: {}",
|
||||
filename,
|
||||
std::error_code(GetLastError(), std::system_category()).message());
|
||||
}
|
||||
#else
|
||||
m_fd = open(filename.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0666);
|
||||
if (m_fd != -1)
|
||||
{
|
||||
if (flock_exnb(m_fd) == -1)
|
||||
{
|
||||
log::error(logcat, "Failed to lock {}: {}", filename, std::strerror(errno));
|
||||
close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log::error(logcat, "Failed to open {}: {}", filename, std::strerror(errno));
|
||||
if (m_fd != -1) {
|
||||
if (flock_exnb(m_fd) == -1) {
|
||||
log::error(logcat, "Failed to lock {}: {}", filename, std::strerror(errno));
|
||||
close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
} else {
|
||||
log::error(logcat, "Failed to open {}: {}", filename, std::strerror(errno));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
file_locker::~file_locker()
|
||||
{
|
||||
if (locked())
|
||||
{
|
||||
}
|
||||
file_locker::~file_locker() {
|
||||
if (locked()) {
|
||||
#ifdef WIN32
|
||||
CloseHandle(m_fd);
|
||||
CloseHandle(m_fd);
|
||||
#else
|
||||
close(m_fd);
|
||||
close(m_fd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
bool file_locker::locked() const
|
||||
{
|
||||
}
|
||||
bool file_locker::locked() const {
|
||||
#ifdef WIN32
|
||||
return m_fd != INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
return m_fd != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
fs::path get_special_folder_path(int nfolder, bool iscreate)
|
||||
{
|
||||
fs::path get_special_folder_path(int nfolder, bool iscreate) {
|
||||
WCHAR psz_path[MAX_PATH] = L"";
|
||||
|
||||
if (SHGetSpecialFolderPathW(NULL, psz_path, nfolder, iscreate))
|
||||
{
|
||||
return fs::path{psz_path};
|
||||
if (SHGetSpecialFolderPathW(NULL, psz_path, nfolder, iscreate)) {
|
||||
return fs::path{psz_path};
|
||||
}
|
||||
|
||||
log::error(logcat, "SHGetSpecialFolderPathW() failed, could not obtain requested path.");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Windows < Vista: C:\Documents and Settings\Username\Application Data\...
|
||||
// Windows >= Vista: C:\Users\Username\AppData\Roaming\...
|
||||
// Sane OSes: ~/
|
||||
static fs::path get_default_parent_dir() {
|
||||
// Windows < Vista: C:\Documents and Settings\Username\Application Data\...
|
||||
// Windows >= Vista: C:\Users\Username\AppData\Roaming\...
|
||||
// Sane OSes: ~/
|
||||
static fs::path get_default_parent_dir() {
|
||||
#ifdef _WIN32
|
||||
return get_special_folder_path(CSIDL_COMMON_APPDATA, true);
|
||||
#else
|
||||
char* home = std::getenv("HOME");
|
||||
return home && std::strlen(home) ? fs::u8path(home) : fs::current_path();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
fs::path get_default_data_dir()
|
||||
{
|
||||
fs::path get_default_data_dir() {
|
||||
return get_default_parent_dir() / fs::u8path(cryptonote::DATA_DIRNAME);
|
||||
}
|
||||
fs::path get_depreciated_default_data_dir()
|
||||
{
|
||||
}
|
||||
fs::path get_depreciated_default_data_dir() {
|
||||
return get_default_parent_dir() / fs::u8path(cryptonote::old::DATA_DIRNAME);
|
||||
}
|
||||
}
|
||||
|
||||
void set_strict_default_file_permissions(bool strict)
|
||||
{
|
||||
void set_strict_default_file_permissions(bool strict) {
|
||||
#if defined(__MINGW32__) || defined(__MINGW__)
|
||||
// no clue about the odd one out
|
||||
#else
|
||||
mode_t mode = strict ? 077 : 0;
|
||||
umask(mode);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool slurp_file(const fs::path& filename, std::string& contents)
|
||||
{
|
||||
bool slurp_file(const fs::path& filename, std::string& contents) {
|
||||
|
||||
try {
|
||||
fs::ifstream in(filename, std::ios::binary);
|
||||
std::string content((std::istreambuf_iterator<char>(in)), (std::istreambuf_iterator<char>()));
|
||||
contents = std::move(content);
|
||||
return true;
|
||||
fs::ifstream in(filename, std::ios::binary);
|
||||
std::string content(
|
||||
(std::istreambuf_iterator<char>(in)), (std::istreambuf_iterator<char>()));
|
||||
contents = std::move(content);
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool dump_file(const fs::path& filename, std::string_view contents)
|
||||
{
|
||||
bool dump_file(const fs::path& filename, std::string_view contents) {
|
||||
fs::ofstream out;
|
||||
out.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
try {
|
||||
out.open(filename, std::ios::binary | std::ios::out | std::ios::trunc);
|
||||
out.write(contents.data(), contents.size());
|
||||
return true;
|
||||
out.open(filename, std::ios::binary | std::ios::out | std::ios::trunc);
|
||||
out.write(contents.data(), contents.size());
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright (c) 2018-2020, The Loki Project
|
||||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -26,49 +26,46 @@
|
|||
// 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.
|
||||
//
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "windows.h"
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Various utilities for dealing with files/directories.
|
||||
*/
|
||||
|
||||
namespace tools {
|
||||
|
||||
//! Functional class for closing C file handles.
|
||||
struct close_file
|
||||
{
|
||||
void operator()(std::FILE* handle) const noexcept
|
||||
{
|
||||
if (handle)
|
||||
{
|
||||
std::fclose(handle);
|
||||
}
|
||||
//! Functional class for closing C file handles.
|
||||
struct close_file {
|
||||
void operator()(std::FILE* handle) const noexcept {
|
||||
if (handle) {
|
||||
std::fclose(handle);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
//! A file restricted to process owner AND process. Deletes file on destruction.
|
||||
class private_file {
|
||||
//! A file restricted to process owner AND process. Deletes file on destruction.
|
||||
class private_file {
|
||||
std::unique_ptr<std::FILE, close_file> m_handle;
|
||||
fs::path m_filename;
|
||||
|
||||
private_file(std::FILE* handle, fs::path filename) noexcept;
|
||||
public:
|
||||
|
||||
public:
|
||||
//! `handle() == nullptr && filename.empty()`.
|
||||
private_file() noexcept;
|
||||
|
||||
|
@ -84,55 +81,55 @@ namespace tools {
|
|||
|
||||
std::FILE* handle() const noexcept { return m_handle.get(); }
|
||||
const fs::path& filename() const noexcept { return m_filename; }
|
||||
};
|
||||
};
|
||||
|
||||
class file_locker
|
||||
{
|
||||
class file_locker {
|
||||
public:
|
||||
file_locker(const fs::path& filename);
|
||||
~file_locker();
|
||||
bool locked() const;
|
||||
|
||||
private:
|
||||
#ifdef WIN32
|
||||
HANDLE m_fd;
|
||||
#else
|
||||
int m_fd;
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
/*! \brief Returns the default data directory.
|
||||
*
|
||||
* \details Windows < Vista: C:\\Documents and Settings\\Username\\Application Data\\CRYPTONOTE_NAME
|
||||
*
|
||||
* Windows >= Vista: C:\\Users\\Username\\AppData\\Roaming\\CRYPTONOTE_NAME
|
||||
*
|
||||
* Mac: ~/Library/Application Support/CRYPTONOTE_NAME
|
||||
*
|
||||
* Unix: ~/.CRYPTONOTE_NAME
|
||||
*/
|
||||
fs::path get_default_data_dir();
|
||||
fs::path get_depreciated_default_data_dir();
|
||||
/*! \brief Returns the default data directory.
|
||||
*
|
||||
* \details Windows < Vista: C:\\Documents and Settings\\Username\\Application Data\\CRYPTONOTE_NAME
|
||||
*
|
||||
* Windows >= Vista: C:\\Users\\Username\\AppData\\Roaming\\CRYPTONOTE_NAME
|
||||
*
|
||||
* Mac: ~/Library/Application Support/CRYPTONOTE_NAME
|
||||
*
|
||||
* Unix: ~/.CRYPTONOTE_NAME
|
||||
*/
|
||||
fs::path get_default_data_dir();
|
||||
fs::path get_depreciated_default_data_dir();
|
||||
|
||||
#ifdef WIN32
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param nfolder
|
||||
* @param iscreate
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
fs::path get_special_folder_path(int nfolder, bool iscreate);
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param nfolder
|
||||
* @param iscreate
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
fs::path get_special_folder_path(int nfolder, bool iscreate);
|
||||
#endif
|
||||
|
||||
void set_strict_default_file_permissions(bool strict);
|
||||
void set_strict_default_file_permissions(bool strict);
|
||||
|
||||
void closefrom(int fd);
|
||||
void closefrom(int fd);
|
||||
|
||||
/// Reads a (binary) file from disk into the string `contents`.
|
||||
bool slurp_file(const fs::path& filename, std::string& contents);
|
||||
/// Reads a (binary) file from disk into the string `contents`.
|
||||
bool slurp_file(const fs::path& filename, std::string& contents);
|
||||
|
||||
/// Dumps (binary) string contents to disk. The file is overwritten if it already exists.
|
||||
bool dump_file(const fs::path& filename, std::string_view contents);
|
||||
/// Dumps (binary) string contents to disk. The file is overwritten if it already exists.
|
||||
bool dump_file(const fs::path& filename, std::string_view contents);
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,117 +1,117 @@
|
|||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace formattable {
|
||||
|
||||
// Types can opt-in to being formattable as a string by specializing this to true. Such a type
|
||||
// must have one of:
|
||||
//
|
||||
// - a `to_string()` method on the type; when formatted we will call `val.to_string()` to format
|
||||
// it as a string.
|
||||
// - a `to_string(val)` function in the same namespace as the type; we will call it to format it
|
||||
// as a string.
|
||||
//
|
||||
// The function should return something string-like (string, string_view, const char*).
|
||||
//
|
||||
// For instance to opt-in MyType for such string formatting, use:
|
||||
//
|
||||
// template <> inline constexpr bool formattable::via_to_string<MyType> = true;
|
||||
//
|
||||
// You can also partially specialize; for instance to make all derived classes of a common base
|
||||
// type formattable via to_string you could do:
|
||||
//
|
||||
// template <T> inline constexpr bool formattable::via_to_string<T,
|
||||
// std::enable_if_t<std::is_base_of_v<MyBaseType, T>>> = true;
|
||||
//
|
||||
// Types can opt-in to being formattable as a string by specializing this to true. Such a type
|
||||
// must have one of:
|
||||
//
|
||||
// - a `to_string()` method on the type; when formatted we will call `val.to_string()` to format
|
||||
// it as a string.
|
||||
// - a `to_string(val)` function in the same namespace as the type; we will call it to format it
|
||||
// as a string.
|
||||
//
|
||||
// The function should return something string-like (string, string_view, const char*).
|
||||
//
|
||||
// For instance to opt-in MyType for such string formatting, use:
|
||||
//
|
||||
// template <> inline constexpr bool formattable::via_to_string<MyType> = true;
|
||||
//
|
||||
// You can also partially specialize; for instance to make all derived classes of a common base
|
||||
// type formattable via to_string you could do:
|
||||
//
|
||||
// template <T> inline constexpr bool formattable::via_to_string<T,
|
||||
// std::enable_if_t<std::is_base_of_v<MyBaseType, T>>> = true;
|
||||
//
|
||||
template <typename T, typename SFINAE = void>
|
||||
constexpr bool via_to_string = false;
|
||||
|
||||
// Same as above, but looks for a to_hex_string() instead of to_string(), for types that get
|
||||
// dumped as hex.
|
||||
template <typename T, typename SFINAE = void>
|
||||
constexpr bool via_to_hex_string = false;
|
||||
|
||||
// Scoped enums can alternatively be formatted as their underlying integer value by specializing
|
||||
// this function to true:
|
||||
template <typename T, typename SFINAE = void>
|
||||
constexpr bool via_underlying = false;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T, typename SFINAE = void>
|
||||
constexpr bool via_to_string = false;
|
||||
|
||||
// Same as above, but looks for a to_hex_string() instead of to_string(), for types that get
|
||||
// dumped as hex.
|
||||
constexpr bool has_to_string_method = false;
|
||||
template <typename T, typename SFINAE = void>
|
||||
constexpr bool via_to_hex_string = false;
|
||||
|
||||
// Scoped enums can alternatively be formatted as their underlying integer value by specializing
|
||||
// this function to true:
|
||||
template <typename T, typename SFINAE = void>
|
||||
constexpr bool via_underlying = false;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T, typename SFINAE = void>
|
||||
constexpr bool has_to_string_method = false;
|
||||
template <typename T, typename SFINAE = void>
|
||||
constexpr bool has_to_hex_string_method = false;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool has_to_string_method<T,
|
||||
std::void_t<decltype(std::declval<const T&>().to_string())>
|
||||
> = true;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool has_to_hex_string_method<T,
|
||||
std::void_t<decltype(std::declval<const T&>().to_hex_string())>
|
||||
> = true;
|
||||
|
||||
} // namespace detail
|
||||
constexpr bool has_to_hex_string_method = false;
|
||||
|
||||
template <typename T>
|
||||
struct to_string_formatter : fmt::formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const T& val, FormatContext& ctx) const {
|
||||
if constexpr (::formattable::detail::has_to_string_method<T>)
|
||||
return formatter<std::string_view>::format(val.to_string(), ctx);
|
||||
else
|
||||
return formatter<std::string_view>::format(to_string(val), ctx);
|
||||
}
|
||||
};
|
||||
inline constexpr bool
|
||||
has_to_string_method<T, std::void_t<decltype(std::declval<const T&>().to_string())>> =
|
||||
true;
|
||||
|
||||
template <typename T>
|
||||
struct to_hex_string_formatter : fmt::formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const T& val, FormatContext& ctx) const {
|
||||
if constexpr (::formattable::detail::has_to_hex_string_method<T>)
|
||||
return formatter<std::string_view>::format(val.to_hex_string(), ctx);
|
||||
else
|
||||
return formatter<std::string_view>::format(to_hex_string(val), ctx);
|
||||
}
|
||||
};
|
||||
inline constexpr bool has_to_hex_string_method<
|
||||
T,
|
||||
std::void_t<decltype(std::declval<const T&>().to_hex_string())>> = true;
|
||||
|
||||
template <typename T>
|
||||
struct underlying_t_formatter : fmt::formatter<std::underlying_type_t<T>> {
|
||||
#ifdef __cpp_lib_is_scoped_enum // C++23
|
||||
static_assert(std::is_scoped_enum_v<T>);
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
struct to_string_formatter : fmt::formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const T& val, FormatContext& ctx) const {
|
||||
if constexpr (::formattable::detail::has_to_string_method<T>)
|
||||
return formatter<std::string_view>::format(val.to_string(), ctx);
|
||||
else
|
||||
return formatter<std::string_view>::format(to_string(val), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct to_hex_string_formatter : fmt::formatter<std::string_view> {
|
||||
template <typename FormatContext>
|
||||
auto format(const T& val, FormatContext& ctx) const {
|
||||
if constexpr (::formattable::detail::has_to_hex_string_method<T>)
|
||||
return formatter<std::string_view>::format(val.to_hex_string(), ctx);
|
||||
else
|
||||
return formatter<std::string_view>::format(to_hex_string(val), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct underlying_t_formatter : fmt::formatter<std::underlying_type_t<T>> {
|
||||
#ifdef __cpp_lib_is_scoped_enum // C++23
|
||||
static_assert(std::is_scoped_enum_v<T>);
|
||||
#else
|
||||
static_assert(std::is_enum_v<T> && !std::is_convertible_v<T, std::underlying_type_t<T>>,
|
||||
"formattable::via_underlying<T> type is not a scoped enum");
|
||||
static_assert(
|
||||
std::is_enum_v<T> && !std::is_convertible_v<T, std::underlying_type_t<T>>,
|
||||
"formattable::via_underlying<T> type is not a scoped enum");
|
||||
#endif
|
||||
template <typename FormatContext>
|
||||
auto format(const T& val, FormatContext& ctx) const {
|
||||
using Underlying = std::underlying_type_t<T>;
|
||||
return fmt::formatter<Underlying>::format(static_cast<Underlying>(val), ctx);
|
||||
}
|
||||
};
|
||||
template <typename FormatContext>
|
||||
auto format(const T& val, FormatContext& ctx) const {
|
||||
using Underlying = std::underlying_type_t<T>;
|
||||
return fmt::formatter<Underlying>::format(static_cast<Underlying>(val), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace formattable
|
||||
|
||||
|
||||
|
||||
namespace fmt {
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<T, Char, std::enable_if_t<::formattable::via_to_string<T>>>
|
||||
template <typename T, typename Char>
|
||||
struct formatter<T, Char, std::enable_if_t<::formattable::via_to_string<T>>>
|
||||
: ::formattable::to_string_formatter<T> {};
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<T, Char, std::enable_if_t<::formattable::via_to_hex_string<T>>>
|
||||
template <typename T, typename Char>
|
||||
struct formatter<T, Char, std::enable_if_t<::formattable::via_to_hex_string<T>>>
|
||||
: ::formattable::to_hex_string_formatter<T> {};
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<T, Char, std::enable_if_t<::formattable::via_underlying<T>>>
|
||||
template <typename T, typename Char>
|
||||
struct formatter<T, Char, std::enable_if_t<::formattable::via_underlying<T>>>
|
||||
: ::formattable::underlying_t_formatter<T> {};
|
||||
|
||||
} // namespace fmt
|
||||
|
|
|
@ -8,13 +8,12 @@
|
|||
#include <fmt/core.h>
|
||||
|
||||
namespace fmt {
|
||||
template <>
|
||||
struct formatter<ghc::filesystem::path> : formatter<std::string>
|
||||
{
|
||||
template <typename FormatContext>
|
||||
auto format(const ghc::filesystem::path& val, FormatContext& ctx) const {
|
||||
return formatter<std::string>::format(val.u8string(), ctx);
|
||||
}
|
||||
};
|
||||
}
|
||||
template <>
|
||||
struct formatter<ghc::filesystem::path> : formatter<std::string> {
|
||||
template <typename FormatContext>
|
||||
auto format(const ghc::filesystem::path& val, FormatContext& ctx) const {
|
||||
return formatter<std::string>::format(val.u8string(), ctx);
|
||||
}
|
||||
};
|
||||
} // namespace fmt
|
||||
#endif
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
|
||||
#include <filesystem>
|
||||
namespace fs {
|
||||
using namespace std::filesystem;
|
||||
using ifstream = std::ifstream;
|
||||
using ofstream = std::ofstream;
|
||||
using fstream = std::fstream;
|
||||
}
|
||||
using namespace std::filesystem;
|
||||
using ifstream = std::ifstream;
|
||||
using ofstream = std::ofstream;
|
||||
using fstream = std::fstream;
|
||||
} // namespace fs
|
||||
#else
|
||||
|
||||
#include <ghc/filesystem.hpp>
|
||||
|
|
|
@ -1,28 +1,33 @@
|
|||
#pragma once
|
||||
#include <oxenc/hex.h>
|
||||
|
||||
#include <type_traits>
|
||||
#include "epee/span.h" // epee
|
||||
|
||||
#include "epee/span.h" // epee
|
||||
|
||||
namespace tools {
|
||||
// Reads a hex string directly into a trivially copyable type T without performing any temporary
|
||||
// allocation. Returns false if the given string is not hex or does not match T in length,
|
||||
// otherwise copies directly into `x` and returns true.
|
||||
template <typename T, typename = std::enable_if_t<
|
||||
!std::is_const_v<T> && (std::is_trivially_copyable_v<T> || epee::is_byte_spannable<T>)
|
||||
>>
|
||||
bool hex_to_type(std::string_view hex, T& x) {
|
||||
if (!oxenc::is_hex(hex) || hex.size() != 2*sizeof(T))
|
||||
return false;
|
||||
// Reads a hex string directly into a trivially copyable type T without performing any temporary
|
||||
// allocation. Returns false if the given string is not hex or does not match T in length,
|
||||
// otherwise copies directly into `x` and returns true.
|
||||
template <
|
||||
typename T,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_const_v<T> &&
|
||||
(std::is_trivially_copyable_v<T> || epee::is_byte_spannable<T>)>>
|
||||
bool hex_to_type(std::string_view hex, T& x) {
|
||||
if (!oxenc::is_hex(hex) || hex.size() != 2 * sizeof(T))
|
||||
return false;
|
||||
oxenc::from_hex(hex.begin(), hex.end(), reinterpret_cast<char*>(&x));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Converts a standard layout, padding-free type into a hex string of its contents.
|
||||
template <typename T, typename = std::enable_if_t<
|
||||
(std::is_standard_layout_v<T> && std::has_unique_object_representations_v<T>)
|
||||
|| epee::is_byte_spannable<T>
|
||||
>>
|
||||
std::string type_to_hex(const T& val) {
|
||||
return oxenc::to_hex(std::string_view{reinterpret_cast<const char*>(&val), sizeof(val)});
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a standard layout, padding-free type into a hex string of its contents.
|
||||
template <
|
||||
typename T,
|
||||
typename = std::enable_if_t<
|
||||
(std::is_standard_layout_v<T> && std::has_unique_object_representations_v<T>) ||
|
||||
epee::is_byte_spannable<T>>>
|
||||
std::string type_to_hex(const T& val) {
|
||||
return oxenc::to_hex(std::string_view{reinterpret_cast<const char*>(&val), sizeof(val)});
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright (c) 2014-2019, The Monero Project
|
||||
// Copyright (c) 2018, The Loki Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -28,303 +28,310 @@
|
|||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "i18n.h"
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <logging/oxen_logger.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
#include "file.h"
|
||||
#include <logging/oxen_logger.h>
|
||||
|
||||
static auto logcat = oxen::log::Cat("i18n");
|
||||
|
||||
#define MAX_LANGUAGE_SIZE 16
|
||||
|
||||
static const unsigned char qm_magic[16] = {0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95, 0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd};
|
||||
static const unsigned char qm_magic[16] = {
|
||||
0x3c,
|
||||
0xb8,
|
||||
0x64,
|
||||
0x18,
|
||||
0xca,
|
||||
0xef,
|
||||
0x9c,
|
||||
0x95,
|
||||
0xcd,
|
||||
0x21,
|
||||
0x1c,
|
||||
0xbf,
|
||||
0x60,
|
||||
0xa1,
|
||||
0xbd,
|
||||
0xdd};
|
||||
|
||||
static std::map<std::string,std::string> i18n_entries;
|
||||
static std::map<std::string, std::string> i18n_entries;
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
/* Logging isn't initialized yet when this is run */
|
||||
/* add std::flush, because std::endl doesn't seem to flush, contrary to expected */
|
||||
// #define i18n_log(x) do { std::cout << __FILE__ << ":" << __LINE__ << ": " << x << std::endl; std::cout << std::flush; } while(0)
|
||||
// #define i18n_log(x) do { std::cout << __FILE__ << ":" << __LINE__ << ": " << x << std::endl;
|
||||
// std::cout << std::flush; } while(0)
|
||||
#define i18n_log(x) ((void)0)
|
||||
|
||||
std::string i18n_get_language()
|
||||
{
|
||||
const char *e;
|
||||
std::string i18n_get_language() {
|
||||
const char* e;
|
||||
|
||||
e = getenv("LANG");
|
||||
i18n_log("LANG=" << e);
|
||||
if (!e || !*e) {
|
||||
e = getenv("LC_ALL");
|
||||
i18n_log("LC_ALL=" << e);
|
||||
}
|
||||
if (!e || !*e)
|
||||
e = "en";
|
||||
e = getenv("LANG");
|
||||
i18n_log("LANG=" << e);
|
||||
if (!e || !*e) {
|
||||
e = getenv("LC_ALL");
|
||||
i18n_log("LC_ALL=" << e);
|
||||
}
|
||||
if (!e || !*e)
|
||||
e = "en";
|
||||
|
||||
std::string language = e;
|
||||
language = language.substr(0, language.find("."));
|
||||
language = language.substr(0, language.find("@"));
|
||||
std::string language = e;
|
||||
language = language.substr(0, language.find("."));
|
||||
language = language.substr(0, language.find("@"));
|
||||
|
||||
// check valid values
|
||||
for (char c: language)
|
||||
if (!strchr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-.@", c))
|
||||
return "en";
|
||||
// check valid values
|
||||
for (char c : language)
|
||||
if (!strchr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-.@", c))
|
||||
return "en";
|
||||
|
||||
std::transform(language.begin(), language.end(), language.begin(), tolower);
|
||||
if (language.size() > MAX_LANGUAGE_SIZE)
|
||||
{
|
||||
i18n_log("Language from LANG/LC_ALL suspiciously long, defaulting to en");
|
||||
return "en";
|
||||
}
|
||||
return language;
|
||||
std::transform(language.begin(), language.end(), language.begin(), tolower);
|
||||
if (language.size() > MAX_LANGUAGE_SIZE) {
|
||||
i18n_log("Language from LANG/LC_ALL suspiciously long, defaulting to en");
|
||||
return "en";
|
||||
}
|
||||
return language;
|
||||
}
|
||||
|
||||
static uint32_t be32(const unsigned char *data)
|
||||
{
|
||||
return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
|
||||
static uint32_t be32(const unsigned char* data) {
|
||||
return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
|
||||
}
|
||||
|
||||
static std::string utf16(const unsigned char *data, uint32_t len)
|
||||
{
|
||||
std::string s;
|
||||
while (len >= 2) {
|
||||
uint32_t code = (data[0] << 8) | data[1];
|
||||
data += 2;
|
||||
len -= 2;
|
||||
if (code >= 0xd800 && code <= 0xdbfff && len >= 2) {
|
||||
uint32_t next = (data[0] << 8) | data[1];
|
||||
if (next >= 0xdc00 && next <= 0xdfff) {
|
||||
code = (code << 10) + next - 0x35dfc00;
|
||||
static std::string utf16(const unsigned char* data, uint32_t len) {
|
||||
std::string s;
|
||||
while (len >= 2) {
|
||||
uint32_t code = (data[0] << 8) | data[1];
|
||||
data += 2;
|
||||
len -= 2;
|
||||
}
|
||||
if (code >= 0xd800 && code <= 0xdbfff && len >= 2) {
|
||||
uint32_t next = (data[0] << 8) | data[1];
|
||||
if (next >= 0xdc00 && next <= 0xdfff) {
|
||||
code = (code << 10) + next - 0x35dfc00;
|
||||
data += 2;
|
||||
len -= 2;
|
||||
}
|
||||
}
|
||||
if (code <= 0x7f) {
|
||||
s += (char)code;
|
||||
} else if (code <= 0x7ff) {
|
||||
s += 0xc0 | (code >> 6);
|
||||
s += 0x80 | (code & 0x3f);
|
||||
} else if (code <= 0xffff) {
|
||||
s += 0xe0 | (code >> 12);
|
||||
s += 0x80 | ((code >> 6) & 0x3f);
|
||||
s += 0x80 | (code & 0x3f);
|
||||
} else {
|
||||
s += 0xf0 | (code >> 18);
|
||||
s += 0x80 | ((code >> 12) & 0x3f);
|
||||
s += 0x80 | ((code >> 6) & 0x3f);
|
||||
s += 0x80 | (code & 0x3f);
|
||||
}
|
||||
}
|
||||
if (code <= 0x7f) {
|
||||
s += (char)code;
|
||||
}
|
||||
else if (code <= 0x7ff) {
|
||||
s += 0xc0 | (code >> 6);
|
||||
s += 0x80 | (code & 0x3f);
|
||||
}
|
||||
else if (code <= 0xffff) {
|
||||
s += 0xe0 | (code >> 12);
|
||||
s += 0x80 | ((code >> 6) & 0x3f);
|
||||
s += 0x80 | (code & 0x3f);
|
||||
}
|
||||
else {
|
||||
s += 0xf0 | (code >> 18);
|
||||
s += 0x80 | ((code >> 12) & 0x3f);
|
||||
s += 0x80 | ((code >> 6) & 0x3f);
|
||||
s += 0x80 | (code & 0x3f);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
return s;
|
||||
}
|
||||
|
||||
static std::string utf8(const unsigned char *data, uint32_t len)
|
||||
{
|
||||
/* assume well formedness */
|
||||
return std::string((const char *)data,len);
|
||||
static std::string utf8(const unsigned char* data, uint32_t len) {
|
||||
/* assume well formedness */
|
||||
return std::string((const char*)data, len);
|
||||
}
|
||||
|
||||
int i18n_set_language(const char *directory, const char *base, std::string language)
|
||||
{
|
||||
i18n_log("i18n_set_language(" << directory << "," << base << ")");
|
||||
if (!directory || !base)
|
||||
return -1;
|
||||
int i18n_set_language(const char* directory, const char* base, std::string language) {
|
||||
i18n_log("i18n_set_language(" << directory << "," << base << ")");
|
||||
if (!directory || !base)
|
||||
return -1;
|
||||
|
||||
if (language.empty())
|
||||
language = i18n_get_language();
|
||||
std::string basename = base + "_"s + language + ".qm";
|
||||
auto filename = fs::u8path(directory) / fs::u8path(basename);
|
||||
i18n_log("Loading translations for language " << language);
|
||||
if (language.empty())
|
||||
language = i18n_get_language();
|
||||
std::string basename = base + "_"s + language + ".qm";
|
||||
auto filename = fs::u8path(directory) / fs::u8path(basename);
|
||||
i18n_log("Loading translations for language " << language);
|
||||
|
||||
std::string contents;
|
||||
if (std::error_code ec; fs::exists(filename, ec)) {
|
||||
if (!tools::slurp_file(filename, contents)) {
|
||||
i18n_log("Failed to load translations file: " << filename);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
i18n_log("Translations file not found: " << filename);
|
||||
if (!find_embedded_file(basename, contents)) {
|
||||
i18n_log("Embedded translations file not found: " << basename);
|
||||
const char *underscore = strchr(language.c_str(), '_');
|
||||
if (underscore) {
|
||||
std::string fallback_language = std::string(language, 0, underscore - language.c_str());
|
||||
basename = base + "_"s + fallback_language + ".qm";
|
||||
filename.replace_filename(fs::u8path(basename));
|
||||
i18n_log("Loading translations for language " << fallback_language);
|
||||
if (std::error_code ec; fs::exists(filename, ec)) {
|
||||
if (!tools::slurp_file(filename, contents)) {
|
||||
std::string contents;
|
||||
if (std::error_code ec; fs::exists(filename, ec)) {
|
||||
if (!tools::slurp_file(filename, contents)) {
|
||||
i18n_log("Failed to load translations file: " << filename);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
i18n_log("Translations file not found: " << filename);
|
||||
if (!find_embedded_file(basename, contents)) {
|
||||
i18n_log("Embedded translations file not found: " << filename);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned char *data = reinterpret_cast<const unsigned char*>(contents.c_str());
|
||||
size_t datalen = contents.size();
|
||||
size_t idx = 0;
|
||||
i18n_log("Translations file size: " << datalen);
|
||||
|
||||
/* Format of the QM file (AFAICT):
|
||||
* 16 bytes magic
|
||||
* chunk list: N instances of chunks:
|
||||
* 1 byte: chunk type (0x42: offsets, 0x69: messages)
|
||||
* 4 bytes: chunk length, big endian
|
||||
* D bytes: "chunk length" bytes of data
|
||||
*
|
||||
* 0x42 chunk: N instances of subchunks:
|
||||
* 1 byte: subchunk type
|
||||
* 0x01: end, no data
|
||||
* 0x02: unsupported
|
||||
* 0x03: translation
|
||||
* 4 bytes: string length, big endian
|
||||
* N bytes: string data, UTF-16 (or UCS2-BE ?)
|
||||
* 0x04: unsupported
|
||||
* 0x05: obsolete, unsupported
|
||||
* 0x06: source text
|
||||
* 0x07: context
|
||||
* 0x08: obsolete, unsupported
|
||||
* other: unsupported
|
||||
* 4 bytes: subchunk length, big endian - except for 0x01, which has none
|
||||
* S bytes: "chunk length" bytes of data
|
||||
* 0x69 chunk:
|
||||
* string data indexed by the 0x42 chunk data
|
||||
*/
|
||||
if (datalen < sizeof(qm_magic) || memcmp(data, qm_magic, sizeof(qm_magic))) {
|
||||
i18n_log("Bad translations file format: " << filename);
|
||||
return -1;
|
||||
}
|
||||
idx += sizeof(qm_magic);
|
||||
|
||||
unsigned char chunk_type;
|
||||
uint32_t chunk_size;
|
||||
uint32_t num_messages = (uint32_t)-1;
|
||||
uint32_t messages_idx = (uint32_t)-1;
|
||||
uint32_t offsets_idx = (uint32_t)-1;
|
||||
while (idx < datalen) {
|
||||
if (idx + 5 > datalen) {
|
||||
i18n_log("Bad translations file format: " << filename);
|
||||
return -1;
|
||||
}
|
||||
chunk_type = data[idx++];
|
||||
chunk_size = be32(data+idx);
|
||||
idx += 4;
|
||||
|
||||
i18n_log("Found " << chunk_type << " of " << chunk_size << " bytes");
|
||||
if (chunk_size >= datalen || idx > datalen - chunk_size) {
|
||||
i18n_log("Bad translations file format: " << filename);
|
||||
return -1;
|
||||
} else {
|
||||
i18n_log("Translations file not found: " << filename);
|
||||
if (!find_embedded_file(basename, contents)) {
|
||||
i18n_log("Embedded translations file not found: " << basename);
|
||||
const char* underscore = strchr(language.c_str(), '_');
|
||||
if (underscore) {
|
||||
std::string fallback_language =
|
||||
std::string(language, 0, underscore - language.c_str());
|
||||
basename = base + "_"s + fallback_language + ".qm";
|
||||
filename.replace_filename(fs::u8path(basename));
|
||||
i18n_log("Loading translations for language " << fallback_language);
|
||||
if (std::error_code ec; fs::exists(filename, ec)) {
|
||||
if (!tools::slurp_file(filename, contents)) {
|
||||
i18n_log("Failed to load translations file: " << filename);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
i18n_log("Translations file not found: " << filename);
|
||||
if (!find_embedded_file(basename, contents)) {
|
||||
i18n_log("Embedded translations file not found: " << filename);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (chunk_type) {
|
||||
case 0x42:
|
||||
i18n_log("Found offsets at " << idx);
|
||||
/* two 32 bit integers, and possible padding */
|
||||
offsets_idx = idx;
|
||||
num_messages = chunk_size / 8;
|
||||
break;
|
||||
case 0x69:
|
||||
i18n_log("Found messages at " << idx);
|
||||
messages_idx = idx;
|
||||
break;
|
||||
default:
|
||||
i18n_log("Found unsupported chunk type: " << chunk_type);
|
||||
break;
|
||||
}
|
||||
const unsigned char* data = reinterpret_cast<const unsigned char*>(contents.c_str());
|
||||
size_t datalen = contents.size();
|
||||
size_t idx = 0;
|
||||
i18n_log("Translations file size: " << datalen);
|
||||
|
||||
idx += chunk_size;
|
||||
}
|
||||
|
||||
if (offsets_idx == (uint32_t)-1) {
|
||||
i18n_log("No offsets chunk found");
|
||||
return -1;
|
||||
}
|
||||
if (messages_idx == (uint32_t)-1) {
|
||||
i18n_log("No messages chunk found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string translation, source, context;
|
||||
for (uint32_t m = 0; m < num_messages; ++m) {
|
||||
be32(data+offsets_idx+m*8); // unused
|
||||
idx = be32(data+offsets_idx+m*8+4);
|
||||
idx += messages_idx;
|
||||
|
||||
if (idx > datalen || idx + 1 > datalen) {
|
||||
i18n_log("Bad translations file format: " << filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (idx + 5 > datalen) {
|
||||
/* Format of the QM file (AFAICT):
|
||||
* 16 bytes magic
|
||||
* chunk list: N instances of chunks:
|
||||
* 1 byte: chunk type (0x42: offsets, 0x69: messages)
|
||||
* 4 bytes: chunk length, big endian
|
||||
* D bytes: "chunk length" bytes of data
|
||||
*
|
||||
* 0x42 chunk: N instances of subchunks:
|
||||
* 1 byte: subchunk type
|
||||
* 0x01: end, no data
|
||||
* 0x02: unsupported
|
||||
* 0x03: translation
|
||||
* 4 bytes: string length, big endian
|
||||
* N bytes: string data, UTF-16 (or UCS2-BE ?)
|
||||
* 0x04: unsupported
|
||||
* 0x05: obsolete, unsupported
|
||||
* 0x06: source text
|
||||
* 0x07: context
|
||||
* 0x08: obsolete, unsupported
|
||||
* other: unsupported
|
||||
* 4 bytes: subchunk length, big endian - except for 0x01, which has none
|
||||
* S bytes: "chunk length" bytes of data
|
||||
* 0x69 chunk:
|
||||
* string data indexed by the 0x42 chunk data
|
||||
*/
|
||||
if (datalen < sizeof(qm_magic) || memcmp(data, qm_magic, sizeof(qm_magic))) {
|
||||
i18n_log("Bad translations file format: " << filename);
|
||||
return -1;
|
||||
}
|
||||
chunk_type = data[idx++];
|
||||
chunk_size = 0;
|
||||
if (chunk_type == 0x01) {
|
||||
i18n_entries[context + "\0"s + source] = translation;
|
||||
context.clear();
|
||||
source.clear();
|
||||
translation.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
chunk_size = be32(data+idx);
|
||||
idx += 4;
|
||||
i18n_log("Found " << chunk_type << " of " << chunk_size << " bytes");
|
||||
if (chunk_size >= datalen || idx > datalen - chunk_size) {
|
||||
i18n_log("Bad translations file format: " << filename);
|
||||
return -1;
|
||||
}
|
||||
switch (chunk_type) {
|
||||
case 0x03: // translation, UTF-16
|
||||
translation = utf16(data+idx, chunk_size);
|
||||
i18n_log("Found translation: " << translation);
|
||||
break;
|
||||
case 0x06: // source, UTF-8
|
||||
source = utf8(data+idx, chunk_size);
|
||||
i18n_log("Found source: " << source);
|
||||
break;
|
||||
case 0x07: // context, UTF-8
|
||||
context = utf8(data+idx, chunk_size);
|
||||
i18n_log("Found context: " << context);
|
||||
break;
|
||||
}
|
||||
idx += chunk_size;
|
||||
}
|
||||
}
|
||||
idx += sizeof(qm_magic);
|
||||
|
||||
return 0;
|
||||
unsigned char chunk_type;
|
||||
uint32_t chunk_size;
|
||||
uint32_t num_messages = (uint32_t)-1;
|
||||
uint32_t messages_idx = (uint32_t)-1;
|
||||
uint32_t offsets_idx = (uint32_t)-1;
|
||||
while (idx < datalen) {
|
||||
if (idx + 5 > datalen) {
|
||||
i18n_log("Bad translations file format: " << filename);
|
||||
return -1;
|
||||
}
|
||||
chunk_type = data[idx++];
|
||||
chunk_size = be32(data + idx);
|
||||
idx += 4;
|
||||
|
||||
i18n_log("Found " << chunk_type << " of " << chunk_size << " bytes");
|
||||
if (chunk_size >= datalen || idx > datalen - chunk_size) {
|
||||
i18n_log("Bad translations file format: " << filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (chunk_type) {
|
||||
case 0x42:
|
||||
i18n_log("Found offsets at " << idx);
|
||||
/* two 32 bit integers, and possible padding */
|
||||
offsets_idx = idx;
|
||||
num_messages = chunk_size / 8;
|
||||
break;
|
||||
case 0x69:
|
||||
i18n_log("Found messages at " << idx);
|
||||
messages_idx = idx;
|
||||
break;
|
||||
default: i18n_log("Found unsupported chunk type: " << chunk_type); break;
|
||||
}
|
||||
|
||||
idx += chunk_size;
|
||||
}
|
||||
|
||||
if (offsets_idx == (uint32_t)-1) {
|
||||
i18n_log("No offsets chunk found");
|
||||
return -1;
|
||||
}
|
||||
if (messages_idx == (uint32_t)-1) {
|
||||
i18n_log("No messages chunk found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string translation, source, context;
|
||||
for (uint32_t m = 0; m < num_messages; ++m) {
|
||||
be32(data + offsets_idx + m * 8); // unused
|
||||
idx = be32(data + offsets_idx + m * 8 + 4);
|
||||
idx += messages_idx;
|
||||
|
||||
if (idx > datalen || idx + 1 > datalen) {
|
||||
i18n_log("Bad translations file format: " << filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (idx + 5 > datalen) {
|
||||
i18n_log("Bad translations file format: " << filename);
|
||||
return -1;
|
||||
}
|
||||
chunk_type = data[idx++];
|
||||
chunk_size = 0;
|
||||
if (chunk_type == 0x01) {
|
||||
i18n_entries[context + "\0"s + source] = translation;
|
||||
context.clear();
|
||||
source.clear();
|
||||
translation.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
chunk_size = be32(data + idx);
|
||||
idx += 4;
|
||||
i18n_log("Found " << chunk_type << " of " << chunk_size << " bytes");
|
||||
if (chunk_size >= datalen || idx > datalen - chunk_size) {
|
||||
i18n_log("Bad translations file format: " << filename);
|
||||
return -1;
|
||||
}
|
||||
switch (chunk_type) {
|
||||
case 0x03: // translation, UTF-16
|
||||
translation = utf16(data + idx, chunk_size);
|
||||
i18n_log("Found translation: " << translation);
|
||||
break;
|
||||
case 0x06: // source, UTF-8
|
||||
source = utf8(data + idx, chunk_size);
|
||||
i18n_log("Found source: " << source);
|
||||
break;
|
||||
case 0x07: // context, UTF-8
|
||||
context = utf8(data + idx, chunk_size);
|
||||
i18n_log("Found context: " << context);
|
||||
break;
|
||||
}
|
||||
idx += chunk_size;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The entries is constant by that time */
|
||||
const char *i18n_translate(const char *s, const std::string &context)
|
||||
{
|
||||
const std::string key = context + "\0"s + s;
|
||||
std::map<std::string,std::string>::const_iterator i = i18n_entries.find(key);
|
||||
if (i == i18n_entries.end())
|
||||
return s;
|
||||
return (*i).second.c_str();
|
||||
const char* i18n_translate(const char* s, const std::string& context) {
|
||||
const std::string key = context + "\0"s + s;
|
||||
std::map<std::string, std::string>::const_iterator i = i18n_entries.find(key);
|
||||
if (i == i18n_entries.end())
|
||||
return s;
|
||||
return (*i).second.c_str();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -29,10 +29,14 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
#define QT_TRANSLATE_NOOP(context,str) i18n_translate(str,context)
|
||||
#define QT_TRANSLATE_NOOP(context, str) i18n_translate(str, context)
|
||||
|
||||
std::string i18n_get_language();
|
||||
int i18n_set_language(const char *directory, const char *base, std::string language = std::string());
|
||||
const char *i18n_translate(const char *str, const std::string &context);
|
||||
inline const char *tr(const char *str) { return i18n_translate(str, std::string{}); }
|
||||
bool find_embedded_file(const std::string &name, std::string &data); // In the generated translation_files.cpp
|
||||
int i18n_set_language(
|
||||
const char* directory, const char* base, std::string language = std::string());
|
||||
const char* i18n_translate(const char* str, const std::string& context);
|
||||
inline const char* tr(const char* str) {
|
||||
return i18n_translate(str, std::string{});
|
||||
}
|
||||
bool find_embedded_file(
|
||||
const std::string& name, std::string& data); // In the generated translation_files.cpp
|
||||
|
|
|
@ -1,42 +1,46 @@
|
|||
#include "json_binary_proxy.h"
|
||||
#include <oxenc/hex.h>
|
||||
|
||||
#include <oxenc/base64.h>
|
||||
#include <oxenc/hex.h>
|
||||
|
||||
namespace tools {
|
||||
|
||||
void load_binary_parameter_impl(std::string_view bytes, size_t raw_size, bool allow_raw, uint8_t* val_data) {
|
||||
void load_binary_parameter_impl(
|
||||
std::string_view bytes, size_t raw_size, bool allow_raw, uint8_t* val_data) {
|
||||
if (allow_raw && bytes.size() == raw_size) {
|
||||
std::memcpy(val_data, bytes.data(), bytes.size());
|
||||
return;
|
||||
} else if (bytes.size() == raw_size * 2) {
|
||||
if (oxenc::is_hex(bytes)) {
|
||||
oxenc::from_hex(bytes.begin(), bytes.end(), val_data);
|
||||
std::memcpy(val_data, bytes.data(), bytes.size());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const size_t b64_padded = (raw_size + 2) / 3 * 4;
|
||||
const size_t b64_padding = raw_size % 3 == 1 ? 2 : raw_size % 3 == 2 ? 1 : 0;
|
||||
const size_t b64_unpadded = b64_padded - b64_padding;
|
||||
const std::string_view b64_padding_string = b64_padding == 2 ? "=="sv : b64_padding == 1 ? "="sv : ""sv;
|
||||
if (bytes.size() == b64_unpadded ||
|
||||
(b64_padding > 0 && bytes.size() == b64_padded && bytes.substr(b64_unpadded) == b64_padding_string)) {
|
||||
if (oxenc::is_base64(bytes)) {
|
||||
oxenc::from_base64(bytes.begin(), bytes.end(), val_data);
|
||||
return;
|
||||
} else if (bytes.size() == raw_size * 2) {
|
||||
if (oxenc::is_hex(bytes)) {
|
||||
oxenc::from_hex(bytes.begin(), bytes.end(), val_data);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const size_t b64_padded = (raw_size + 2) / 3 * 4;
|
||||
const size_t b64_padding = raw_size % 3 == 1 ? 2 : raw_size % 3 == 2 ? 1 : 0;
|
||||
const size_t b64_unpadded = b64_padded - b64_padding;
|
||||
const std::string_view b64_padding_string = b64_padding == 2 ? "=="sv
|
||||
: b64_padding == 1 ? "="sv
|
||||
: ""sv;
|
||||
if (bytes.size() == b64_unpadded || (b64_padding > 0 && bytes.size() == b64_padded &&
|
||||
bytes.substr(b64_unpadded) == b64_padding_string)) {
|
||||
if (oxenc::is_base64(bytes)) {
|
||||
oxenc::from_base64(bytes.begin(), bytes.end(), val_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error{"Invalid binary value: unexpected size and/or encoding"};
|
||||
}
|
||||
}
|
||||
|
||||
nlohmann::json& json_binary_proxy::operator=(std::string_view binary_data) {
|
||||
nlohmann::json& json_binary_proxy::operator=(std::string_view binary_data) {
|
||||
switch (format) {
|
||||
case fmt::bt: return e = binary_data;
|
||||
case fmt::hex: return e = oxenc::to_hex(binary_data);
|
||||
case fmt::base64: return e = oxenc::to_base64(binary_data);
|
||||
case fmt::bt: return e = binary_data;
|
||||
case fmt::hex: return e = oxenc::to_hex(binary_data);
|
||||
case fmt::base64: return e = oxenc::to_base64(binary_data);
|
||||
}
|
||||
throw std::runtime_error{"Internal error: invalid binary encoding"};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,57 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include "ringct/rctTypes.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include <string_view>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "crypto/crypto.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace tools {
|
||||
|
||||
// Binary types that we support for rpc input/output. For json, these must be specified as hex or
|
||||
// base64; for bt-encoded requests these can be accepted as binary, hex, or base64.
|
||||
template <typename T>
|
||||
inline constexpr bool json_is_binary = false;
|
||||
template <> inline constexpr bool json_is_binary<crypto::hash> = true;
|
||||
template <> inline constexpr bool json_is_binary<crypto::public_key> = true;
|
||||
template <> inline constexpr bool json_is_binary<crypto::ed25519_public_key> = true;
|
||||
template <> inline constexpr bool json_is_binary<crypto::x25519_public_key> = true;
|
||||
template <> inline constexpr bool json_is_binary<crypto::key_image> = true;
|
||||
template <> inline constexpr bool json_is_binary<rct::key> = true;
|
||||
// Binary types that we support for rpc input/output. For json, these must be specified as hex or
|
||||
// base64; for bt-encoded requests these can be accepted as binary, hex, or base64.
|
||||
template <typename T>
|
||||
inline constexpr bool json_is_binary = false;
|
||||
template <>
|
||||
inline constexpr bool json_is_binary<crypto::hash> = true;
|
||||
template <>
|
||||
inline constexpr bool json_is_binary<crypto::public_key> = true;
|
||||
template <>
|
||||
inline constexpr bool json_is_binary<crypto::ed25519_public_key> = true;
|
||||
template <>
|
||||
inline constexpr bool json_is_binary<crypto::x25519_public_key> = true;
|
||||
template <>
|
||||
inline constexpr bool json_is_binary<crypto::key_image> = true;
|
||||
template <>
|
||||
inline constexpr bool json_is_binary<rct::key> = true;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool json_is_binary_container = false;
|
||||
template <typename T>
|
||||
inline constexpr bool json_is_binary_container<std::vector<T>> = json_is_binary<T>;
|
||||
template <typename T>
|
||||
inline constexpr bool json_is_binary_container<std::unordered_set<T>> = json_is_binary<T>;
|
||||
template <typename T>
|
||||
inline constexpr bool json_is_binary_container = false;
|
||||
template <typename T>
|
||||
inline constexpr bool json_is_binary_container<std::vector<T>> = json_is_binary<T>;
|
||||
template <typename T>
|
||||
inline constexpr bool json_is_binary_container<std::unordered_set<T>> = json_is_binary<T>;
|
||||
|
||||
// De-referencing wrappers around the above:
|
||||
template <typename T> inline constexpr bool json_is_binary<const T&> = json_is_binary<T>;
|
||||
template <typename T> inline constexpr bool json_is_binary<T&&> = json_is_binary<T>;
|
||||
template <typename T> inline constexpr bool json_is_binary_container<const T&> = json_is_binary_container<T>;
|
||||
template <typename T> inline constexpr bool json_is_binary_container<T&&> = json_is_binary_container<T>;
|
||||
// De-referencing wrappers around the above:
|
||||
template <typename T>
|
||||
inline constexpr bool json_is_binary<const T&> = json_is_binary<T>;
|
||||
template <typename T>
|
||||
inline constexpr bool json_is_binary<T&&> = json_is_binary<T>;
|
||||
template <typename T>
|
||||
inline constexpr bool json_is_binary_container<const T&> = json_is_binary_container<T>;
|
||||
template <typename T>
|
||||
inline constexpr bool json_is_binary_container<T&&> = json_is_binary_container<T>;
|
||||
|
||||
void load_binary_parameter_impl(
|
||||
std::string_view bytes, size_t raw_size, bool allow_raw, uint8_t* val_data);
|
||||
|
||||
void load_binary_parameter_impl(std::string_view bytes, size_t raw_size, bool allow_raw, uint8_t* val_data);
|
||||
|
||||
// Loads a binary value from a string_view which may contain hex, base64, and (optionally) raw
|
||||
// bytes.
|
||||
template <typename T, typename = std::enable_if_t<json_is_binary<T>>>
|
||||
void load_binary_parameter(std::string_view bytes, bool allow_raw, T& val) {
|
||||
// Loads a binary value from a string_view which may contain hex, base64, and (optionally) raw
|
||||
// bytes.
|
||||
template <typename T, typename = std::enable_if_t<json_is_binary<T>>>
|
||||
void load_binary_parameter(std::string_view bytes, bool allow_raw, T& val) {
|
||||
load_binary_parameter_impl(bytes, sizeof(T), allow_raw, reinterpret_cast<uint8_t*>(&val));
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper around a nlohmann::json that assigns a binary value either as binary (for bt-encoding);
|
||||
// or as hex or base64 (for json-encoding).
|
||||
class json_binary_proxy {
|
||||
public:
|
||||
// Wrapper around a nlohmann::json that assigns a binary value either as binary (for bt-encoding);
|
||||
// or as hex or base64 (for json-encoding).
|
||||
class json_binary_proxy {
|
||||
public:
|
||||
nlohmann::json& e;
|
||||
enum class fmt { bt, hex, base64 } format;
|
||||
explicit json_binary_proxy(nlohmann::json& elem, fmt format)
|
||||
: e{elem}, format{format} {}
|
||||
explicit json_binary_proxy(nlohmann::json& elem, fmt format) : e{elem}, format{format} {}
|
||||
json_binary_proxy() = delete;
|
||||
|
||||
json_binary_proxy(const json_binary_proxy&) = default;
|
||||
|
@ -64,7 +74,7 @@ namespace tools {
|
|||
/// Descends into the json object, returning a new binary value proxy around the child element.
|
||||
template <typename T>
|
||||
json_binary_proxy operator[](T&& key) {
|
||||
return json_binary_proxy{e[std::forward<T>(key)], format};
|
||||
return json_binary_proxy{e[std::forward<T>(key)], format};
|
||||
}
|
||||
|
||||
/// Returns a binary value proxy around the first/last element (requires an underlying list)
|
||||
|
@ -76,63 +86,65 @@ namespace tools {
|
|||
|
||||
/// Assigns binary data from a string_view over a 1-byte, non-char type (e.g. unsigned char or
|
||||
/// uint8_t).
|
||||
template <typename Char, std::enable_if_t<sizeof(Char) == 1 && !std::is_same_v<Char, char>, int> = 0>
|
||||
template <
|
||||
typename Char,
|
||||
std::enable_if_t<sizeof(Char) == 1 && !std::is_same_v<Char, char>, int> = 0>
|
||||
nlohmann::json& operator=(std::basic_string_view<Char> binary_data) {
|
||||
return *this = std::string_view{reinterpret_cast<const char*>(binary_data.data()), binary_data.size()};
|
||||
return *this = std::string_view{
|
||||
reinterpret_cast<const char*>(binary_data.data()), binary_data.size()};
|
||||
}
|
||||
|
||||
/// Takes a trivial, no-padding data structure (e.g. a crypto::hash) as the value and dumps its
|
||||
/// contents as the binary value.
|
||||
template <typename T, std::enable_if_t<json_is_binary<T>, int> = 0>
|
||||
nlohmann::json& operator=(const T& val) {
|
||||
return *this = std::string_view{reinterpret_cast<const char*>(&val), sizeof(val)};
|
||||
return *this = std::string_view{reinterpret_cast<const char*>(&val), sizeof(val)};
|
||||
}
|
||||
|
||||
/// Takes a vector of some json_binary_proxy-assignable type and builds an array by assigning
|
||||
/// each one into a new array of binary values.
|
||||
template <typename T, std::enable_if_t<json_is_binary_container<T>, int> = 0>
|
||||
nlohmann::json& operator=(const T& vals) {
|
||||
auto a = nlohmann::json::array();
|
||||
for (auto& val : vals)
|
||||
json_binary_proxy{a.emplace_back(), format} = val;
|
||||
return e = std::move(a);
|
||||
auto a = nlohmann::json::array();
|
||||
for (auto& val : vals)
|
||||
json_binary_proxy{a.emplace_back(), format} = val;
|
||||
return e = std::move(a);
|
||||
}
|
||||
/// Emplaces a new nlohman::json to the end of an underlying list and returns a
|
||||
/// json_binary_proxy wrapping it.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// auto child = wrappedelem.emplace_back({"key1": 1}, {"key2": 2});
|
||||
/// child["binary-key"] = some_binary_thing;
|
||||
template <typename... Args>
|
||||
json_binary_proxy emplace_back(Args&&... args) {
|
||||
return json_binary_proxy{e.emplace_back(std::forward<Args>(args)...), format};
|
||||
}
|
||||
/// Emplaces a new nlohman::json to the end of an underlying list and returns a
|
||||
/// json_binary_proxy wrapping it.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// auto child = wrappedelem.emplace_back({"key1": 1}, {"key2": 2});
|
||||
/// child["binary-key"] = some_binary_thing;
|
||||
template <typename... Args>
|
||||
json_binary_proxy emplace_back(Args&&... args) {
|
||||
return json_binary_proxy{e.emplace_back(std::forward<Args>(args)...), format};
|
||||
}
|
||||
|
||||
/// Adds an element to an underlying list, then copies or moves the given argument onto it via
|
||||
/// json_binary_proxy assignment.
|
||||
template <typename T>
|
||||
void push_back(T&& val) {
|
||||
emplace_back() = std::forward<T>(val);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
/// Adds an element to an underlying list, then copies or moves the given argument onto it via
|
||||
/// json_binary_proxy assignment.
|
||||
template <typename T>
|
||||
void push_back(T&& val) {
|
||||
emplace_back() = std::forward<T>(val);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace tools
|
||||
|
||||
// Specializations of binary types for deserialization; when receiving these from json we expect
|
||||
// them encoded in hex or base64. These may *not* be used for serialization, and will throw if so
|
||||
// invoked; for serialization you need to use RPC_COMMAND::response_hex (or _b64) instead.
|
||||
namespace nlohmann {
|
||||
template <typename T>
|
||||
struct adl_serializer<T, std::enable_if_t<tools::json_is_binary<T>>> {
|
||||
template <typename T>
|
||||
struct adl_serializer<T, std::enable_if_t<tools::json_is_binary<T>>> {
|
||||
static_assert(std::is_trivially_copyable_v<T> && std::has_unique_object_representations_v<T>);
|
||||
|
||||
static void to_json(json& j, const T&) {
|
||||
throw std::logic_error{"Internal error: binary types are not directly serializable"};
|
||||
throw std::logic_error{"Internal error: binary types are not directly serializable"};
|
||||
}
|
||||
static void from_json(const json& j, T& val) {
|
||||
tools::load_binary_parameter(j.get<std::string_view>(), false /*no raw*/, val);
|
||||
tools::load_binary_parameter(j.get<std::string_view>(), false /*no raw*/, val);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace nlohmann
|
||||
|
|
|
@ -36,11 +36,10 @@ namespace tools {
|
|||
/// Takes any number of lockable objects, locks them atomically, and returns a tuple of
|
||||
/// std::unique_lock holding the individual locks.
|
||||
template <typename... T>
|
||||
[[nodiscard]]
|
||||
std::tuple<std::unique_lock<T>...> unique_locks(T& ...lockables) {
|
||||
[[nodiscard]] std::tuple<std::unique_lock<T>...> unique_locks(T&... lockables) {
|
||||
std::lock(lockables...);
|
||||
auto locks = std::make_tuple(std::unique_lock<T>(lockables, std::adopt_lock)...);
|
||||
return locks;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -5,29 +5,29 @@
|
|||
|
||||
namespace tools {
|
||||
|
||||
// Calculate the median element (the middle element, if an odd size, and the mean of the two
|
||||
// middle elements if even). Pass first=true if you don't care about the mean of the middle two,
|
||||
// in which case you'll get back the value of lower of the two middle elements.
|
||||
// This leaves the given range in an indeterminant (partially sorted) order.
|
||||
template <typename RandomAccessIter>
|
||||
auto median(RandomAccessIter begin, RandomAccessIter end, bool first=false) {
|
||||
// Calculate the median element (the middle element, if an odd size, and the mean of the two
|
||||
// middle elements if even). Pass first=true if you don't care about the mean of the middle two,
|
||||
// in which case you'll get back the value of lower of the two middle elements.
|
||||
// This leaves the given range in an indeterminant (partially sorted) order.
|
||||
template <typename RandomAccessIter>
|
||||
auto median(RandomAccessIter begin, RandomAccessIter end, bool first = false) {
|
||||
std::size_t size = end - begin;
|
||||
if (size == 0)
|
||||
return std::decay_t<decltype(*begin)>{};
|
||||
return std::decay_t<decltype(*begin)>{};
|
||||
|
||||
auto mid = begin + (size - 1) / 2;
|
||||
std::nth_element(begin, mid, end);
|
||||
|
||||
if (first || size % 2)
|
||||
return *mid;
|
||||
return *mid;
|
||||
|
||||
auto mid2 = std::min_element(mid + 1, end);
|
||||
return (*mid + *mid2) / 2;
|
||||
}
|
||||
|
||||
// Same as above, but takes a vector by value or move for convenience.
|
||||
template <typename T>
|
||||
T median(std::vector<T> v, bool first=false) {
|
||||
return median(v.begin(), v.end(), first);
|
||||
}
|
||||
}
|
||||
|
||||
// Same as above, but takes a vector by value or move for convenience.
|
||||
template <typename T>
|
||||
T median(std::vector<T> v, bool first = false) {
|
||||
return median(v.begin(), v.end(), first);
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <oxenc/variant.h>
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
#ifdef __GNUG__
|
||||
#include <cxxabi.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#endif
|
||||
|
||||
|
@ -12,24 +14,28 @@ namespace tools {
|
|||
|
||||
namespace detail {
|
||||
|
||||
template <typename T, typename T1, typename... Ts>
|
||||
constexpr size_t template_index_impl_inner() {
|
||||
if constexpr (std::is_same_v<T, T1>) return 0;
|
||||
else {
|
||||
static_assert(sizeof...(Ts) > 0, "Type not found");
|
||||
return 1 + template_index_impl_inner<T, Ts...>();
|
||||
template <typename T, typename T1, typename... Ts>
|
||||
constexpr size_t template_index_impl_inner() {
|
||||
if constexpr (std::is_same_v<T, T1>)
|
||||
return 0;
|
||||
else {
|
||||
static_assert(sizeof...(Ts) > 0, "Type not found");
|
||||
return 1 + template_index_impl_inner<T, Ts...>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename C> struct template_index_impl {};
|
||||
template <typename T, typename C>
|
||||
struct template_index_impl {};
|
||||
|
||||
template <typename T, template<typename...> typename C, typename... Ts>
|
||||
struct template_index_impl<T, C<Ts...>> : std::integral_constant<size_t, template_index_impl_inner<T, Ts...>()> {};
|
||||
template <typename T, template <typename...> typename C, typename... Ts>
|
||||
struct template_index_impl<T, C<Ts...>>
|
||||
: std::integral_constant<size_t, template_index_impl_inner<T, Ts...>()> {};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace detail
|
||||
|
||||
/// Type wrapper that contains an arbitrary list of types.
|
||||
template <typename...> struct type_list {};
|
||||
template <typename...>
|
||||
struct type_list {};
|
||||
|
||||
/// Accesses the index of the first T within a template type's type list. E.g.
|
||||
///
|
||||
|
@ -70,6 +76,8 @@ inline std::string type_name(const std::type_info& ti) {
|
|||
|
||||
/// Same as above, but uses a templated type instead of a type_info argument.
|
||||
template <typename T>
|
||||
inline std::string type_name() { return type_name(typeid(T)); }
|
||||
inline std::string type_name() {
|
||||
return type_name(typeid(T));
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -26,52 +26,51 @@
|
|||
// 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.
|
||||
|
||||
#include "string_util.h"
|
||||
#include "notify.h"
|
||||
|
||||
#include "epee/misc_log_ex.h"
|
||||
#include "logging/oxen_logger.h"
|
||||
#include "spawn.h"
|
||||
#include "notify.h"
|
||||
#include "string_util.h"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
namespace tools {
|
||||
|
||||
static auto logcat = log::Cat("notify");
|
||||
static auto logcat = log::Cat("notify");
|
||||
|
||||
/*
|
||||
TODO:
|
||||
TODO:
|
||||
- Improve tokenization to handle paths containing whitespaces, quotes, etc.
|
||||
- Windows unicode support (implies implementing unicode command line parsing code)
|
||||
*/
|
||||
Notify::Notify(std::string_view spec)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(!spec.empty(), "Empty spec");
|
||||
Notify::Notify(std::string_view spec) {
|
||||
CHECK_AND_ASSERT_THROW_MES(!spec.empty(), "Empty spec");
|
||||
|
||||
auto pieces = tools::split_any(spec, " \t", true);
|
||||
CHECK_AND_ASSERT_THROW_MES(pieces.size() > 0, "Failed to parse spec");
|
||||
filename = fs::u8path(pieces[0]);
|
||||
CHECK_AND_ASSERT_THROW_MES(fs::exists(filename), "File not found: " << filename);
|
||||
auto pieces = tools::split_any(spec, " \t", true);
|
||||
CHECK_AND_ASSERT_THROW_MES(pieces.size() > 0, "Failed to parse spec");
|
||||
filename = fs::u8path(pieces[0]);
|
||||
CHECK_AND_ASSERT_THROW_MES(fs::exists(filename), "File not found: " << filename);
|
||||
|
||||
args.reserve(pieces.size());
|
||||
for (const auto& piece : pieces)
|
||||
args.emplace_back(piece);
|
||||
args.reserve(pieces.size());
|
||||
for (const auto& piece : pieces)
|
||||
args.emplace_back(piece);
|
||||
}
|
||||
|
||||
void Notify::replace_tag(std::vector<std::string>& margs, std::string_view tag, std::string_view value)
|
||||
{
|
||||
if (tag.empty())
|
||||
return;
|
||||
// Skip margs[0], it's the binary name
|
||||
for (size_t i = 1; i < margs.size(); i++) {
|
||||
size_t pos = 0;
|
||||
while ((pos = margs[i].find(tag, pos)) != std::string::npos) {
|
||||
margs[i].replace(pos, tag.size(), value);
|
||||
pos += value.size();
|
||||
void Notify::replace_tag(
|
||||
std::vector<std::string>& margs, std::string_view tag, std::string_view value) {
|
||||
if (tag.empty())
|
||||
return;
|
||||
// Skip margs[0], it's the binary name
|
||||
for (size_t i = 1; i < margs.size(); i++) {
|
||||
size_t pos = 0;
|
||||
while ((pos = margs[i].find(tag, pos)) != std::string::npos) {
|
||||
margs[i].replace(pos, tag.size(), value);
|
||||
pos += value.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Notify::spawn(const std::vector<std::string>& margs) const {
|
||||
return tools::spawn(filename, margs, false);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -26,44 +26,49 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include "common/format.h"
|
||||
#include "fs.h"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
namespace tools {
|
||||
|
||||
class Notify
|
||||
{
|
||||
public:
|
||||
explicit Notify(std::string_view spec);
|
||||
class Notify {
|
||||
public:
|
||||
explicit Notify(std::string_view spec);
|
||||
|
||||
template <typename T, typename... MoreTags>
|
||||
int notify(std::string_view tag, const T& value, MoreTags&&... more) const {
|
||||
std::vector<std::string> margs{args};
|
||||
replace_tags(margs, tag, value, std::forward<MoreTags>(more)...);
|
||||
return spawn(margs);
|
||||
}
|
||||
template <typename T, typename... MoreTags>
|
||||
int notify(std::string_view tag, const T& value, MoreTags&&... more) const {
|
||||
std::vector<std::string> margs{args};
|
||||
replace_tags(margs, tag, value, std::forward<MoreTags>(more)...);
|
||||
return spawn(margs);
|
||||
}
|
||||
|
||||
private:
|
||||
fs::path filename;
|
||||
std::vector<std::string> args;
|
||||
private:
|
||||
fs::path filename;
|
||||
std::vector<std::string> args;
|
||||
|
||||
int spawn(const std::vector<std::string>& margs) const;
|
||||
int spawn(const std::vector<std::string>& margs) const;
|
||||
|
||||
template <typename T, typename... MoreTags>
|
||||
static void replace_tags(std::vector<std::string>& margs, std::string_view tag, const T& value, MoreTags&&... more) {
|
||||
replace_tag(margs, tag, "{}"_format(value));
|
||||
if constexpr (sizeof...(MoreTags) > 0)
|
||||
replace_tags(margs, std::forward<MoreTags>(more)...);
|
||||
}
|
||||
template <typename T, typename... MoreTags>
|
||||
static void replace_tags(
|
||||
std::vector<std::string>& margs,
|
||||
std::string_view tag,
|
||||
const T& value,
|
||||
MoreTags&&... more) {
|
||||
replace_tag(margs, tag, "{}"_format(value));
|
||||
if constexpr (sizeof...(MoreTags) > 0)
|
||||
replace_tags(margs, std::forward<MoreTags>(more)...);
|
||||
}
|
||||
|
||||
static void replace_tag(std::vector<std::string>& margs, std::string_view tag, std::string_view value);
|
||||
static void replace_tag(
|
||||
std::vector<std::string>& margs, std::string_view tag, std::string_view value);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -18,14 +18,15 @@
|
|||
|
||||
/* Specification. */
|
||||
|
||||
#include <limits>
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
// TODO(oxen): This is temporary until we switch to integer math for calculating
|
||||
// block rewards. We provide the specific implementation to minimise the risk of
|
||||
// different results from math functions across different std libraries.
|
||||
static_assert(std::numeric_limits<double>::is_iec559, "We require IEEE standard compliant doubles.");
|
||||
static_assert(
|
||||
std::numeric_limits<double>::is_iec559, "We require IEEE standard compliant doubles.");
|
||||
|
||||
/* Best possible approximation of log(2) as a 'double'. */
|
||||
#define LOG2 0.693147180559945309417232121458176568075
|
||||
|
@ -39,360 +40,352 @@ static_assert(std::numeric_limits<double>::is_iec559, "We require IEEE standard
|
|||
/* Best possible approximation of 256/log(2) as a 'double'. */
|
||||
#define LOG2_BY_256_INVERSE 369.329930467574632284140718336484387181
|
||||
|
||||
double
|
||||
oxen::exp2(double x)
|
||||
{
|
||||
/* exp2(x) = exp(x*log(2)).
|
||||
If we would compute it like this, there would be rounding errors for
|
||||
integer or near-integer values of x. To avoid these, we inline the
|
||||
algorithm for exp(), and the multiplication with log(2) cancels a
|
||||
division by log(2). */
|
||||
double oxen::exp2(double x) {
|
||||
/* exp2(x) = exp(x*log(2)).
|
||||
If we would compute it like this, there would be rounding errors for
|
||||
integer or near-integer values of x. To avoid these, we inline the
|
||||
algorithm for exp(), and the multiplication with log(2) cancels a
|
||||
division by log(2). */
|
||||
|
||||
// if (isnand (x)) // unnecessary for us
|
||||
// return x;
|
||||
// if (isnand (x)) // unnecessary for us
|
||||
// return x;
|
||||
|
||||
if (x > (double) DBL_MAX_EXP)
|
||||
/* x > DBL_MAX_EXP
|
||||
hence exp2(x) > 2^DBL_MAX_EXP, overflows to Infinity. */
|
||||
return HUGE_VAL;
|
||||
if (x > (double)DBL_MAX_EXP)
|
||||
/* x > DBL_MAX_EXP
|
||||
hence exp2(x) > 2^DBL_MAX_EXP, overflows to Infinity. */
|
||||
return HUGE_VAL;
|
||||
|
||||
if (x < (double) (DBL_MIN_EXP - 1 - DBL_MANT_DIG))
|
||||
/* x < (DBL_MIN_EXP - 1 - DBL_MANT_DIG)
|
||||
hence exp2(x) < 2^(DBL_MIN_EXP-1-DBL_MANT_DIG),
|
||||
underflows to zero. */
|
||||
return 0.0;
|
||||
if (x < (double)(DBL_MIN_EXP - 1 - DBL_MANT_DIG))
|
||||
/* x < (DBL_MIN_EXP - 1 - DBL_MANT_DIG)
|
||||
hence exp2(x) < 2^(DBL_MIN_EXP-1-DBL_MANT_DIG),
|
||||
underflows to zero. */
|
||||
return 0.0;
|
||||
|
||||
/* Decompose x into
|
||||
x = n + m/256 + y/log(2)
|
||||
where
|
||||
n is an integer,
|
||||
m is an integer, -128 <= m <= 128,
|
||||
y is a number, |y| <= log(2)/512 + epsilon = 0.00135...
|
||||
Then
|
||||
exp2(x) = 2^n * exp(m * log(2)/256) * exp(y)
|
||||
The first factor is an ldexpl() call.
|
||||
The second factor is a table lookup.
|
||||
The third factor is computed
|
||||
- either as sinh(y) + cosh(y)
|
||||
where sinh(y) is computed through the power series:
|
||||
sinh(y) = y + y^3/3! + y^5/5! + ...
|
||||
and cosh(y) is computed as hypot(1, sinh(y)),
|
||||
- or as exp(2*z) = (1 + tanh(z)) / (1 - tanh(z))
|
||||
where z = y/2
|
||||
and tanh(z) is computed through its power series:
|
||||
tanh(z) = z
|
||||
- 1/3 * z^3
|
||||
+ 2/15 * z^5
|
||||
- 17/315 * z^7
|
||||
+ 62/2835 * z^9
|
||||
- 1382/155925 * z^11
|
||||
+ 21844/6081075 * z^13
|
||||
- 929569/638512875 * z^15
|
||||
+ ...
|
||||
Since |z| <= log(2)/1024 < 0.0007, the relative contribution of the
|
||||
z^7 term is < 0.0007^6 < 2^-60 <= 2^-DBL_MANT_DIG, therefore we can
|
||||
truncate the series after the z^5 term. */
|
||||
/* Decompose x into
|
||||
x = n + m/256 + y/log(2)
|
||||
where
|
||||
n is an integer,
|
||||
m is an integer, -128 <= m <= 128,
|
||||
y is a number, |y| <= log(2)/512 + epsilon = 0.00135...
|
||||
Then
|
||||
exp2(x) = 2^n * exp(m * log(2)/256) * exp(y)
|
||||
The first factor is an ldexpl() call.
|
||||
The second factor is a table lookup.
|
||||
The third factor is computed
|
||||
- either as sinh(y) + cosh(y)
|
||||
where sinh(y) is computed through the power series:
|
||||
sinh(y) = y + y^3/3! + y^5/5! + ...
|
||||
and cosh(y) is computed as hypot(1, sinh(y)),
|
||||
- or as exp(2*z) = (1 + tanh(z)) / (1 - tanh(z))
|
||||
where z = y/2
|
||||
and tanh(z) is computed through its power series:
|
||||
tanh(z) = z
|
||||
- 1/3 * z^3
|
||||
+ 2/15 * z^5
|
||||
- 17/315 * z^7
|
||||
+ 62/2835 * z^9
|
||||
- 1382/155925 * z^11
|
||||
+ 21844/6081075 * z^13
|
||||
- 929569/638512875 * z^15
|
||||
+ ...
|
||||
Since |z| <= log(2)/1024 < 0.0007, the relative contribution of the
|
||||
z^7 term is < 0.0007^6 < 2^-60 <= 2^-DBL_MANT_DIG, therefore we can
|
||||
truncate the series after the z^5 term. */
|
||||
|
||||
{
|
||||
double nm = oxen::round (x * 256.0); /* = 256 * n + m */
|
||||
double z = (x * 256.0 - nm) * (LOG2_BY_256 * 0.5);
|
||||
{
|
||||
double nm = oxen::round(x * 256.0); /* = 256 * n + m */
|
||||
double z = (x * 256.0 - nm) * (LOG2_BY_256 * 0.5);
|
||||
|
||||
/* Coefficients of the power series for tanh(z). */
|
||||
#define TANH_COEFF_1 1.0
|
||||
#define TANH_COEFF_3 -0.333333333333333333333333333333333333334
|
||||
#define TANH_COEFF_5 0.133333333333333333333333333333333333334
|
||||
#define TANH_COEFF_7 -0.053968253968253968253968253968253968254
|
||||
#define TANH_COEFF_9 0.0218694885361552028218694885361552028218
|
||||
#define TANH_COEFF_1 1.0
|
||||
#define TANH_COEFF_3 -0.333333333333333333333333333333333333334
|
||||
#define TANH_COEFF_5 0.133333333333333333333333333333333333334
|
||||
#define TANH_COEFF_7 -0.053968253968253968253968253968253968254
|
||||
#define TANH_COEFF_9 0.0218694885361552028218694885361552028218
|
||||
#define TANH_COEFF_11 -0.00886323552990219656886323552990219656886
|
||||
#define TANH_COEFF_13 0.00359212803657248101692546136990581435026
|
||||
#define TANH_COEFF_13 0.00359212803657248101692546136990581435026
|
||||
#define TANH_COEFF_15 -0.00145583438705131826824948518070211191904
|
||||
|
||||
double z2 = z * z;
|
||||
double tanh_z =
|
||||
((TANH_COEFF_5
|
||||
* z2 + TANH_COEFF_3)
|
||||
* z2 + TANH_COEFF_1)
|
||||
* z;
|
||||
double z2 = z * z;
|
||||
double tanh_z = ((TANH_COEFF_5 * z2 + TANH_COEFF_3) * z2 + TANH_COEFF_1) * z;
|
||||
|
||||
double exp_y = (1.0 + tanh_z) / (1.0 - tanh_z);
|
||||
double exp_y = (1.0 + tanh_z) / (1.0 - tanh_z);
|
||||
|
||||
int n = (int) oxen::round (nm * (1.0 / 256.0));
|
||||
int m = (int) nm - 256 * n;
|
||||
int n = (int)oxen::round(nm * (1.0 / 256.0));
|
||||
int m = (int)nm - 256 * n;
|
||||
|
||||
/* exp_table[i] = exp((i - 128) * log(2)/256).
|
||||
Computed in GNU clisp through
|
||||
(setf (long-float-digits) 128)
|
||||
(setq a 0L0)
|
||||
(setf (long-float-digits) 256)
|
||||
(dotimes (i 257)
|
||||
(format t " ~D,~%"
|
||||
(float (exp (* (/ (- i 128) 256) (log 2L0))) a))) */
|
||||
static const double exp_table[257] =
|
||||
{
|
||||
0.707106781186547524400844362104849039284,
|
||||
0.709023942160207598920563322257676190836,
|
||||
0.710946301084582779904674297352120049962,
|
||||
0.71287387205274715340350157671438300618,
|
||||
0.714806669195985005617532889137569953044,
|
||||
0.71674470668389442125974978427737336719,
|
||||
0.71868799872449116280161304224785251353,
|
||||
0.720636559564312831364255957304947586072,
|
||||
0.72259040348852331001850312073583545284,
|
||||
0.724549544821017490259402705487111270714,
|
||||
0.726513997924526282423036245842287293786,
|
||||
0.728483777200721910815451524818606761737,
|
||||
0.730458897090323494325651445155310766577,
|
||||
0.732439372073202913296664682112279175616,
|
||||
0.734425216668490963430822513132890712652,
|
||||
0.736416445434683797507470506133110286942,
|
||||
0.738413072969749655693453740187024961962,
|
||||
0.740415113911235885228829945155951253966,
|
||||
0.742422582936376250272386395864403155277,
|
||||
0.744435494762198532693663597314273242753,
|
||||
0.746453864145632424600321765743336770838,
|
||||
0.748477705883617713391824861712720862423,
|
||||
0.750507034813212760132561481529764324813,
|
||||
0.752541865811703272039672277899716132493,
|
||||
0.75458221379671136988300977551659676571,
|
||||
0.756628093726304951096818488157633113612,
|
||||
0.75867952059910734940489114658718937343,
|
||||
0.760736509454407291763130627098242426467,
|
||||
0.762799075372269153425626844758470477304,
|
||||
0.76486723347364351194254345936342587308,
|
||||
0.766940998920478000900300751753859329456,
|
||||
0.769020386915828464216738479594307884331,
|
||||
0.771105412703970411806145931045367420652,
|
||||
0.773196091570510777431255778146135325272,
|
||||
0.77529243884249997956151370535341912283,
|
||||
0.777394469888544286059157168801667390437,
|
||||
0.779502200118918483516864044737428940745,
|
||||
0.781615644985678852072965367573877941354,
|
||||
0.783734819982776446532455855478222575498,
|
||||
0.78585974064617068462428149076570281356,
|
||||
0.787990422553943243227635080090952504452,
|
||||
0.790126881326412263402248482007960521995,
|
||||
0.79226913262624686505993407346567890838,
|
||||
0.794417192158581972116898048814333564685,
|
||||
0.796571075671133448968624321559534367934,
|
||||
0.798730798954313549131410147104316569576,
|
||||
0.800896377841346676896923120795476813684,
|
||||
0.803067828208385462848443946517563571584,
|
||||
0.805245165974627154089760333678700291728,
|
||||
0.807428407102430320039984581575729114268,
|
||||
0.809617567597431874649880866726368203972,
|
||||
0.81181266350866441589760797777344082227,
|
||||
0.814013710928673883424109261007007338614,
|
||||
0.816220725993637535170713864466769240053,
|
||||
0.818433724883482243883852017078007231025,
|
||||
0.82065272382200311435413206848451310067,
|
||||
0.822877739076982422259378362362911222833,
|
||||
0.825108786960308875483586738272485101678,
|
||||
0.827345883828097198786118571797909120834,
|
||||
0.829589046080808042697824787210781231927,
|
||||
0.831838290163368217523168228488195222638,
|
||||
0.834093632565291253329796170708536192903,
|
||||
0.836355089820798286809404612069230711295,
|
||||
0.83862267850893927589613232455870870518,
|
||||
0.84089641525371454303112547623321489504,
|
||||
0.84317631672419664796432298771385230143,
|
||||
0.84546239963465259098692866759361830709,
|
||||
0.84775468074466634749045860363936420312,
|
||||
0.850053176859261734750681286748751167545,
|
||||
0.852357904829025611837203530384718316326,
|
||||
0.854668881550231413551897437515331498025,
|
||||
0.856986123964963019301812477839166009452,
|
||||
0.859309649061238957814672188228156252257,
|
||||
0.861639473873136948607517116872358729753,
|
||||
0.863975615480918781121524414614366207052,
|
||||
0.866318091011155532438509953514163469652,
|
||||
0.868666917636853124497101040936083380124,
|
||||
0.871022112577578221729056715595464682243,
|
||||
0.873383693099584470038708278290226842228,
|
||||
0.875751676515939078050995142767930296012,
|
||||
0.878126080186649741556080309687656610647,
|
||||
0.880506921518791912081045787323636256171,
|
||||
0.882894217966636410521691124969260937028,
|
||||
0.885287987031777386769987907431242017412,
|
||||
0.88768824626326062627527960009966160388,
|
||||
0.89009501325771220447985955243623523504,
|
||||
0.892508305659467490072110281986409916153,
|
||||
0.8949281411607004980029443898876582985,
|
||||
0.897354537501553593213851621063890907178,
|
||||
0.899787512470267546027427696662514569756,
|
||||
0.902227083903311940153838631655504844215,
|
||||
0.904673269685515934269259325789226871994,
|
||||
0.907126087750199378124917300181170171233,
|
||||
0.909585556079304284147971563828178746372,
|
||||
0.91205169270352665549806275316460097744,
|
||||
0.914524515702448671545983912696158354092,
|
||||
0.91700404320467123174354159479414442804,
|
||||
0.919490293387946858856304371174663918816,
|
||||
0.921983284479312962533570386670938449637,
|
||||
0.92448303475522546419252726694739603678,
|
||||
0.92698956254169278419622653516884831976,
|
||||
0.929502886214410192307650717745572682403,
|
||||
0.932023024198894522404814545597236289343,
|
||||
0.934549994970619252444512104439799143264,
|
||||
0.93708381705514995066499947497722326722,
|
||||
0.93962450902828008902058735120448448827,
|
||||
0.942172089516167224843810351983745154882,
|
||||
0.944726577195469551733539267378681531548,
|
||||
0.947287990793482820670109326713462307376,
|
||||
0.949856349088277632361251759806996099924,
|
||||
0.952431670908837101825337466217860725517,
|
||||
0.955013975135194896221170529572799135168,
|
||||
0.957603280698573646936305635147915443924,
|
||||
0.960199606581523736948607188887070611744,
|
||||
0.962802971818062464478519115091191368377,
|
||||
0.965413395493813583952272948264534783197,
|
||||
0.968030896746147225299027952283345762418,
|
||||
0.970655494764320192607710617437589705184,
|
||||
0.973287208789616643172102023321302921373,
|
||||
0.97592605811548914795551023340047499377,
|
||||
0.978572062087700134509161125813435745597,
|
||||
0.981225240104463713381244885057070325016,
|
||||
0.983885611616587889056366801238014683926,
|
||||
0.98655319612761715646797006813220671315,
|
||||
0.989228013193975484129124959065583667775,
|
||||
0.99191008242510968492991311132615581644,
|
||||
0.994599423483633175652477686222166314457,
|
||||
0.997296056085470126257659913847922601123,
|
||||
1.0,
|
||||
1.00271127505020248543074558845036204047,
|
||||
1.0054299011128028213513839559347998147,
|
||||
1.008155898118417515783094890817201039276,
|
||||
1.01088928605170046002040979056186052439,
|
||||
1.013630084951489438840258929063939929597,
|
||||
1.01637831491095303794049311378629406276,
|
||||
1.0191339960777379496848780958207928794,
|
||||
1.02189714865411667823448013478329943978,
|
||||
1.02466779289713564514828907627081492763,
|
||||
1.0274459491187636965388611939222137815,
|
||||
1.030231637686041012871707902453904567093,
|
||||
1.033024879021228422500108283970460918086,
|
||||
1.035825693601957120029983209018081371844,
|
||||
1.03863410196137879061243669795463973258,
|
||||
1.04145012468831614126454607901189312648,
|
||||
1.044273782427413840321966478739929008784,
|
||||
1.04710509587928986612990725022711224056,
|
||||
1.04994408580068726608203812651590790906,
|
||||
1.05279077300462632711989120298074630319,
|
||||
1.05564517836055715880834132515293865216,
|
||||
1.058507322794512690105772109683716645074,
|
||||
1.061377227289262080950567678003883726294,
|
||||
1.06425491288446454978861125700158022068,
|
||||
1.06714040067682361816952112099280916261,
|
||||
1.0700337118202417735424119367576235685,
|
||||
1.072934867525975551385035450873827585343,
|
||||
1.075843889062791037803228648476057074063,
|
||||
1.07876079775711979374068003743848295849,
|
||||
1.081685614993215201942115594422531125643,
|
||||
1.08461836221330923781610517190661434161,
|
||||
1.087559060917769665346797830944039707867,
|
||||
1.09050773266525765920701065576070797899,
|
||||
1.09346439907288585422822014625044716208,
|
||||
1.096429081816376823386138295859248481766,
|
||||
1.09940180263022198546369696823882990404,
|
||||
1.10238258330784094355641420942564685751,
|
||||
1.10537144570174125558827469625695031104,
|
||||
1.108368411723678638009423649426619850137,
|
||||
1.111373503344817603850149254228916637444,
|
||||
1.1143867425958925363088129569196030678,
|
||||
1.11740815156736919905457996308578026665,
|
||||
1.12043775240960668442900387986631301277,
|
||||
1.123475567333019800733729739775321431954,
|
||||
1.12652161860824189979479864378703477763,
|
||||
1.129575928566288145997264988840249825907,
|
||||
1.13263851959871922798707372367762308438,
|
||||
1.13570941415780551424039033067611701343,
|
||||
1.13878863475669165370383028384151125472,
|
||||
1.14187620396956162271229760828788093894,
|
||||
1.14497214443180421939441388822291589579,
|
||||
1.14807647884017900677879966269734268003,
|
||||
1.15118922995298270581775963520198253612,
|
||||
1.154310420590216039548221528724806960684,
|
||||
1.157440073633751029613085766293796821106,
|
||||
1.16057821202749874636945947257609098625,
|
||||
1.16372485877757751381357359909218531234,
|
||||
1.166880036952481570555516298414089287834,
|
||||
1.170043769683250188080259035792738573,
|
||||
1.17321608016363724753480435451324538889,
|
||||
1.176396991650281276284645728483848641054,
|
||||
1.17958652746287594548610056676944051898,
|
||||
1.182784710984341029924457204693850757966,
|
||||
1.18599156566099383137126564953421556374,
|
||||
1.18920711500272106671749997056047591529,
|
||||
1.19243138258315122214272755814543101148,
|
||||
1.195664392039827374583837049865451975705,
|
||||
1.19890616707438048177030255797630020695,
|
||||
1.202156731452703142096396957497765876003,
|
||||
1.205416109005123825604211432558411335666,
|
||||
1.208684323626581577354792255889216998484,
|
||||
1.21196139927680119446816891773249304545,
|
||||
1.215247359980468878116520251338798457624,
|
||||
1.218542229827408361758207148117394510724,
|
||||
1.221846032972757516903891841911570785836,
|
||||
1.225158793637145437709464594384845353707,
|
||||
1.22848053610687000569400895779278184036,
|
||||
1.2318112847340759358845566532127948166,
|
||||
1.235151063936933305692912507415415760294,
|
||||
1.238499898199816567833368865859612431545,
|
||||
1.24185781207348404859367746872659560551,
|
||||
1.24522483017525793277520496748615267417,
|
||||
1.24860097718920473662176609730249554519,
|
||||
1.25198627786631627006020603178920359732,
|
||||
1.255380757024691089579390657442301194595,
|
||||
1.25878443954971644307786044181516261876,
|
||||
1.26219735039425070801401025851841645967,
|
||||
1.265619514578806324196273999873453036296,
|
||||
1.26905095719173322255441908103233800472,
|
||||
1.27249170338940275123669204418460217677,
|
||||
1.27594177839639210038120243475928938891,
|
||||
1.27940120750566922691358797002785254596,
|
||||
1.28287001607877828072666978102151405111,
|
||||
1.286348229546025533601482208069738348355,
|
||||
1.28983587340666581223274729549155218968,
|
||||
1.293332973229089436725559789048704304684,
|
||||
1.296839554651009665933754117792451159835,
|
||||
1.30035564337965065101414056707091779129,
|
||||
1.30388126519193589857452364895199736833,
|
||||
1.30741644593467724479715157747196172848,
|
||||
1.310961211524764341922991786330755849366,
|
||||
1.314515587949354658485983613383997794965,
|
||||
1.318079601266063994690185647066116617664,
|
||||
1.32165327760315751432651181233060922616,
|
||||
1.32523664315974129462953709549872167411,
|
||||
1.32882972420595439547865089632866510792,
|
||||
1.33243254708316144935164337949073577407,
|
||||
1.33604513820414577344262790437186975929,
|
||||
1.33966752405330300536003066972435257602,
|
||||
1.34329973118683526382421714618163087542,
|
||||
1.346941786232945835788173713229537282075,
|
||||
1.35059371589203439140852219606013396004,
|
||||
1.35425554693689272829801474014070280434,
|
||||
1.357927306212901046494536695671766697446,
|
||||
1.36160902063822475558553593883194147464,
|
||||
1.36530071720401181543069836033754285543,
|
||||
1.36900242297459061192960113298219283217,
|
||||
1.37271416508766836928499785714471721579,
|
||||
1.37643597075453010021632280551868696026,
|
||||
1.380167867260238095581945274358283464697,
|
||||
1.383909881963831954872659527265192818,
|
||||
1.387662042298529159042861017950775988896,
|
||||
1.39142437577192618714983552956624344668,
|
||||
1.395196909966200178275574599249220994716,
|
||||
1.398979672538311140209528136715194969206,
|
||||
1.40277269122020470637471352433337881711,
|
||||
1.40657599381901544248361973255451684411,
|
||||
1.410389608217270704414375128268675481145,
|
||||
1.41421356237309504880168872420969807857
|
||||
};
|
||||
/* exp_table[i] = exp((i - 128) * log(2)/256).
|
||||
Computed in GNU clisp through
|
||||
(setf (long-float-digits) 128)
|
||||
(setq a 0L0)
|
||||
(setf (long-float-digits) 256)
|
||||
(dotimes (i 257)
|
||||
(format t " ~D,~%"
|
||||
(float (exp (* (/ (- i 128) 256) (log 2L0))) a))) */
|
||||
static const double exp_table[257] = {
|
||||
0.707106781186547524400844362104849039284,
|
||||
0.709023942160207598920563322257676190836,
|
||||
0.710946301084582779904674297352120049962,
|
||||
0.71287387205274715340350157671438300618,
|
||||
0.714806669195985005617532889137569953044,
|
||||
0.71674470668389442125974978427737336719,
|
||||
0.71868799872449116280161304224785251353,
|
||||
0.720636559564312831364255957304947586072,
|
||||
0.72259040348852331001850312073583545284,
|
||||
0.724549544821017490259402705487111270714,
|
||||
0.726513997924526282423036245842287293786,
|
||||
0.728483777200721910815451524818606761737,
|
||||
0.730458897090323494325651445155310766577,
|
||||
0.732439372073202913296664682112279175616,
|
||||
0.734425216668490963430822513132890712652,
|
||||
0.736416445434683797507470506133110286942,
|
||||
0.738413072969749655693453740187024961962,
|
||||
0.740415113911235885228829945155951253966,
|
||||
0.742422582936376250272386395864403155277,
|
||||
0.744435494762198532693663597314273242753,
|
||||
0.746453864145632424600321765743336770838,
|
||||
0.748477705883617713391824861712720862423,
|
||||
0.750507034813212760132561481529764324813,
|
||||
0.752541865811703272039672277899716132493,
|
||||
0.75458221379671136988300977551659676571,
|
||||
0.756628093726304951096818488157633113612,
|
||||
0.75867952059910734940489114658718937343,
|
||||
0.760736509454407291763130627098242426467,
|
||||
0.762799075372269153425626844758470477304,
|
||||
0.76486723347364351194254345936342587308,
|
||||
0.766940998920478000900300751753859329456,
|
||||
0.769020386915828464216738479594307884331,
|
||||
0.771105412703970411806145931045367420652,
|
||||
0.773196091570510777431255778146135325272,
|
||||
0.77529243884249997956151370535341912283,
|
||||
0.777394469888544286059157168801667390437,
|
||||
0.779502200118918483516864044737428940745,
|
||||
0.781615644985678852072965367573877941354,
|
||||
0.783734819982776446532455855478222575498,
|
||||
0.78585974064617068462428149076570281356,
|
||||
0.787990422553943243227635080090952504452,
|
||||
0.790126881326412263402248482007960521995,
|
||||
0.79226913262624686505993407346567890838,
|
||||
0.794417192158581972116898048814333564685,
|
||||
0.796571075671133448968624321559534367934,
|
||||
0.798730798954313549131410147104316569576,
|
||||
0.800896377841346676896923120795476813684,
|
||||
0.803067828208385462848443946517563571584,
|
||||
0.805245165974627154089760333678700291728,
|
||||
0.807428407102430320039984581575729114268,
|
||||
0.809617567597431874649880866726368203972,
|
||||
0.81181266350866441589760797777344082227,
|
||||
0.814013710928673883424109261007007338614,
|
||||
0.816220725993637535170713864466769240053,
|
||||
0.818433724883482243883852017078007231025,
|
||||
0.82065272382200311435413206848451310067,
|
||||
0.822877739076982422259378362362911222833,
|
||||
0.825108786960308875483586738272485101678,
|
||||
0.827345883828097198786118571797909120834,
|
||||
0.829589046080808042697824787210781231927,
|
||||
0.831838290163368217523168228488195222638,
|
||||
0.834093632565291253329796170708536192903,
|
||||
0.836355089820798286809404612069230711295,
|
||||
0.83862267850893927589613232455870870518,
|
||||
0.84089641525371454303112547623321489504,
|
||||
0.84317631672419664796432298771385230143,
|
||||
0.84546239963465259098692866759361830709,
|
||||
0.84775468074466634749045860363936420312,
|
||||
0.850053176859261734750681286748751167545,
|
||||
0.852357904829025611837203530384718316326,
|
||||
0.854668881550231413551897437515331498025,
|
||||
0.856986123964963019301812477839166009452,
|
||||
0.859309649061238957814672188228156252257,
|
||||
0.861639473873136948607517116872358729753,
|
||||
0.863975615480918781121524414614366207052,
|
||||
0.866318091011155532438509953514163469652,
|
||||
0.868666917636853124497101040936083380124,
|
||||
0.871022112577578221729056715595464682243,
|
||||
0.873383693099584470038708278290226842228,
|
||||
0.875751676515939078050995142767930296012,
|
||||
0.878126080186649741556080309687656610647,
|
||||
0.880506921518791912081045787323636256171,
|
||||
0.882894217966636410521691124969260937028,
|
||||
0.885287987031777386769987907431242017412,
|
||||
0.88768824626326062627527960009966160388,
|
||||
0.89009501325771220447985955243623523504,
|
||||
0.892508305659467490072110281986409916153,
|
||||
0.8949281411607004980029443898876582985,
|
||||
0.897354537501553593213851621063890907178,
|
||||
0.899787512470267546027427696662514569756,
|
||||
0.902227083903311940153838631655504844215,
|
||||
0.904673269685515934269259325789226871994,
|
||||
0.907126087750199378124917300181170171233,
|
||||
0.909585556079304284147971563828178746372,
|
||||
0.91205169270352665549806275316460097744,
|
||||
0.914524515702448671545983912696158354092,
|
||||
0.91700404320467123174354159479414442804,
|
||||
0.919490293387946858856304371174663918816,
|
||||
0.921983284479312962533570386670938449637,
|
||||
0.92448303475522546419252726694739603678,
|
||||
0.92698956254169278419622653516884831976,
|
||||
0.929502886214410192307650717745572682403,
|
||||
0.932023024198894522404814545597236289343,
|
||||
0.934549994970619252444512104439799143264,
|
||||
0.93708381705514995066499947497722326722,
|
||||
0.93962450902828008902058735120448448827,
|
||||
0.942172089516167224843810351983745154882,
|
||||
0.944726577195469551733539267378681531548,
|
||||
0.947287990793482820670109326713462307376,
|
||||
0.949856349088277632361251759806996099924,
|
||||
0.952431670908837101825337466217860725517,
|
||||
0.955013975135194896221170529572799135168,
|
||||
0.957603280698573646936305635147915443924,
|
||||
0.960199606581523736948607188887070611744,
|
||||
0.962802971818062464478519115091191368377,
|
||||
0.965413395493813583952272948264534783197,
|
||||
0.968030896746147225299027952283345762418,
|
||||
0.970655494764320192607710617437589705184,
|
||||
0.973287208789616643172102023321302921373,
|
||||
0.97592605811548914795551023340047499377,
|
||||
0.978572062087700134509161125813435745597,
|
||||
0.981225240104463713381244885057070325016,
|
||||
0.983885611616587889056366801238014683926,
|
||||
0.98655319612761715646797006813220671315,
|
||||
0.989228013193975484129124959065583667775,
|
||||
0.99191008242510968492991311132615581644,
|
||||
0.994599423483633175652477686222166314457,
|
||||
0.997296056085470126257659913847922601123,
|
||||
1.0,
|
||||
1.00271127505020248543074558845036204047,
|
||||
1.0054299011128028213513839559347998147,
|
||||
1.008155898118417515783094890817201039276,
|
||||
1.01088928605170046002040979056186052439,
|
||||
1.013630084951489438840258929063939929597,
|
||||
1.01637831491095303794049311378629406276,
|
||||
1.0191339960777379496848780958207928794,
|
||||
1.02189714865411667823448013478329943978,
|
||||
1.02466779289713564514828907627081492763,
|
||||
1.0274459491187636965388611939222137815,
|
||||
1.030231637686041012871707902453904567093,
|
||||
1.033024879021228422500108283970460918086,
|
||||
1.035825693601957120029983209018081371844,
|
||||
1.03863410196137879061243669795463973258,
|
||||
1.04145012468831614126454607901189312648,
|
||||
1.044273782427413840321966478739929008784,
|
||||
1.04710509587928986612990725022711224056,
|
||||
1.04994408580068726608203812651590790906,
|
||||
1.05279077300462632711989120298074630319,
|
||||
1.05564517836055715880834132515293865216,
|
||||
1.058507322794512690105772109683716645074,
|
||||
1.061377227289262080950567678003883726294,
|
||||
1.06425491288446454978861125700158022068,
|
||||
1.06714040067682361816952112099280916261,
|
||||
1.0700337118202417735424119367576235685,
|
||||
1.072934867525975551385035450873827585343,
|
||||
1.075843889062791037803228648476057074063,
|
||||
1.07876079775711979374068003743848295849,
|
||||
1.081685614993215201942115594422531125643,
|
||||
1.08461836221330923781610517190661434161,
|
||||
1.087559060917769665346797830944039707867,
|
||||
1.09050773266525765920701065576070797899,
|
||||
1.09346439907288585422822014625044716208,
|
||||
1.096429081816376823386138295859248481766,
|
||||
1.09940180263022198546369696823882990404,
|
||||
1.10238258330784094355641420942564685751,
|
||||
1.10537144570174125558827469625695031104,
|
||||
1.108368411723678638009423649426619850137,
|
||||
1.111373503344817603850149254228916637444,
|
||||
1.1143867425958925363088129569196030678,
|
||||
1.11740815156736919905457996308578026665,
|
||||
1.12043775240960668442900387986631301277,
|
||||
1.123475567333019800733729739775321431954,
|
||||
1.12652161860824189979479864378703477763,
|
||||
1.129575928566288145997264988840249825907,
|
||||
1.13263851959871922798707372367762308438,
|
||||
1.13570941415780551424039033067611701343,
|
||||
1.13878863475669165370383028384151125472,
|
||||
1.14187620396956162271229760828788093894,
|
||||
1.14497214443180421939441388822291589579,
|
||||
1.14807647884017900677879966269734268003,
|
||||
1.15118922995298270581775963520198253612,
|
||||
1.154310420590216039548221528724806960684,
|
||||
1.157440073633751029613085766293796821106,
|
||||
1.16057821202749874636945947257609098625,
|
||||
1.16372485877757751381357359909218531234,
|
||||
1.166880036952481570555516298414089287834,
|
||||
1.170043769683250188080259035792738573,
|
||||
1.17321608016363724753480435451324538889,
|
||||
1.176396991650281276284645728483848641054,
|
||||
1.17958652746287594548610056676944051898,
|
||||
1.182784710984341029924457204693850757966,
|
||||
1.18599156566099383137126564953421556374,
|
||||
1.18920711500272106671749997056047591529,
|
||||
1.19243138258315122214272755814543101148,
|
||||
1.195664392039827374583837049865451975705,
|
||||
1.19890616707438048177030255797630020695,
|
||||
1.202156731452703142096396957497765876003,
|
||||
1.205416109005123825604211432558411335666,
|
||||
1.208684323626581577354792255889216998484,
|
||||
1.21196139927680119446816891773249304545,
|
||||
1.215247359980468878116520251338798457624,
|
||||
1.218542229827408361758207148117394510724,
|
||||
1.221846032972757516903891841911570785836,
|
||||
1.225158793637145437709464594384845353707,
|
||||
1.22848053610687000569400895779278184036,
|
||||
1.2318112847340759358845566532127948166,
|
||||
1.235151063936933305692912507415415760294,
|
||||
1.238499898199816567833368865859612431545,
|
||||
1.24185781207348404859367746872659560551,
|
||||
1.24522483017525793277520496748615267417,
|
||||
1.24860097718920473662176609730249554519,
|
||||
1.25198627786631627006020603178920359732,
|
||||
1.255380757024691089579390657442301194595,
|
||||
1.25878443954971644307786044181516261876,
|
||||
1.26219735039425070801401025851841645967,
|
||||
1.265619514578806324196273999873453036296,
|
||||
1.26905095719173322255441908103233800472,
|
||||
1.27249170338940275123669204418460217677,
|
||||
1.27594177839639210038120243475928938891,
|
||||
1.27940120750566922691358797002785254596,
|
||||
1.28287001607877828072666978102151405111,
|
||||
1.286348229546025533601482208069738348355,
|
||||
1.28983587340666581223274729549155218968,
|
||||
1.293332973229089436725559789048704304684,
|
||||
1.296839554651009665933754117792451159835,
|
||||
1.30035564337965065101414056707091779129,
|
||||
1.30388126519193589857452364895199736833,
|
||||
1.30741644593467724479715157747196172848,
|
||||
1.310961211524764341922991786330755849366,
|
||||
1.314515587949354658485983613383997794965,
|
||||
1.318079601266063994690185647066116617664,
|
||||
1.32165327760315751432651181233060922616,
|
||||
1.32523664315974129462953709549872167411,
|
||||
1.32882972420595439547865089632866510792,
|
||||
1.33243254708316144935164337949073577407,
|
||||
1.33604513820414577344262790437186975929,
|
||||
1.33966752405330300536003066972435257602,
|
||||
1.34329973118683526382421714618163087542,
|
||||
1.346941786232945835788173713229537282075,
|
||||
1.35059371589203439140852219606013396004,
|
||||
1.35425554693689272829801474014070280434,
|
||||
1.357927306212901046494536695671766697446,
|
||||
1.36160902063822475558553593883194147464,
|
||||
1.36530071720401181543069836033754285543,
|
||||
1.36900242297459061192960113298219283217,
|
||||
1.37271416508766836928499785714471721579,
|
||||
1.37643597075453010021632280551868696026,
|
||||
1.380167867260238095581945274358283464697,
|
||||
1.383909881963831954872659527265192818,
|
||||
1.387662042298529159042861017950775988896,
|
||||
1.39142437577192618714983552956624344668,
|
||||
1.395196909966200178275574599249220994716,
|
||||
1.398979672538311140209528136715194969206,
|
||||
1.40277269122020470637471352433337881711,
|
||||
1.40657599381901544248361973255451684411,
|
||||
1.410389608217270704414375128268675481145,
|
||||
1.41421356237309504880168872420969807857};
|
||||
|
||||
double ret = exp_table[128 + m] * exp_y;
|
||||
for (int i = 0; i < n; i++)
|
||||
ret *= 2;
|
||||
return ret;
|
||||
}
|
||||
double ret = exp_table[128 + m] * exp_y;
|
||||
for (int i = 0; i < n; i++)
|
||||
ret *= 2;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Round toward nearest, breaking ties away from zero.
|
||||
|
@ -416,84 +409,76 @@ oxen::exp2(double x)
|
|||
|
||||
/* Specification. */
|
||||
|
||||
#include <cstdint>
|
||||
#include <cfloat>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
/* -0.0. See minus-zero.h. */
|
||||
#if defined __hpux || defined __sgi || defined __ICC
|
||||
# define MINUS_ZERO (-DBL_MIN * DBL_MIN)
|
||||
#define MINUS_ZERO (-DBL_MIN * DBL_MIN)
|
||||
#else
|
||||
# define MINUS_ZERO -0.0
|
||||
#define MINUS_ZERO -0.0
|
||||
#endif
|
||||
|
||||
/* MSVC with option -fp:strict refuses to compile constant initializers that
|
||||
contain floating-point operations. Pacify this compiler. */
|
||||
#ifdef _MSC_VER
|
||||
# pragma fenv_access (off)
|
||||
#pragma fenv_access(off)
|
||||
#endif
|
||||
|
||||
double
|
||||
oxen::round (double x)
|
||||
{
|
||||
/* 2^(DBL_MANT_DIG-1). */
|
||||
static const double TWO_MANT_DIG =
|
||||
/* Assume DBL_MANT_DIG <= 5 * 31.
|
||||
Use the identity
|
||||
n = floor(n/5) + floor((n+1)/5) + ... + floor((n+4)/5). */
|
||||
(double) (1U << ((DBL_MANT_DIG - 1) / 5))
|
||||
* (double) (1U << ((DBL_MANT_DIG - 1 + 1) / 5))
|
||||
* (double) (1U << ((DBL_MANT_DIG - 1 + 2) / 5))
|
||||
* (double) (1U << ((DBL_MANT_DIG - 1 + 3) / 5))
|
||||
* (double) (1U << ((DBL_MANT_DIG - 1 + 4) / 5));
|
||||
double oxen::round(double x) {
|
||||
/* 2^(DBL_MANT_DIG-1). */
|
||||
static const double TWO_MANT_DIG =
|
||||
/* Assume DBL_MANT_DIG <= 5 * 31.
|
||||
Use the identity
|
||||
n = floor(n/5) + floor((n+1)/5) + ... + floor((n+4)/5). */
|
||||
(double)(1U << ((DBL_MANT_DIG - 1) / 5)) *
|
||||
(double)(1U << ((DBL_MANT_DIG - 1 + 1) / 5)) *
|
||||
(double)(1U << ((DBL_MANT_DIG - 1 + 2) / 5)) *
|
||||
(double)(1U << ((DBL_MANT_DIG - 1 + 3) / 5)) *
|
||||
(double)(1U << ((DBL_MANT_DIG - 1 + 4) / 5));
|
||||
|
||||
/* The use of 'volatile' guarantees that excess precision bits are dropped at
|
||||
each addition step and before the following comparison at the caller's
|
||||
site. It is necessary on x86 systems where double-floats are not IEEE
|
||||
compliant by default, to avoid that the results become platform and
|
||||
compiler option dependent. 'volatile' is a portable alternative to gcc's
|
||||
-ffloat-store option. */
|
||||
volatile double y = x;
|
||||
volatile double z = y;
|
||||
/* The use of 'volatile' guarantees that excess precision bits are dropped at
|
||||
each addition step and before the following comparison at the caller's
|
||||
site. It is necessary on x86 systems where double-floats are not IEEE
|
||||
compliant by default, to avoid that the results become platform and
|
||||
compiler option dependent. 'volatile' is a portable alternative to gcc's
|
||||
-ffloat-store option. */
|
||||
volatile double y = x;
|
||||
volatile double z = y;
|
||||
|
||||
if (z > 0.0)
|
||||
{
|
||||
/* Avoid rounding error for x = 0.5 - 2^(-DBL_MANT_DIG-1). */
|
||||
if (z < 0.5)
|
||||
z = 0.0;
|
||||
/* Avoid rounding errors for values near 2^k, where k >= DBL_MANT_DIG-1. */
|
||||
else if (z < TWO_MANT_DIG)
|
||||
{
|
||||
/* Add 0.5 to the absolute value. */
|
||||
y = z += 0.5;
|
||||
/* Round to the next integer (nearest or up or down, doesn't
|
||||
matter). */
|
||||
z += TWO_MANT_DIG;
|
||||
z -= TWO_MANT_DIG;
|
||||
/* Enforce rounding down. */
|
||||
if (z > y)
|
||||
z -= 1.0;
|
||||
if (z > 0.0) {
|
||||
/* Avoid rounding error for x = 0.5 - 2^(-DBL_MANT_DIG-1). */
|
||||
if (z < 0.5)
|
||||
z = 0.0;
|
||||
/* Avoid rounding errors for values near 2^k, where k >= DBL_MANT_DIG-1. */
|
||||
else if (z < TWO_MANT_DIG) {
|
||||
/* Add 0.5 to the absolute value. */
|
||||
y = z += 0.5;
|
||||
/* Round to the next integer (nearest or up or down, doesn't
|
||||
matter). */
|
||||
z += TWO_MANT_DIG;
|
||||
z -= TWO_MANT_DIG;
|
||||
/* Enforce rounding down. */
|
||||
if (z > y)
|
||||
z -= 1.0;
|
||||
}
|
||||
} else if (z < 0.0) {
|
||||
/* Avoid rounding error for x = -(0.5 - 2^(-DBL_MANT_DIG-1)). */
|
||||
if (z > -0.5)
|
||||
z = MINUS_ZERO;
|
||||
/* Avoid rounding errors for values near -2^k, where k >= DBL_MANT_DIG-1. */
|
||||
else if (z > -TWO_MANT_DIG) {
|
||||
/* Add 0.5 to the absolute value. */
|
||||
y = z -= 0.5;
|
||||
/* Round to the next integer (nearest or up or down, doesn't
|
||||
matter). */
|
||||
z -= TWO_MANT_DIG;
|
||||
z += TWO_MANT_DIG;
|
||||
/* Enforce rounding up. */
|
||||
if (z < y)
|
||||
z += 1.0;
|
||||
}
|
||||
}
|
||||
else if (z < 0.0)
|
||||
{
|
||||
/* Avoid rounding error for x = -(0.5 - 2^(-DBL_MANT_DIG-1)). */
|
||||
if (z > - 0.5)
|
||||
z = MINUS_ZERO;
|
||||
/* Avoid rounding errors for values near -2^k, where k >= DBL_MANT_DIG-1. */
|
||||
else if (z > -TWO_MANT_DIG)
|
||||
{
|
||||
/* Add 0.5 to the absolute value. */
|
||||
y = z -= 0.5;
|
||||
/* Round to the next integer (nearest or up or down, doesn't
|
||||
matter). */
|
||||
z -= TWO_MANT_DIG;
|
||||
z += TWO_MANT_DIG;
|
||||
/* Enforce rounding up. */
|
||||
if (z < y)
|
||||
z += 1.0;
|
||||
}
|
||||
}
|
||||
return z;
|
||||
return z;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2018, The Loki Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -33,49 +33,68 @@
|
|||
#include <utility>
|
||||
|
||||
#define OXEN_RPC_DOC_INTROSPECT
|
||||
namespace oxen
|
||||
{
|
||||
double round (double);
|
||||
double exp2 (double);
|
||||
namespace oxen {
|
||||
double round(double);
|
||||
double exp2(double);
|
||||
|
||||
template <typename lambda_t>
|
||||
struct deferred
|
||||
{
|
||||
private:
|
||||
lambda_t lambda;
|
||||
bool cancelled = false;
|
||||
public:
|
||||
deferred(lambda_t lambda) : lambda(lambda) {}
|
||||
void invoke() { lambda(); cancelled = true; } // Invoke early instead of at destruction
|
||||
void cancel() { cancelled = true; } // Cancel invocation at destruction
|
||||
~deferred() { if (!cancelled) lambda(); }
|
||||
struct deferred {
|
||||
private:
|
||||
lambda_t lambda;
|
||||
bool cancelled = false;
|
||||
|
||||
deferred(deferred<lambda_t>&& d) : lambda{std::move(d.lambda)}, cancelled{d.cancelled} { d.cancel(); }
|
||||
deferred& operator=(deferred<lambda_t>&& d) { lambda = std::move(d.lambda); cancelled = d.cancelled; d.cancel(); return *this; }
|
||||
deferred(const deferred<lambda_t>&) = delete;
|
||||
deferred& operator=(const deferred<lambda_t>&) = delete;
|
||||
public:
|
||||
deferred(lambda_t lambda) : lambda(lambda) {}
|
||||
void invoke() {
|
||||
lambda();
|
||||
cancelled = true;
|
||||
} // Invoke early instead of at destruction
|
||||
void cancel() { cancelled = true; } // Cancel invocation at destruction
|
||||
~deferred() {
|
||||
if (!cancelled)
|
||||
lambda();
|
||||
}
|
||||
|
||||
deferred(deferred<lambda_t>&& d) : lambda{std::move(d.lambda)}, cancelled{d.cancelled} {
|
||||
d.cancel();
|
||||
}
|
||||
deferred& operator=(deferred<lambda_t>&& d) {
|
||||
lambda = std::move(d.lambda);
|
||||
cancelled = d.cancelled;
|
||||
d.cancel();
|
||||
return *this;
|
||||
}
|
||||
deferred(const deferred<lambda_t>&) = delete;
|
||||
deferred& operator=(const deferred<lambda_t>&) = delete;
|
||||
};
|
||||
|
||||
template <typename lambda_t>
|
||||
[[nodiscard]]
|
||||
deferred<lambda_t> defer(lambda_t lambda) { return lambda; }
|
||||
[[nodiscard]] deferred<lambda_t> defer(lambda_t lambda) {
|
||||
return lambda;
|
||||
}
|
||||
|
||||
struct defer_helper
|
||||
{
|
||||
template <typename lambda_t>
|
||||
deferred<lambda_t> operator+(lambda_t lambda) { return lambda; }
|
||||
struct defer_helper {
|
||||
template <typename lambda_t>
|
||||
deferred<lambda_t> operator+(lambda_t lambda) {
|
||||
return lambda;
|
||||
}
|
||||
};
|
||||
|
||||
#define OXEN_TOKEN_COMBINE2(x, y) x ## y
|
||||
#define OXEN_TOKEN_COMBINE2(x, y) x##y
|
||||
#define OXEN_TOKEN_COMBINE(x, y) OXEN_TOKEN_COMBINE2(x, y)
|
||||
#define OXEN_DEFER auto const OXEN_TOKEN_COMBINE(oxen_defer_, __LINE__) = oxen::defer_helper() + [&]()
|
||||
#define OXEN_DEFER \
|
||||
auto const OXEN_TOKEN_COMBINE(oxen_defer_, __LINE__) = oxen::defer_helper() + [&]()
|
||||
|
||||
template <typename T, size_t N>
|
||||
constexpr size_t array_count(T (&)[N]) { return N; }
|
||||
constexpr size_t array_count(T (&)[N]) {
|
||||
return N;
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
constexpr size_t char_count(T (&)[N]) { return N - 1; }
|
||||
constexpr size_t char_count(T (&)[N]) {
|
||||
return N - 1;
|
||||
}
|
||||
|
||||
}; // namespace Oxen
|
||||
}; // namespace oxen
|
||||
|
||||
#endif // OXEN_H
|
||||
#endif // OXEN_H
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -25,15 +25,15 @@
|
|||
// 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.
|
||||
//
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#include "password.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <io.h>
|
||||
|
@ -45,16 +45,13 @@
|
|||
|
||||
#define EOT 0x4
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace {
|
||||
#if defined(_WIN32)
|
||||
bool is_cin_tty() noexcept
|
||||
{
|
||||
bool is_cin_tty() noexcept {
|
||||
return 0 != _isatty(_fileno(stdin));
|
||||
}
|
||||
}
|
||||
|
||||
bool read_from_tty(epee::wipeable_string& pass, bool hide_input)
|
||||
{
|
||||
bool read_from_tty(epee::wipeable_string& pass, bool hide_input) {
|
||||
HANDLE h_cin = ::GetStdHandle(STD_INPUT_HANDLE);
|
||||
|
||||
DWORD mode_old;
|
||||
|
@ -66,60 +63,52 @@ namespace
|
|||
pass.reserve(tools::password_container::max_password_size);
|
||||
std::vector<int> chlen;
|
||||
chlen.reserve(tools::password_container::max_password_size);
|
||||
while (pass.size() < tools::password_container::max_password_size)
|
||||
{
|
||||
DWORD read;
|
||||
wchar_t ucs2_ch;
|
||||
r = (TRUE == ::ReadConsoleW(h_cin, &ucs2_ch, 1, &read, NULL));
|
||||
r &= (1 == read);
|
||||
while (pass.size() < tools::password_container::max_password_size) {
|
||||
DWORD read;
|
||||
wchar_t ucs2_ch;
|
||||
r = (TRUE == ::ReadConsoleW(h_cin, &ucs2_ch, 1, &read, NULL));
|
||||
r &= (1 == read);
|
||||
|
||||
if (!r)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (ucs2_ch == L'\r')
|
||||
{
|
||||
std::cout << std::endl;
|
||||
break;
|
||||
}
|
||||
else if (ucs2_ch == L'\b')
|
||||
{
|
||||
if (!pass.empty())
|
||||
{
|
||||
int len = chlen.back();
|
||||
chlen.pop_back();
|
||||
while(len-- > 0)
|
||||
pass.pop_back();
|
||||
if (!r) {
|
||||
break;
|
||||
} else if (ucs2_ch == L'\r') {
|
||||
std::cout << std::endl;
|
||||
break;
|
||||
} else if (ucs2_ch == L'\b') {
|
||||
if (!pass.empty()) {
|
||||
int len = chlen.back();
|
||||
chlen.pop_back();
|
||||
while (len-- > 0)
|
||||
pass.pop_back();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
char utf8_ch[8] = {0};
|
||||
int len;
|
||||
if((len = WideCharToMultiByte(CP_UTF8, 0, &ucs2_ch, 1, utf8_ch, sizeof(utf8_ch), NULL, NULL)) <= 0)
|
||||
break;
|
||||
|
||||
if(pass.size() + len >= tools::password_container::max_password_size)
|
||||
break;
|
||||
char utf8_ch[8] = {0};
|
||||
int len;
|
||||
if ((len = WideCharToMultiByte(
|
||||
CP_UTF8, 0, &ucs2_ch, 1, utf8_ch, sizeof(utf8_ch), NULL, NULL)) <= 0)
|
||||
break;
|
||||
|
||||
chlen.push_back(len);
|
||||
pass += utf8_ch;
|
||||
if (pass.size() + len >= tools::password_container::max_password_size)
|
||||
break;
|
||||
|
||||
chlen.push_back(len);
|
||||
pass += utf8_ch;
|
||||
}
|
||||
|
||||
::SetConsoleMode(h_cin, mode_old);
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
#else // end WIN32
|
||||
#else // end WIN32
|
||||
|
||||
bool is_cin_tty() noexcept
|
||||
{
|
||||
bool is_cin_tty() noexcept {
|
||||
return 0 != isatty(fileno(stdin));
|
||||
}
|
||||
}
|
||||
|
||||
int getch(bool hide_input) noexcept
|
||||
{
|
||||
int getch(bool hide_input) noexcept {
|
||||
struct termios tty_old;
|
||||
tcgetattr(STDIN_FILENO, &tty_old);
|
||||
|
||||
|
@ -133,150 +122,127 @@ namespace
|
|||
tcsetattr(STDIN_FILENO, TCSANOW, &tty_old);
|
||||
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
|
||||
bool read_from_tty(epee::wipeable_string& aPass, bool hide_input)
|
||||
{
|
||||
bool read_from_tty(epee::wipeable_string& aPass, bool hide_input) {
|
||||
static constexpr const char BACKSPACE = 127;
|
||||
|
||||
aPass.reserve(tools::password_container::max_password_size);
|
||||
while (aPass.size() < tools::password_container::max_password_size)
|
||||
{
|
||||
int ch = getch(hide_input);
|
||||
if (EOF == ch || ch == EOT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (ch == '\n' || ch == '\r')
|
||||
{
|
||||
std::cout << std::endl;
|
||||
break;
|
||||
}
|
||||
else if (ch == BACKSPACE)
|
||||
{
|
||||
if (!aPass.empty())
|
||||
{
|
||||
aPass.pop_back();
|
||||
if (!hide_input)
|
||||
std::cout << "\b\b\b \b\b\b" << std::flush;
|
||||
while (aPass.size() < tools::password_container::max_password_size) {
|
||||
int ch = getch(hide_input);
|
||||
if (EOF == ch || ch == EOT) {
|
||||
return false;
|
||||
} else if (ch == '\n' || ch == '\r') {
|
||||
std::cout << std::endl;
|
||||
break;
|
||||
} else if (ch == BACKSPACE) {
|
||||
if (!aPass.empty()) {
|
||||
aPass.pop_back();
|
||||
if (!hide_input)
|
||||
std::cout << "\b\b\b \b\b\b" << std::flush;
|
||||
} else {
|
||||
if (!hide_input)
|
||||
std::cout << "\b\b \b\b" << std::flush;
|
||||
}
|
||||
} else {
|
||||
aPass.push_back(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!hide_input)
|
||||
std::cout << "\b\b \b\b" << std::flush;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
aPass.push_back(ch);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // end !WIN32
|
||||
#endif // end !WIN32
|
||||
|
||||
bool read_from_tty(const bool verify, const char *message, bool hide_input, epee::wipeable_string& pass1, epee::wipeable_string& pass2)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (message)
|
||||
std::cout << message <<": " << std::flush;
|
||||
if (!read_from_tty(pass1, hide_input))
|
||||
return false;
|
||||
if (verify)
|
||||
{
|
||||
std::cout << "Confirm password: ";
|
||||
if (!read_from_tty(pass2, hide_input))
|
||||
return false;
|
||||
if(pass1!=pass2)
|
||||
{
|
||||
std::cout << "Passwords do not match! Please try again." << std::endl;
|
||||
pass1.clear();
|
||||
pass2.clear();
|
||||
}
|
||||
else //new password matches
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
//No need to verify password entered at this point in the code
|
||||
bool read_from_tty(
|
||||
const bool verify,
|
||||
const char* message,
|
||||
bool hide_input,
|
||||
epee::wipeable_string& pass1,
|
||||
epee::wipeable_string& pass2) {
|
||||
while (true) {
|
||||
if (message)
|
||||
std::cout << message << ": " << std::flush;
|
||||
if (!read_from_tty(pass1, hide_input))
|
||||
return false;
|
||||
if (verify) {
|
||||
std::cout << "Confirm password: ";
|
||||
if (!read_from_tty(pass2, hide_input))
|
||||
return false;
|
||||
if (pass1 != pass2) {
|
||||
std::cout << "Passwords do not match! Please try again." << std::endl;
|
||||
pass1.clear();
|
||||
pass2.clear();
|
||||
} else // new password matches
|
||||
return true;
|
||||
} else
|
||||
return true;
|
||||
// No need to verify password entered at this point in the code
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool read_from_file(epee::wipeable_string& pass)
|
||||
{
|
||||
bool read_from_file(epee::wipeable_string& pass) {
|
||||
pass.reserve(tools::password_container::max_password_size);
|
||||
for (size_t i = 0; i < tools::password_container::max_password_size; ++i)
|
||||
{
|
||||
char ch = static_cast<char>(std::cin.get());
|
||||
if (std::cin.eof() || ch == '\n' || ch == '\r')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (std::cin.fail())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
pass.push_back(ch);
|
||||
}
|
||||
for (size_t i = 0; i < tools::password_container::max_password_size; ++i) {
|
||||
char ch = static_cast<char>(std::cin.get());
|
||||
if (std::cin.eof() || ch == '\n' || ch == '\r') {
|
||||
break;
|
||||
} else if (std::cin.fail()) {
|
||||
return false;
|
||||
} else {
|
||||
pass.push_back(ch);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
} // anonymous namespace
|
||||
|
||||
namespace tools
|
||||
{
|
||||
// deleted via private member
|
||||
password_container::password_container() noexcept : m_password() {}
|
||||
password_container::password_container(epee::wipeable_string password) noexcept
|
||||
: m_password{std::move(password)}
|
||||
{
|
||||
}
|
||||
namespace tools {
|
||||
// deleted via private member
|
||||
password_container::password_container() noexcept : m_password() {}
|
||||
password_container::password_container(epee::wipeable_string password) noexcept :
|
||||
m_password{std::move(password)} {}
|
||||
|
||||
std::atomic<bool> password_container::is_prompting(false);
|
||||
std::atomic<bool> password_container::is_prompting(false);
|
||||
|
||||
std::optional<password_container> password_container::prompt(const bool verify, const char *message, bool hide_input)
|
||||
{
|
||||
std::optional<password_container> password_container::prompt(
|
||||
const bool verify, const char* message, bool hide_input) {
|
||||
is_prompting = true;
|
||||
password_container pass1{};
|
||||
password_container pass2{};
|
||||
if (is_cin_tty() ? read_from_tty(verify, message, hide_input, pass1.m_password, pass2.m_password) : read_from_file(pass1.m_password))
|
||||
{
|
||||
is_prompting = false;
|
||||
return {std::move(pass1)};
|
||||
if (is_cin_tty()
|
||||
? read_from_tty(verify, message, hide_input, pass1.m_password, pass2.m_password)
|
||||
: read_from_file(pass1.m_password)) {
|
||||
is_prompting = false;
|
||||
return {std::move(pass1)};
|
||||
}
|
||||
|
||||
is_prompting = false;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<login> login::parse(std::string&& userpass, bool verify, const std::function<std::optional<password_container>(bool)> &prompt)
|
||||
{
|
||||
std::optional<login> login::parse(
|
||||
std::string&& userpass,
|
||||
bool verify,
|
||||
const std::function<std::optional<password_container>(bool)>& prompt) {
|
||||
login out{};
|
||||
|
||||
const auto loc = userpass.find(':');
|
||||
if (loc == std::string::npos)
|
||||
{
|
||||
auto result = prompt(verify);
|
||||
if (!result)
|
||||
return std::nullopt;
|
||||
if (loc == std::string::npos) {
|
||||
auto result = prompt(verify);
|
||||
if (!result)
|
||||
return std::nullopt;
|
||||
|
||||
out.password = std::move(*result);
|
||||
}
|
||||
else
|
||||
{
|
||||
out.password = password_container{userpass.substr(loc + 1)};
|
||||
out.password = std::move(*result);
|
||||
} else {
|
||||
out.password = password_container{userpass.substr(loc + 1)};
|
||||
}
|
||||
|
||||
out.username = userpass.substr(0, loc);
|
||||
password_container wipe{std::move(userpass)};
|
||||
return {std::move(out)};
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -25,22 +25,21 @@
|
|||
// 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.
|
||||
//
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "epee/wipeable_string.h"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
class password_container
|
||||
{
|
||||
namespace tools {
|
||||
class password_container {
|
||||
public:
|
||||
static constexpr const size_t max_password_size = 1024;
|
||||
|
||||
|
@ -51,22 +50,22 @@ namespace tools
|
|||
password_container(epee::wipeable_string password) noexcept;
|
||||
|
||||
//! \return A password from stdin TTY prompt or `std::cin` pipe.
|
||||
static std::optional<password_container> prompt(bool verify, const char *mesage = "Password", bool hide_input = true);
|
||||
static std::optional<password_container> prompt(
|
||||
bool verify, const char* mesage = "Password", bool hide_input = true);
|
||||
static std::atomic<bool> is_prompting;
|
||||
|
||||
const epee::wipeable_string &password() const noexcept { return m_password; }
|
||||
const epee::wipeable_string& password() const noexcept { return m_password; }
|
||||
|
||||
private:
|
||||
epee::wipeable_string m_password;
|
||||
};
|
||||
};
|
||||
|
||||
struct login
|
||||
{
|
||||
struct login {
|
||||
login() = default;
|
||||
|
||||
/// Constructs a login from a username/password. Does not prompt.
|
||||
login(std::string_view user, epee::wipeable_string pass)
|
||||
: username{user}, password{std::move(pass)} {}
|
||||
login(std::string_view user, epee::wipeable_string pass) :
|
||||
username{user}, password{std::move(pass)} {}
|
||||
|
||||
/*!
|
||||
Extracts username and password from the format `username:password`. A
|
||||
|
@ -81,9 +80,12 @@ namespace tools
|
|||
\return The username and password, or std::nullopt if
|
||||
`password_container::prompt` fails.
|
||||
*/
|
||||
static std::optional<login> parse(std::string&& userpass, bool verify, const std::function<std::optional<password_container>(bool)> &prompt);
|
||||
static std::optional<login> parse(
|
||||
std::string&& userpass,
|
||||
bool verify,
|
||||
const std::function<std::optional<password_container>(bool)>& prompt);
|
||||
|
||||
std::string username;
|
||||
password_container password;
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,51 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
||||
#include "crypto/crypto.h"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
namespace tools {
|
||||
// Periodic timer that gatekeeps calling of a job to a minimum interval after the previous job
|
||||
// finished. Only the reset() call is thread-safe; everything else should be confined to the
|
||||
// owning thread.
|
||||
class periodic_task
|
||||
{
|
||||
class periodic_task {
|
||||
public:
|
||||
explicit periodic_task(std::chrono::microseconds interval,
|
||||
bool start_immediately = true,
|
||||
std::pair<int, int> random_delay_interval = {})
|
||||
: m_interval{interval}
|
||||
, m_last_worked_time{std::chrono::steady_clock::now()}
|
||||
, m_trigger_now{start_immediately}
|
||||
, m_random_delay_interval{random_delay_interval}
|
||||
, m_next_delay{std::chrono::microseconds(crypto::rand_range(m_random_delay_interval.first, m_random_delay_interval.second))}
|
||||
{}
|
||||
explicit periodic_task(
|
||||
std::chrono::microseconds interval,
|
||||
bool start_immediately = true,
|
||||
std::pair<int, int> random_delay_interval = {}) :
|
||||
m_interval{interval},
|
||||
m_last_worked_time{std::chrono::steady_clock::now()},
|
||||
m_trigger_now{start_immediately},
|
||||
m_random_delay_interval{random_delay_interval},
|
||||
m_next_delay{std::chrono::microseconds(crypto::rand_range(
|
||||
m_random_delay_interval.first, m_random_delay_interval.second))} {}
|
||||
|
||||
template <class functor_t>
|
||||
void do_call(functor_t functr)
|
||||
{
|
||||
if (m_trigger_now || std::chrono::steady_clock::now() - m_last_worked_time > (m_interval + m_next_delay))
|
||||
{
|
||||
functr();
|
||||
m_last_worked_time = std::chrono::steady_clock::now();
|
||||
m_trigger_now = false;
|
||||
m_next_delay = std::chrono::microseconds(crypto::rand_range(m_random_delay_interval.first, m_random_delay_interval.second));
|
||||
template <class functor_t>
|
||||
void do_call(functor_t functr) {
|
||||
if (m_trigger_now ||
|
||||
std::chrono::steady_clock::now() - m_last_worked_time > (m_interval + m_next_delay)) {
|
||||
functr();
|
||||
m_last_worked_time = std::chrono::steady_clock::now();
|
||||
m_trigger_now = false;
|
||||
m_next_delay = std::chrono::microseconds(crypto::rand_range(
|
||||
m_random_delay_interval.first, m_random_delay_interval.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Makes the next task attempt run the job, regardless of the time since the last job. Atomic.
|
||||
void reset() { m_trigger_now = true; }
|
||||
// Returns the current interval
|
||||
std::chrono::microseconds interval() const { return m_interval; }
|
||||
// Changes the current interval
|
||||
void interval(std::chrono::microseconds us) { m_interval = us; }
|
||||
// Makes the next task attempt run the job, regardless of the time since the last job. Atomic.
|
||||
void reset() { m_trigger_now = true; }
|
||||
// Returns the current interval
|
||||
std::chrono::microseconds interval() const { return m_interval; }
|
||||
// Changes the current interval
|
||||
void interval(std::chrono::microseconds us) { m_interval = us; }
|
||||
|
||||
private:
|
||||
std::chrono::microseconds m_interval;
|
||||
std::chrono::steady_clock::time_point m_last_worked_time;
|
||||
std::atomic<bool> m_trigger_now;
|
||||
std::pair<int, int> m_random_delay_interval;
|
||||
std::chrono::microseconds m_next_delay;
|
||||
};
|
||||
private:
|
||||
std::chrono::microseconds m_interval;
|
||||
std::chrono::steady_clock::time_point m_last_worked_time;
|
||||
std::atomic<bool> m_trigger_now;
|
||||
std::pair<int, int> m_random_delay_interval;
|
||||
std::chrono::microseconds m_next_delay;
|
||||
};
|
||||
}; // namespace tools
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -26,93 +26,94 @@
|
|||
// 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.
|
||||
|
||||
#include "cryptonote_config.h"
|
||||
#include "epee/misc_log_ex.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "pruning.h"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
#include "crypto/crypto.h"
|
||||
#include "cryptonote_config.h"
|
||||
#include "epee/misc_log_ex.h"
|
||||
|
||||
namespace tools {
|
||||
|
||||
using namespace cryptonote;
|
||||
|
||||
uint32_t make_pruning_seed(uint32_t stripe, uint32_t log_stripes)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(log_stripes <= PRUNING_SEED_LOG_STRIPES_MASK, "log_stripes out of range");
|
||||
CHECK_AND_ASSERT_THROW_MES(stripe > 0 && stripe <= (1ul << log_stripes), "stripe out of range");
|
||||
return (log_stripes << PRUNING_SEED_LOG_STRIPES_SHIFT) | ((stripe - 1) << PRUNING_SEED_STRIPE_SHIFT);
|
||||
uint32_t make_pruning_seed(uint32_t stripe, uint32_t log_stripes) {
|
||||
CHECK_AND_ASSERT_THROW_MES(
|
||||
log_stripes <= PRUNING_SEED_LOG_STRIPES_MASK, "log_stripes out of range");
|
||||
CHECK_AND_ASSERT_THROW_MES(stripe > 0 && stripe <= (1ul << log_stripes), "stripe out of range");
|
||||
return (log_stripes << PRUNING_SEED_LOG_STRIPES_SHIFT) |
|
||||
((stripe - 1) << PRUNING_SEED_STRIPE_SHIFT);
|
||||
}
|
||||
|
||||
bool has_unpruned_block(uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed)
|
||||
{
|
||||
const uint32_t stripe = get_pruning_stripe(pruning_seed);
|
||||
if (stripe == 0)
|
||||
return true;
|
||||
const uint32_t log_stripes = get_pruning_log_stripes(pruning_seed);
|
||||
uint32_t block_stripe = get_pruning_stripe(block_height, blockchain_height, log_stripes);
|
||||
return block_stripe == 0 || block_stripe == stripe;
|
||||
bool has_unpruned_block(uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed) {
|
||||
const uint32_t stripe = get_pruning_stripe(pruning_seed);
|
||||
if (stripe == 0)
|
||||
return true;
|
||||
const uint32_t log_stripes = get_pruning_log_stripes(pruning_seed);
|
||||
uint32_t block_stripe = get_pruning_stripe(block_height, blockchain_height, log_stripes);
|
||||
return block_stripe == 0 || block_stripe == stripe;
|
||||
}
|
||||
|
||||
uint32_t get_pruning_stripe(uint64_t block_height, uint64_t blockchain_height, uint32_t log_stripes)
|
||||
{
|
||||
if (block_height + PRUNING_TIP_BLOCKS >= blockchain_height)
|
||||
return 0;
|
||||
return ((block_height / PRUNING_STRIPE_SIZE) & (uint64_t)((1ul << log_stripes) - 1)) + 1;
|
||||
uint32_t get_pruning_stripe(
|
||||
uint64_t block_height, uint64_t blockchain_height, uint32_t log_stripes) {
|
||||
if (block_height + PRUNING_TIP_BLOCKS >= blockchain_height)
|
||||
return 0;
|
||||
return ((block_height / PRUNING_STRIPE_SIZE) & (uint64_t)((1ul << log_stripes) - 1)) + 1;
|
||||
}
|
||||
|
||||
uint32_t get_pruning_seed(uint64_t block_height, uint64_t blockchain_height, uint32_t log_stripes)
|
||||
{
|
||||
const uint32_t stripe = get_pruning_stripe(block_height, blockchain_height, log_stripes);
|
||||
if (stripe == 0)
|
||||
return 0;
|
||||
return make_pruning_seed(stripe, log_stripes);
|
||||
uint32_t get_pruning_seed(uint64_t block_height, uint64_t blockchain_height, uint32_t log_stripes) {
|
||||
const uint32_t stripe = get_pruning_stripe(block_height, blockchain_height, log_stripes);
|
||||
if (stripe == 0)
|
||||
return 0;
|
||||
return make_pruning_seed(stripe, log_stripes);
|
||||
}
|
||||
|
||||
uint64_t get_next_unpruned_block_height(uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(block_height <= MAX_BLOCK_NUMBER+1, block_height, "block_height too large");
|
||||
CHECK_AND_ASSERT_MES(blockchain_height <= MAX_BLOCK_NUMBER+1, block_height, "blockchain_height too large");
|
||||
const uint32_t stripe = get_pruning_stripe(pruning_seed);
|
||||
if (stripe == 0)
|
||||
return block_height;
|
||||
if (block_height + PRUNING_TIP_BLOCKS >= blockchain_height)
|
||||
return block_height;
|
||||
const uint32_t seed_log_stripes = get_pruning_log_stripes(pruning_seed);
|
||||
const uint64_t log_stripes = seed_log_stripes ? seed_log_stripes : PRUNING_LOG_STRIPES;
|
||||
const uint64_t mask = (1ul << log_stripes) - 1;
|
||||
const uint32_t block_pruning_stripe = ((block_height / PRUNING_STRIPE_SIZE) & mask) + 1;
|
||||
if (block_pruning_stripe == stripe)
|
||||
return block_height;
|
||||
const uint64_t cycles = ((block_height / PRUNING_STRIPE_SIZE) >> log_stripes);
|
||||
const uint64_t cycle_start = cycles + ((stripe > block_pruning_stripe) ? 0 : 1);
|
||||
const uint64_t h = cycle_start * (PRUNING_STRIPE_SIZE << log_stripes) + (stripe - 1) * PRUNING_STRIPE_SIZE;
|
||||
if (h + PRUNING_TIP_BLOCKS > blockchain_height)
|
||||
return blockchain_height < PRUNING_TIP_BLOCKS ? 0 : blockchain_height - PRUNING_TIP_BLOCKS;
|
||||
CHECK_AND_ASSERT_MES(h >= block_height, block_height, "h < block_height, unexpected");
|
||||
return h;
|
||||
uint64_t get_next_unpruned_block_height(
|
||||
uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed) {
|
||||
CHECK_AND_ASSERT_MES(
|
||||
block_height <= MAX_BLOCK_NUMBER + 1, block_height, "block_height too large");
|
||||
CHECK_AND_ASSERT_MES(
|
||||
blockchain_height <= MAX_BLOCK_NUMBER + 1, block_height, "blockchain_height too large");
|
||||
const uint32_t stripe = get_pruning_stripe(pruning_seed);
|
||||
if (stripe == 0)
|
||||
return block_height;
|
||||
if (block_height + PRUNING_TIP_BLOCKS >= blockchain_height)
|
||||
return block_height;
|
||||
const uint32_t seed_log_stripes = get_pruning_log_stripes(pruning_seed);
|
||||
const uint64_t log_stripes = seed_log_stripes ? seed_log_stripes : PRUNING_LOG_STRIPES;
|
||||
const uint64_t mask = (1ul << log_stripes) - 1;
|
||||
const uint32_t block_pruning_stripe = ((block_height / PRUNING_STRIPE_SIZE) & mask) + 1;
|
||||
if (block_pruning_stripe == stripe)
|
||||
return block_height;
|
||||
const uint64_t cycles = ((block_height / PRUNING_STRIPE_SIZE) >> log_stripes);
|
||||
const uint64_t cycle_start = cycles + ((stripe > block_pruning_stripe) ? 0 : 1);
|
||||
const uint64_t h =
|
||||
cycle_start * (PRUNING_STRIPE_SIZE << log_stripes) + (stripe - 1) * PRUNING_STRIPE_SIZE;
|
||||
if (h + PRUNING_TIP_BLOCKS > blockchain_height)
|
||||
return blockchain_height < PRUNING_TIP_BLOCKS ? 0 : blockchain_height - PRUNING_TIP_BLOCKS;
|
||||
CHECK_AND_ASSERT_MES(h >= block_height, block_height, "h < block_height, unexpected");
|
||||
return h;
|
||||
}
|
||||
|
||||
uint64_t get_next_pruned_block_height(uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed)
|
||||
{
|
||||
const uint32_t stripe = get_pruning_stripe(pruning_seed);
|
||||
if (stripe == 0)
|
||||
return blockchain_height;
|
||||
if (block_height + PRUNING_TIP_BLOCKS >= blockchain_height)
|
||||
return blockchain_height;
|
||||
const uint32_t seed_log_stripes = get_pruning_log_stripes(pruning_seed);
|
||||
const uint64_t log_stripes = seed_log_stripes ? seed_log_stripes : PRUNING_LOG_STRIPES;
|
||||
const uint64_t mask = (1ul << log_stripes) - 1;
|
||||
const uint32_t block_pruning_seed = ((block_height / PRUNING_STRIPE_SIZE) & mask) + 1;
|
||||
if (block_pruning_seed != stripe)
|
||||
return block_height;
|
||||
const uint32_t next_stripe = 1 + (block_pruning_seed & mask);
|
||||
return get_next_unpruned_block_height(block_height, blockchain_height, tools::make_pruning_seed(next_stripe, log_stripes));
|
||||
}
|
||||
|
||||
uint32_t get_random_stripe()
|
||||
{
|
||||
return 1 + crypto::rand<uint8_t>() % (1ul << PRUNING_LOG_STRIPES);
|
||||
uint64_t get_next_pruned_block_height(
|
||||
uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed) {
|
||||
const uint32_t stripe = get_pruning_stripe(pruning_seed);
|
||||
if (stripe == 0)
|
||||
return blockchain_height;
|
||||
if (block_height + PRUNING_TIP_BLOCKS >= blockchain_height)
|
||||
return blockchain_height;
|
||||
const uint32_t seed_log_stripes = get_pruning_log_stripes(pruning_seed);
|
||||
const uint64_t log_stripes = seed_log_stripes ? seed_log_stripes : PRUNING_LOG_STRIPES;
|
||||
const uint64_t mask = (1ul << log_stripes) - 1;
|
||||
const uint32_t block_pruning_seed = ((block_height / PRUNING_STRIPE_SIZE) & mask) + 1;
|
||||
if (block_pruning_seed != stripe)
|
||||
return block_height;
|
||||
const uint32_t next_stripe = 1 + (block_pruning_seed & mask);
|
||||
return get_next_unpruned_block_height(
|
||||
block_height, blockchain_height, tools::make_pruning_seed(next_stripe, log_stripes));
|
||||
}
|
||||
|
||||
uint32_t get_random_stripe() {
|
||||
return 1 + crypto::rand<uint8_t>() % (1ul << PRUNING_LOG_STRIPES);
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -30,29 +30,30 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace tools
|
||||
{
|
||||
inline constexpr uint32_t PRUNING_SEED_LOG_STRIPES_SHIFT = 7;
|
||||
inline constexpr uint32_t PRUNING_SEED_LOG_STRIPES_MASK = 0x7;
|
||||
inline constexpr uint32_t PRUNING_SEED_STRIPE_SHIFT = 0;
|
||||
inline constexpr uint32_t PRUNING_SEED_STRIPE_MASK = 0x7f;
|
||||
namespace tools {
|
||||
inline constexpr uint32_t PRUNING_SEED_LOG_STRIPES_SHIFT = 7;
|
||||
inline constexpr uint32_t PRUNING_SEED_LOG_STRIPES_MASK = 0x7;
|
||||
inline constexpr uint32_t PRUNING_SEED_STRIPE_SHIFT = 0;
|
||||
inline constexpr uint32_t PRUNING_SEED_STRIPE_MASK = 0x7f;
|
||||
|
||||
inline constexpr uint32_t get_pruning_log_stripes(uint32_t pruning_seed) {
|
||||
inline constexpr uint32_t get_pruning_log_stripes(uint32_t pruning_seed) {
|
||||
return (pruning_seed >> PRUNING_SEED_LOG_STRIPES_SHIFT) & PRUNING_SEED_LOG_STRIPES_MASK;
|
||||
}
|
||||
inline constexpr uint32_t get_pruning_stripe(uint32_t pruning_seed) {
|
||||
}
|
||||
inline constexpr uint32_t get_pruning_stripe(uint32_t pruning_seed) {
|
||||
if (pruning_seed == 0)
|
||||
return 0;
|
||||
return 0;
|
||||
return 1 + ((pruning_seed >> PRUNING_SEED_STRIPE_SHIFT) & PRUNING_SEED_STRIPE_MASK);
|
||||
}
|
||||
|
||||
uint32_t make_pruning_seed(uint32_t stripe, uint32_t log_stripes);
|
||||
|
||||
bool has_unpruned_block(uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed);
|
||||
uint32_t get_pruning_stripe(uint64_t block_height, uint64_t blockchain_height, uint32_t log_stripes);
|
||||
uint32_t get_pruning_seed(uint64_t block_height, uint64_t blockchain_height, uint32_t log_stripes);
|
||||
uint64_t get_next_unpruned_block_height(uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed);
|
||||
uint64_t get_next_pruned_block_height(uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed);
|
||||
uint32_t get_random_stripe();
|
||||
}
|
||||
|
||||
uint32_t make_pruning_seed(uint32_t stripe, uint32_t log_stripes);
|
||||
|
||||
bool has_unpruned_block(uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed);
|
||||
uint32_t get_pruning_stripe(
|
||||
uint64_t block_height, uint64_t blockchain_height, uint32_t log_stripes);
|
||||
uint32_t get_pruning_seed(uint64_t block_height, uint64_t blockchain_height, uint32_t log_stripes);
|
||||
uint64_t get_next_unpruned_block_height(
|
||||
uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed);
|
||||
uint64_t get_next_pruned_block_height(
|
||||
uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed);
|
||||
uint32_t get_random_stripe();
|
||||
} // namespace tools
|
||||
|
|
|
@ -27,19 +27,21 @@
|
|||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "random.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace tools {
|
||||
|
||||
thread_local std::mt19937_64 rng{std::random_device{}()};
|
||||
|
||||
uint64_t uniform_distribution_portable(std::mt19937_64& rng, const uint64_t n)
|
||||
{
|
||||
assert(n > 0);
|
||||
const uint64_t secureMax = rng.max() - rng.max() % n;
|
||||
uint64_t x;
|
||||
do x = rng(); while (x >= secureMax);
|
||||
return x / (secureMax / n);
|
||||
uint64_t uniform_distribution_portable(std::mt19937_64& rng, const uint64_t n) {
|
||||
assert(n > 0);
|
||||
const uint64_t secureMax = rng.max() - rng.max() % n;
|
||||
uint64_t x;
|
||||
do
|
||||
x = rng();
|
||||
while (x >= secureMax);
|
||||
return x / (secureMax / n);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -43,32 +43,34 @@ uint64_t uniform_distribution_portable(std::mt19937_64& rng, uint64_t n);
|
|||
|
||||
/// Uniformly shuffles all the elements in [begin, end) in a deterministic method so that, given the
|
||||
/// same seed, this will always produce the same result on any platform/compiler/etc.
|
||||
template<typename RandomIt>
|
||||
void shuffle_portable(RandomIt begin, RandomIt end, std::mt19937_64 &rng)
|
||||
{
|
||||
if (end <= begin + 1) return;
|
||||
const size_t size = std::distance(begin, end);
|
||||
for (size_t i = 1; i < size; i++)
|
||||
{
|
||||
size_t j = (size_t)uniform_distribution_portable(rng, i+1);
|
||||
using std::swap;
|
||||
swap(begin[i], begin[j]);
|
||||
}
|
||||
template <typename RandomIt>
|
||||
void shuffle_portable(RandomIt begin, RandomIt end, std::mt19937_64& rng) {
|
||||
if (end <= begin + 1)
|
||||
return;
|
||||
const size_t size = std::distance(begin, end);
|
||||
for (size_t i = 1; i < size; i++) {
|
||||
size_t j = (size_t)uniform_distribution_portable(rng, i + 1);
|
||||
using std::swap;
|
||||
swap(begin[i], begin[j]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a random element between the elements in [begin, end) will use the random number generator provided as g
|
||||
template<typename Iter, typename RandomGenerator>
|
||||
/// Returns a random element between the elements in [begin, end) will use the random number
|
||||
/// generator provided as g
|
||||
template <typename Iter, typename RandomGenerator>
|
||||
Iter select_randomly(Iter begin, Iter end, RandomGenerator& g) {
|
||||
auto dist = std::distance(begin, end);
|
||||
if (dist <= 1) return begin; // Handles both begin=end case and single-element case
|
||||
std::advance(begin, std::uniform_int_distribution<>{0, static_cast<int>(dist-1)}(g));
|
||||
return begin;
|
||||
auto dist = std::distance(begin, end);
|
||||
if (dist <= 1)
|
||||
return begin; // Handles both begin=end case and single-element case
|
||||
std::advance(begin, std::uniform_int_distribution<>{0, static_cast<int>(dist - 1)}(g));
|
||||
return begin;
|
||||
}
|
||||
|
||||
/// Returns a random element same as above but defaults to the seeded random number generator defined in this file
|
||||
template<typename Iter>
|
||||
/// Returns a random element same as above but defaults to the seeded random number generator
|
||||
/// defined in this file
|
||||
template <typename Iter>
|
||||
Iter select_randomly(Iter begin, Iter end) {
|
||||
return select_randomly(begin, end, rng);
|
||||
return select_randomly(begin, end, rng);
|
||||
}
|
||||
|
||||
};
|
||||
}; // namespace tools
|
||||
|
|
|
@ -28,40 +28,33 @@
|
|||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#include <ctime>
|
||||
#include <cstdint>
|
||||
#include "cryptonote_config.h"
|
||||
#include <ctime>
|
||||
|
||||
#include "common/util.h"
|
||||
#include "cryptonote_config.h"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
namespace cryptonote { namespace rules {
|
||||
|
||||
namespace rules
|
||||
{
|
||||
bool is_output_unlocked(uint64_t unlock_time, uint64_t height) {
|
||||
if (unlock_time < MAX_BLOCK_NUMBER) {
|
||||
// ND: Instead of calling get_current_blockchain_height(), call m_db->height()
|
||||
// directly as get_current_blockchain_height() locks the recursive mutex.
|
||||
if (height - 1 + LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
} else {
|
||||
// interpret as time
|
||||
uint64_t current_time = static_cast<uint64_t>(time(NULL));
|
||||
if (current_time +
|
||||
tools::to_seconds(LOCKED_TX_ALLOWED_DELTA_BLOCKS * TARGET_BLOCK_TIME) >=
|
||||
unlock_time)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_output_unlocked(uint64_t unlock_time, uint64_t height)
|
||||
{
|
||||
if(unlock_time < MAX_BLOCK_NUMBER)
|
||||
{
|
||||
// ND: Instead of calling get_current_blockchain_height(), call m_db->height()
|
||||
// directly as get_current_blockchain_height() locks the recursive mutex.
|
||||
if(height - 1 + LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//interpret as time
|
||||
uint64_t current_time = static_cast<uint64_t>(time(NULL));
|
||||
if(current_time + tools::to_seconds(LOCKED_TX_ALLOWED_DELTA_BLOCKS * TARGET_BLOCK_TIME) >= unlock_time)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace rules
|
||||
|
||||
} // namespace cryptonote
|
||||
}} // namespace cryptonote::rules
|
||||
|
|
|
@ -30,9 +30,8 @@
|
|||
|
||||
#include <cstdint>
|
||||
|
||||
namespace cryptonote::rules
|
||||
{
|
||||
namespace cryptonote::rules {
|
||||
|
||||
bool is_output_unlocked(uint64_t unlock_time, uint64_t height);
|
||||
|
||||
} // namespace cryptonote::rules
|
||||
} // namespace cryptonote::rules
|
||||
|
|
|
@ -1,30 +1,27 @@
|
|||
#include "scoped_message_writer.h"
|
||||
|
||||
#include "common/format.h"
|
||||
|
||||
namespace tools {
|
||||
|
||||
static auto logcat = log::Cat("msgwriter");
|
||||
|
||||
scoped_message_writer& scoped_message_writer::flush()
|
||||
{
|
||||
if (!m_content.empty())
|
||||
{
|
||||
logcat->log(m_log_level, "{}{}", m_prefix, m_content);
|
||||
scoped_message_writer& scoped_message_writer::flush() {
|
||||
if (!m_content.empty()) {
|
||||
logcat->log(m_log_level, "{}{}", m_prefix, m_content);
|
||||
|
||||
if (m_color) {
|
||||
rdln::suspend_readline pause_readline;
|
||||
fmt::print(fg(*m_color), "{}{}\n", m_prefix, m_content);
|
||||
if (m_color) {
|
||||
rdln::suspend_readline pause_readline;
|
||||
fmt::print(fg(*m_color), "{}{}\n", m_prefix, m_content);
|
||||
} else
|
||||
fmt::print("{}{}\n", m_prefix, m_content);
|
||||
|
||||
m_content.clear();
|
||||
}
|
||||
else
|
||||
fmt::print("{}{}\n", m_prefix, m_content);
|
||||
|
||||
m_content.clear();
|
||||
}
|
||||
return *this;
|
||||
return *this;
|
||||
}
|
||||
scoped_message_writer::~scoped_message_writer()
|
||||
{
|
||||
flush();
|
||||
scoped_message_writer::~scoped_message_writer() {
|
||||
flush();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -28,91 +28,89 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "epee/readline_suspend.h"
|
||||
#include "epee/misc_log_ex.h"
|
||||
#include <iostream>
|
||||
#include "logging/oxen_logger.h"
|
||||
#include <fmt/color.h>
|
||||
|
||||
namespace tools
|
||||
{
|
||||
#include <iostream>
|
||||
|
||||
#include "epee/misc_log_ex.h"
|
||||
#include "epee/readline_suspend.h"
|
||||
#include "logging/oxen_logger.h"
|
||||
|
||||
namespace tools {
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
class scoped_message_writer
|
||||
{
|
||||
private:
|
||||
std::string m_prefix;
|
||||
std::string m_content;
|
||||
std::optional<fmt::terminal_color> m_color;
|
||||
oxen::log::Level m_log_level;
|
||||
public:
|
||||
explicit scoped_message_writer(
|
||||
std::optional<fmt::terminal_color> color = std::nullopt,
|
||||
std::string prefix = "",
|
||||
log::Level log_level = log::Level::info
|
||||
)
|
||||
: m_color{color}, m_log_level{log_level}, m_prefix{std::move(prefix)}
|
||||
{}
|
||||
class scoped_message_writer {
|
||||
private:
|
||||
std::string m_prefix;
|
||||
std::string m_content;
|
||||
std::optional<fmt::terminal_color> m_color;
|
||||
oxen::log::Level m_log_level;
|
||||
|
||||
scoped_message_writer(scoped_message_writer&& o)
|
||||
: m_prefix{std::move(o.m_prefix)},
|
||||
m_content{std::move(o.m_content)},
|
||||
m_color{o.m_color},
|
||||
m_log_level{o.m_log_level}
|
||||
{
|
||||
o.m_content.clear();
|
||||
}
|
||||
public:
|
||||
explicit scoped_message_writer(
|
||||
std::optional<fmt::terminal_color> color = std::nullopt,
|
||||
std::string prefix = "",
|
||||
log::Level log_level = log::Level::info) :
|
||||
m_color{color}, m_log_level{log_level}, m_prefix{std::move(prefix)} {}
|
||||
|
||||
scoped_message_writer(const scoped_message_writer& rhs) = delete;
|
||||
scoped_message_writer& operator=(const scoped_message_writer& rhs) = delete;
|
||||
scoped_message_writer& operator=(scoped_message_writer&& rhs) = delete;
|
||||
scoped_message_writer(scoped_message_writer&& o) :
|
||||
m_prefix{std::move(o.m_prefix)},
|
||||
m_content{std::move(o.m_content)},
|
||||
m_color{o.m_color},
|
||||
m_log_level{o.m_log_level} {
|
||||
o.m_content.clear();
|
||||
}
|
||||
|
||||
/// Appends a message and returns *this (so that it can be chained). If called with more than 1
|
||||
/// argument then the first argument is fmt::format'ed with the remaining arguments.
|
||||
template <typename... T>
|
||||
scoped_message_writer& append(std::string_view msg, T&&... args)
|
||||
{
|
||||
if constexpr (sizeof...(T))
|
||||
fmt::format_to(std::back_inserter(m_content), msg, std::forward<T>(args)...);
|
||||
else
|
||||
m_content.append(msg);
|
||||
return *this;
|
||||
}
|
||||
scoped_message_writer(const scoped_message_writer& rhs) = delete;
|
||||
scoped_message_writer& operator=(const scoped_message_writer& rhs) = delete;
|
||||
scoped_message_writer& operator=(scoped_message_writer&& rhs) = delete;
|
||||
|
||||
/// Same as .append(msg). (Doesn't format, just like the single-argument .append(msg)).
|
||||
scoped_message_writer& operator+=(std::string_view msg) { return append(msg); }
|
||||
/// Appends a message and returns *this (so that it can be chained). If called with more than 1
|
||||
/// argument then the first argument is fmt::format'ed with the remaining arguments.
|
||||
template <typename... T>
|
||||
scoped_message_writer& append(std::string_view msg, T&&... args) {
|
||||
if constexpr (sizeof...(T))
|
||||
fmt::format_to(std::back_inserter(m_content), msg, std::forward<T>(args)...);
|
||||
else
|
||||
m_content.append(msg);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Essentially the same as +=, but can only be used on an rvalue instance of the object, so that
|
||||
/// you can do things like: `scoped_message_writer{} + "abc"`, which feels more natural than
|
||||
/// `scoped_message_writer{} += "abc"`.
|
||||
scoped_message_writer&& operator+(std::string_view msg) && { append(msg); return std::move(*this); }
|
||||
/// Same as .append(msg). (Doesn't format, just like the single-argument .append(msg)).
|
||||
scoped_message_writer& operator+=(std::string_view msg) { return append(msg); }
|
||||
|
||||
/// Flushes the current message to output and resets it. This is normally not called explicitly
|
||||
/// but rather implicitly when the object is destroyed.
|
||||
scoped_message_writer& flush();
|
||||
/// Essentially the same as +=, but can only be used on an rvalue instance of the object, so
|
||||
/// that you can do things like: `scoped_message_writer{} + "abc"`, which feels more natural
|
||||
/// than `scoped_message_writer{} += "abc"`.
|
||||
scoped_message_writer&& operator+(std::string_view msg) && {
|
||||
append(msg);
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
/// Prints the complete message on destruction.
|
||||
~scoped_message_writer();
|
||||
/// Flushes the current message to output and resets it. This is normally not called explicitly
|
||||
/// but rather implicitly when the object is destroyed.
|
||||
scoped_message_writer& flush();
|
||||
|
||||
/// Prints the complete message on destruction.
|
||||
~scoped_message_writer();
|
||||
};
|
||||
|
||||
template <typename... T>
|
||||
scoped_message_writer msg_writer(std::optional<fmt::terminal_color> color = std::nullopt, T&&... args)
|
||||
{
|
||||
scoped_message_writer writer{color};
|
||||
if constexpr (sizeof...(T))
|
||||
writer.append(std::forward<T>(args)...);
|
||||
return writer;
|
||||
scoped_message_writer msg_writer(
|
||||
std::optional<fmt::terminal_color> color = std::nullopt, T&&... args) {
|
||||
scoped_message_writer writer{color};
|
||||
if constexpr (sizeof...(T))
|
||||
writer.append(std::forward<T>(args)...);
|
||||
return writer;
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
scoped_message_writer msg_writer(std::string_view msg, T&&... args)
|
||||
{
|
||||
return msg_writer(std::nullopt, msg, std::forward<T>(args)...);
|
||||
scoped_message_writer msg_writer(std::string_view msg, T&&... args) {
|
||||
return msg_writer(std::nullopt, msg, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
|
||||
constexpr std::optional<fmt::terminal_color> success_color{fmt::terminal_color::green};
|
||||
constexpr std::optional<fmt::terminal_color> fail_color{fmt::terminal_color::red};
|
||||
|
||||
|
@ -122,24 +120,21 @@ constexpr std::optional<fmt::terminal_color> fail_color{fmt::terminal_color::red
|
|||
///
|
||||
/// (We deduce the Bool argument here to avoid implicit conversion to bool from non-bool values).
|
||||
template <typename Bool, typename... T, std::enable_if_t<std::is_same_v<Bool, bool>, int> = 0>
|
||||
scoped_message_writer success_msg_writer(Bool color, T&&... args)
|
||||
{
|
||||
auto writer = msg_writer(color ? success_color : std::nullopt);
|
||||
if constexpr (sizeof...(T))
|
||||
writer.append(std::forward<T>(args)...);
|
||||
return writer;
|
||||
scoped_message_writer success_msg_writer(Bool color, T&&... args) {
|
||||
auto writer = msg_writer(color ? success_color : std::nullopt);
|
||||
if constexpr (sizeof...(T))
|
||||
writer.append(std::forward<T>(args)...);
|
||||
return writer;
|
||||
}
|
||||
|
||||
inline scoped_message_writer success_msg_writer()
|
||||
{
|
||||
return success_msg_writer(true);
|
||||
inline scoped_message_writer success_msg_writer() {
|
||||
return success_msg_writer(true);
|
||||
}
|
||||
|
||||
/// Same as above, but for calling without just a message (with a bool). Color will be true.
|
||||
template <typename... T>
|
||||
scoped_message_writer success_msg_writer(std::string_view msg, T&&... args)
|
||||
{
|
||||
return success_msg_writer(true, msg, std::forward<T>(args)...);
|
||||
scoped_message_writer success_msg_writer(std::string_view msg, T&&... args) {
|
||||
return success_msg_writer(true, msg, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
/// Constructs and returns a scoped_message_writer for a typical error message. Color will be
|
||||
|
@ -147,12 +142,11 @@ scoped_message_writer success_msg_writer(std::string_view msg, T&&... args)
|
|||
/// .append() and so can specify either a single unformatted string, or a format string + format
|
||||
/// arguments.
|
||||
template <typename... T>
|
||||
scoped_message_writer fail_msg_writer(T&&... args)
|
||||
{
|
||||
scoped_message_writer writer{fail_color, "Error: ", spdlog::level::err};
|
||||
if constexpr (sizeof...(T))
|
||||
writer.append(std::forward<T>(args)...);
|
||||
return writer;
|
||||
scoped_message_writer fail_msg_writer(T&&... args) {
|
||||
scoped_message_writer writer{fail_color, "Error: ", spdlog::level::err};
|
||||
if constexpr (sizeof...(T))
|
||||
writer.append(std::forward<T>(args)...);
|
||||
return writer;
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,30 +1,29 @@
|
|||
|
||||
#include "sha256sum.h"
|
||||
|
||||
#include <sodium/crypto_hash_sha256.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "crypto/hash.h"
|
||||
#include "fs.h"
|
||||
#include <sodium/crypto_hash_sha256.h>
|
||||
|
||||
namespace tools {
|
||||
|
||||
bool sha256sum_str(std::string_view data, crypto::hash &hash)
|
||||
{
|
||||
bool sha256sum_str(std::string_view data, crypto::hash& hash) {
|
||||
crypto_hash_sha256(
|
||||
hash.data(),
|
||||
reinterpret_cast<const unsigned char*>(data.data()),
|
||||
data.size());
|
||||
hash.data(), reinterpret_cast<const unsigned char*>(data.data()), data.size());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool sha256sum_file(const fs::path& filename, crypto::hash& hash)
|
||||
{
|
||||
bool sha256sum_file(const fs::path& filename, crypto::hash& hash) {
|
||||
if (std::error_code ec; !fs::exists(filename, ec) || ec)
|
||||
return false;
|
||||
return false;
|
||||
fs::ifstream f;
|
||||
f.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
f.open(filename, std::ios_base::binary | std::ios_base::in | std::ios::ate);
|
||||
if (!f)
|
||||
return false;
|
||||
return false;
|
||||
std::ifstream::pos_type file_size = f.tellg();
|
||||
crypto_hash_sha256_state st;
|
||||
crypto_hash_sha256_init(&st);
|
||||
|
@ -32,18 +31,17 @@ namespace tools {
|
|||
f.seekg(0, std::ios::beg);
|
||||
|
||||
std::array<unsigned char, 16384> buf;
|
||||
while (size_left)
|
||||
{
|
||||
auto read_size = std::min(size_left, buf.size());
|
||||
f.read(reinterpret_cast<char*>(buf.data()), read_size);
|
||||
if (!f || !f.good())
|
||||
return false;
|
||||
crypto_hash_sha256_update(&st, buf.data(), read_size);
|
||||
size_left -= read_size;
|
||||
while (size_left) {
|
||||
auto read_size = std::min(size_left, buf.size());
|
||||
f.read(reinterpret_cast<char*>(buf.data()), read_size);
|
||||
if (!f || !f.good())
|
||||
return false;
|
||||
crypto_hash_sha256_update(&st, buf.data(), read_size);
|
||||
size_left -= read_size;
|
||||
}
|
||||
f.close();
|
||||
crypto_hash_sha256_final(&st, hash.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,33 +1,37 @@
|
|||
#pragma once
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
namespace crypto { struct hash; }
|
||||
namespace crypto {
|
||||
struct hash;
|
||||
}
|
||||
|
||||
namespace tools {
|
||||
|
||||
// Calculates sha256 checksum of the given data
|
||||
bool sha256sum_str(std::string_view str, crypto::hash& hash);
|
||||
|
||||
// Calculates sha256 checksum of the given data, for non-char string_view (e.g.
|
||||
// basic_string_view<unsigned char> or basic_string_view<uint8_t>).
|
||||
template <typename Char, std::enable_if_t<sizeof(Char) == 1 && !std::is_same_v<Char, char>, int> = 0>
|
||||
bool sha256sum_str(std::basic_string_view<Char> str, crypto::hash& hash)
|
||||
{
|
||||
return sha256sum_str(std::string_view{reinterpret_cast<const char*>(str.data()), str.size()}, hash);
|
||||
}
|
||||
|
||||
// Calculates sha256 checksum of the given byte data given any arbitrary size-1 value pointer and
|
||||
// byte length.
|
||||
template <typename Char, std::enable_if_t<sizeof(Char) == 1, int> = 0>
|
||||
bool sha256sum_str(const Char* data, size_t len, crypto::hash& hash)
|
||||
{
|
||||
return sha256sum_str(std::string_view{reinterpret_cast<const char*>(data), len}, hash);
|
||||
}
|
||||
|
||||
// Opens the given file and calculates a sha256sum of its contents
|
||||
bool sha256sum_file(const fs::path& filename, crypto::hash& hash);
|
||||
// Calculates sha256 checksum of the given data
|
||||
bool sha256sum_str(std::string_view str, crypto::hash& hash);
|
||||
|
||||
// Calculates sha256 checksum of the given data, for non-char string_view (e.g.
|
||||
// basic_string_view<unsigned char> or basic_string_view<uint8_t>).
|
||||
template <
|
||||
typename Char,
|
||||
std::enable_if_t<sizeof(Char) == 1 && !std::is_same_v<Char, char>, int> = 0>
|
||||
bool sha256sum_str(std::basic_string_view<Char> str, crypto::hash& hash) {
|
||||
return sha256sum_str(
|
||||
std::string_view{reinterpret_cast<const char*>(str.data()), str.size()}, hash);
|
||||
}
|
||||
|
||||
// Calculates sha256 checksum of the given byte data given any arbitrary size-1 value pointer and
|
||||
// byte length.
|
||||
template <typename Char, std::enable_if_t<sizeof(Char) == 1, int> = 0>
|
||||
bool sha256sum_str(const Char* data, size_t len, crypto::hash& hash) {
|
||||
return sha256sum_str(std::string_view{reinterpret_cast<const char*>(data), len}, hash);
|
||||
}
|
||||
|
||||
// Opens the given file and calculates a sha256sum of its contents
|
||||
bool sha256sum_file(const fs::path& filename, crypto::hash& hash);
|
||||
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,80 +1,75 @@
|
|||
#pragma once
|
||||
#include <cstring>
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include "logging/oxen_logger.h"
|
||||
#ifdef _WIN32
|
||||
#include "windows.h"
|
||||
#endif
|
||||
|
||||
|
||||
namespace tools {
|
||||
|
||||
/*! \brief Defines a singleton signal handler for win32 and *nix
|
||||
*/
|
||||
class signal_handler
|
||||
{
|
||||
/*! \brief Defines a singleton signal handler for win32 and *nix
|
||||
*/
|
||||
class signal_handler {
|
||||
public:
|
||||
/*! \brief installs a signal handler */
|
||||
template<typename T>
|
||||
static bool install(T t)
|
||||
{
|
||||
template <typename T>
|
||||
static bool install(T t) {
|
||||
#ifdef _WIN32
|
||||
bool r = TRUE == ::SetConsoleCtrlHandler(&win_handler, TRUE);
|
||||
if (r)
|
||||
{
|
||||
m_handler = t;
|
||||
}
|
||||
return r;
|
||||
bool r = TRUE == ::SetConsoleCtrlHandler(&win_handler, TRUE);
|
||||
if (r) {
|
||||
m_handler = t;
|
||||
}
|
||||
return r;
|
||||
#else
|
||||
static struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_handler = posix_handler;
|
||||
sa.sa_flags = 0;
|
||||
/* Only blocks SIGINT, SIGTERM and SIGPIPE */
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
signal(SIGTERM, posix_handler);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
m_handler = t;
|
||||
return true;
|
||||
static struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_handler = posix_handler;
|
||||
sa.sa_flags = 0;
|
||||
/* Only blocks SIGINT, SIGTERM and SIGPIPE */
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
signal(SIGTERM, posix_handler);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
m_handler = t;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#if defined(WIN32)
|
||||
/*! \brief Handler for win */
|
||||
static BOOL WINAPI win_handler(DWORD type)
|
||||
{
|
||||
if (CTRL_C_EVENT == type || CTRL_BREAK_EVENT == type)
|
||||
{
|
||||
handle_signal(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
log::info(globallogcat, fg(fmt::terminal_color::red), "Got control signal {}. Exiting without saving...", type);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
static BOOL WINAPI win_handler(DWORD type) {
|
||||
if (CTRL_C_EVENT == type || CTRL_BREAK_EVENT == type) {
|
||||
handle_signal(type);
|
||||
} else {
|
||||
log::info(
|
||||
globallogcat,
|
||||
fg(fmt::terminal_color::red),
|
||||
"Got control signal {}. Exiting without saving...",
|
||||
type);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
#else
|
||||
/*! \brief handler for NIX */
|
||||
static void posix_handler(int type)
|
||||
{
|
||||
handle_signal(type);
|
||||
static void posix_handler(int type) {
|
||||
handle_signal(type);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! \brief calles m_handler */
|
||||
static void handle_signal(int type)
|
||||
{
|
||||
static std::mutex m_mutex;
|
||||
std::unique_lock lock{m_mutex};
|
||||
m_handler(type);
|
||||
static void handle_signal(int type) {
|
||||
static std::mutex m_mutex;
|
||||
std::unique_lock lock{m_mutex};
|
||||
m_handler(type);
|
||||
}
|
||||
|
||||
/*! \brief where the installed handler is stored */
|
||||
static inline std::function<void(int)> m_handler;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -27,148 +27,146 @@
|
|||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#include "epee/misc_log_ex.h"
|
||||
#include "logging/oxen_logger.h"
|
||||
#include "util.h"
|
||||
#include "spawn.h"
|
||||
#include "oxen.h"
|
||||
#include "spawn.h"
|
||||
#include "string_util.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
namespace tools {
|
||||
|
||||
static auto logcat = log::Cat("spawn");
|
||||
static auto logcat = log::Cat("spawn");
|
||||
|
||||
#ifndef _WIN32
|
||||
static void closefrom(int fd)
|
||||
{
|
||||
static void closefrom(int fd) {
|
||||
#if defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ || defined __DragonFly__
|
||||
::closefrom(fd);
|
||||
::closefrom(fd);
|
||||
#else
|
||||
#if defined __GLIBC__
|
||||
const int sc_open_max = sysconf(_SC_OPEN_MAX);
|
||||
const int MAX_FDS = std::min(65536, sc_open_max);
|
||||
const int sc_open_max = sysconf(_SC_OPEN_MAX);
|
||||
const int MAX_FDS = std::min(65536, sc_open_max);
|
||||
#else
|
||||
const int MAX_FDS = 65536;
|
||||
const int MAX_FDS = 65536;
|
||||
#endif
|
||||
while (fd < MAX_FDS)
|
||||
{
|
||||
close(fd);
|
||||
++fd;
|
||||
}
|
||||
while (fd < MAX_FDS) {
|
||||
close(fd);
|
||||
++fd;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int spawn(const fs::path& filename, const std::vector<std::string>& args, bool wait)
|
||||
{
|
||||
int spawn(const fs::path& filename, const std::vector<std::string>& args, bool wait) {
|
||||
#ifdef _WIN32
|
||||
std::string joined = tools::join(" ", args);
|
||||
char *commandLine = !joined.empty() ? &joined[0] : nullptr;
|
||||
STARTUPINFOA si = {};
|
||||
si.cb = sizeof(si);
|
||||
PROCESS_INFORMATION pi;
|
||||
// This .string() is wrong for non-ascii paths, but if we switch to CreateProcessW and use
|
||||
// .c_str() directly our commandLine argument will not be accepted (because it then has to be a
|
||||
// wchar_t* but out input is utf-8). Shame on you for this garbage API, Windows.
|
||||
if (!CreateProcessA(filename.string().c_str(), commandLine, nullptr, nullptr, false, 0, nullptr, nullptr, &si, &pi))
|
||||
{
|
||||
log::error(logcat, "CreateProcess failed. Error code {}", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
OXEN_DEFER {
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
};
|
||||
|
||||
if (!wait)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD result = WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
if (result != WAIT_OBJECT_0)
|
||||
{
|
||||
log::error(logcat, "WaitForSingleObject failed. Result {}, error code {}", result, GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
DWORD exitCode;
|
||||
if (!GetExitCodeProcess(pi.hProcess, &exitCode))
|
||||
{
|
||||
log::error(logcat, "GetExitCodeProcess failed. Error code {}", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
log::info(logcat, "Child exited with {}", exitCode);
|
||||
return static_cast<int>(exitCode);
|
||||
#else
|
||||
std::vector<char*> argv(args.size() + 1);
|
||||
for (size_t n = 0; n < args.size(); ++n)
|
||||
argv[n] = (char*)args[n].c_str();
|
||||
argv[args.size()] = NULL;
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0)
|
||||
{
|
||||
log::error(logcat, "Error forking: {}", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// child
|
||||
if (pid == 0)
|
||||
{
|
||||
tools::closefrom(3);
|
||||
close(0);
|
||||
char *envp[] = {NULL};
|
||||
execve(filename.c_str(), argv.data(), envp);
|
||||
log::error(logcat, "Failed to execve: {}", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// parent
|
||||
if (pid > 0)
|
||||
{
|
||||
if (!wait)
|
||||
{
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
int wstatus = 0;
|
||||
pid_t w = waitpid(pid, &wstatus, WUNTRACED | WCONTINUED);
|
||||
if (w < 0) {
|
||||
log::error(logcat, "Error waiting for child: {}", strerror(errno));
|
||||
std::string joined = tools::join(" ", args);
|
||||
char* commandLine = !joined.empty() ? &joined[0] : nullptr;
|
||||
STARTUPINFOA si = {};
|
||||
si.cb = sizeof(si);
|
||||
PROCESS_INFORMATION pi;
|
||||
// This .string() is wrong for non-ascii paths, but if we switch to CreateProcessW and use
|
||||
// .c_str() directly our commandLine argument will not be accepted (because it then has to be a
|
||||
// wchar_t* but out input is utf-8). Shame on you for this garbage API, Windows.
|
||||
if (!CreateProcessA(
|
||||
filename.string().c_str(),
|
||||
commandLine,
|
||||
nullptr,
|
||||
nullptr,
|
||||
false,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&si,
|
||||
&pi)) {
|
||||
log::error(logcat, "CreateProcess failed. Error code {}", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
if (WIFEXITED(wstatus))
|
||||
{
|
||||
log::info(logcat, "Child exited with {}", WEXITSTATUS(wstatus));
|
||||
return WEXITSTATUS(wstatus);
|
||||
}
|
||||
if (WIFSIGNALED(wstatus))
|
||||
{
|
||||
log::info(logcat, "Child killed by {}", WEXITSTATUS(wstatus));
|
||||
return WEXITSTATUS(wstatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
log::error(logcat, "Secret passage found");
|
||||
return -1;
|
||||
|
||||
OXEN_DEFER {
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
};
|
||||
|
||||
if (!wait) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD result = WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
if (result != WAIT_OBJECT_0) {
|
||||
log::error(
|
||||
logcat,
|
||||
"WaitForSingleObject failed. Result {}, error code {}",
|
||||
result,
|
||||
GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
DWORD exitCode;
|
||||
if (!GetExitCodeProcess(pi.hProcess, &exitCode)) {
|
||||
log::error(logcat, "GetExitCodeProcess failed. Error code {}", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
log::info(logcat, "Child exited with {}", exitCode);
|
||||
return static_cast<int>(exitCode);
|
||||
#else
|
||||
std::vector<char*> argv(args.size() + 1);
|
||||
for (size_t n = 0; n < args.size(); ++n)
|
||||
argv[n] = (char*)args[n].c_str();
|
||||
argv[args.size()] = NULL;
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
log::error(logcat, "Error forking: {}", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// child
|
||||
if (pid == 0) {
|
||||
tools::closefrom(3);
|
||||
close(0);
|
||||
char* envp[] = {NULL};
|
||||
execve(filename.c_str(), argv.data(), envp);
|
||||
log::error(logcat, "Failed to execve: {}", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// parent
|
||||
if (pid > 0) {
|
||||
if (!wait) {
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int wstatus = 0;
|
||||
pid_t w = waitpid(pid, &wstatus, WUNTRACED | WCONTINUED);
|
||||
if (w < 0) {
|
||||
log::error(logcat, "Error waiting for child: {}", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (WIFEXITED(wstatus)) {
|
||||
log::info(logcat, "Child exited with {}", WEXITSTATUS(wstatus));
|
||||
return WEXITSTATUS(wstatus);
|
||||
}
|
||||
if (WIFSIGNALED(wstatus)) {
|
||||
log::info(logcat, "Child killed by {}", WEXITSTATUS(wstatus));
|
||||
return WEXITSTATUS(wstatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
log::error(logcat, "Secret passage found");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -26,13 +26,13 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/fs.h"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
namespace tools {
|
||||
|
||||
int spawn(const fs::path& filename, const std::vector<std::string>& args, bool wait);
|
||||
|
||||
|
|
|
@ -1,117 +1,114 @@
|
|||
#include "string_util.h"
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace tools {
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
std::vector<std::string_view> split(std::string_view str, const std::string_view delim, bool trim)
|
||||
{
|
||||
std::vector<std::string_view> results;
|
||||
// Special case for empty delimiter: splits on each character boundary:
|
||||
if (delim.empty())
|
||||
{
|
||||
results.reserve(str.size());
|
||||
for (size_t i = 0; i < str.size(); i++)
|
||||
results.emplace_back(str.data() + i, 1);
|
||||
return results;
|
||||
}
|
||||
std::vector<std::string_view> split(std::string_view str, const std::string_view delim, bool trim) {
|
||||
std::vector<std::string_view> results;
|
||||
// Special case for empty delimiter: splits on each character boundary:
|
||||
if (delim.empty()) {
|
||||
results.reserve(str.size());
|
||||
for (size_t i = 0; i < str.size(); i++)
|
||||
results.emplace_back(str.data() + i, 1);
|
||||
return results;
|
||||
}
|
||||
|
||||
for (size_t pos = str.find(delim); pos != std::string_view::npos; pos = str.find(delim))
|
||||
{
|
||||
if (!trim || !results.empty() || pos > 0)
|
||||
results.push_back(str.substr(0, pos));
|
||||
str.remove_prefix(pos + delim.size());
|
||||
}
|
||||
if (!trim || str.size())
|
||||
results.push_back(str);
|
||||
else
|
||||
while (!results.empty() && results.back().empty())
|
||||
results.pop_back();
|
||||
return results;
|
||||
}
|
||||
|
||||
std::vector<std::string_view> split_any(std::string_view str, const std::string_view delims, bool trim)
|
||||
{
|
||||
if (delims.empty())
|
||||
return split(str, delims, trim);
|
||||
std::vector<std::string_view> results;
|
||||
for (size_t pos = str.find_first_of(delims); pos != std::string_view::npos; pos = str.find_first_of(delims))
|
||||
{
|
||||
if (!trim || !results.empty() || pos > 0)
|
||||
results.push_back(str.substr(0, pos));
|
||||
size_t until = str.find_first_not_of(delims, pos+1);
|
||||
if (until == std::string_view::npos)
|
||||
str.remove_prefix(str.size());
|
||||
for (size_t pos = str.find(delim); pos != std::string_view::npos; pos = str.find(delim)) {
|
||||
if (!trim || !results.empty() || pos > 0)
|
||||
results.push_back(str.substr(0, pos));
|
||||
str.remove_prefix(pos + delim.size());
|
||||
}
|
||||
if (!trim || str.size())
|
||||
results.push_back(str);
|
||||
else
|
||||
str.remove_prefix(until);
|
||||
}
|
||||
if (!trim || str.size())
|
||||
results.push_back(str);
|
||||
else
|
||||
while (!results.empty() && results.back().empty())
|
||||
results.pop_back();
|
||||
return results;
|
||||
while (!results.empty() && results.back().empty())
|
||||
results.pop_back();
|
||||
return results;
|
||||
}
|
||||
|
||||
void trim(std::string_view& s)
|
||||
{
|
||||
constexpr auto simple_whitespace = " \t\r\n"sv;
|
||||
auto pos = s.find_first_not_of(simple_whitespace);
|
||||
if (pos == std::string_view::npos) { // whole string is whitespace
|
||||
s.remove_prefix(s.size());
|
||||
return;
|
||||
}
|
||||
s.remove_prefix(pos);
|
||||
pos = s.find_last_not_of(simple_whitespace);
|
||||
assert(pos != std::string_view::npos);
|
||||
s.remove_suffix(s.size() - (pos + 1));
|
||||
std::vector<std::string_view> split_any(
|
||||
std::string_view str, const std::string_view delims, bool trim) {
|
||||
if (delims.empty())
|
||||
return split(str, delims, trim);
|
||||
std::vector<std::string_view> results;
|
||||
for (size_t pos = str.find_first_of(delims); pos != std::string_view::npos;
|
||||
pos = str.find_first_of(delims)) {
|
||||
if (!trim || !results.empty() || pos > 0)
|
||||
results.push_back(str.substr(0, pos));
|
||||
size_t until = str.find_first_not_of(delims, pos + 1);
|
||||
if (until == std::string_view::npos)
|
||||
str.remove_prefix(str.size());
|
||||
else
|
||||
str.remove_prefix(until);
|
||||
}
|
||||
if (!trim || str.size())
|
||||
results.push_back(str);
|
||||
else
|
||||
while (!results.empty() && results.back().empty())
|
||||
results.pop_back();
|
||||
return results;
|
||||
}
|
||||
|
||||
std::string lowercase_ascii_string(std::string_view src)
|
||||
{
|
||||
std::string result;
|
||||
result.reserve(src.size());
|
||||
for (char ch : src)
|
||||
result += ch >= 'A' && ch <= 'Z' ? ch + ('a' - 'A') : ch;
|
||||
return result;
|
||||
void trim(std::string_view& s) {
|
||||
constexpr auto simple_whitespace = " \t\r\n"sv;
|
||||
auto pos = s.find_first_not_of(simple_whitespace);
|
||||
if (pos == std::string_view::npos) { // whole string is whitespace
|
||||
s.remove_prefix(s.size());
|
||||
return;
|
||||
}
|
||||
s.remove_prefix(pos);
|
||||
pos = s.find_last_not_of(simple_whitespace);
|
||||
assert(pos != std::string_view::npos);
|
||||
s.remove_suffix(s.size() - (pos + 1));
|
||||
}
|
||||
|
||||
std::string lowercase_ascii_string(std::string_view src) {
|
||||
std::string result;
|
||||
result.reserve(src.size());
|
||||
for (char ch : src)
|
||||
result += ch >= 'A' && ch <= 'Z' ? ch + ('a' - 'A') : ch;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string friendly_duration(std::chrono::nanoseconds dur) {
|
||||
std::string friendly;
|
||||
auto append = std::back_inserter(friendly);
|
||||
bool some = false;
|
||||
if (dur >= 24h) {
|
||||
fmt::format_to(append, "{}d", dur / 24h);
|
||||
dur %= 24h;
|
||||
some = true;
|
||||
}
|
||||
if (dur >= 1h || some) {
|
||||
fmt::format_to(append, "{}h", dur / 1h);
|
||||
dur %= 1h;
|
||||
some = true;
|
||||
}
|
||||
if (dur >= 1min || some) {
|
||||
fmt::format_to(append, "{}m", dur / 1min);
|
||||
dur %= 1min;
|
||||
some = true;
|
||||
}
|
||||
if (some || dur == 0s) {
|
||||
// If we have >= minutes or its exactly 0 seconds then don't bother with fractional seconds
|
||||
fmt::format_to(append, "{}s", dur / 1s);
|
||||
} else {
|
||||
double seconds = std::chrono::duration<double>(dur).count();
|
||||
if (dur >= 1s)
|
||||
fmt::format_to(append, "{:.3f}s", seconds);
|
||||
else if (dur >= 1ms)
|
||||
fmt::format_to(append, "{:.3f}ms", seconds * 1000);
|
||||
else if (dur >= 1us)
|
||||
fmt::format_to(append, "{:.3f}µs", seconds * 1'000'000);
|
||||
else
|
||||
fmt::format_to(append, "{:.0f}ns", seconds * 1'000'000'000);
|
||||
}
|
||||
return friendly;
|
||||
std::string friendly;
|
||||
auto append = std::back_inserter(friendly);
|
||||
bool some = false;
|
||||
if (dur >= 24h) {
|
||||
fmt::format_to(append, "{}d", dur / 24h);
|
||||
dur %= 24h;
|
||||
some = true;
|
||||
}
|
||||
if (dur >= 1h || some) {
|
||||
fmt::format_to(append, "{}h", dur / 1h);
|
||||
dur %= 1h;
|
||||
some = true;
|
||||
}
|
||||
if (dur >= 1min || some) {
|
||||
fmt::format_to(append, "{}m", dur / 1min);
|
||||
dur %= 1min;
|
||||
some = true;
|
||||
}
|
||||
if (some || dur == 0s) {
|
||||
// If we have >= minutes or its exactly 0 seconds then don't bother with fractional seconds
|
||||
fmt::format_to(append, "{}s", dur / 1s);
|
||||
} else {
|
||||
double seconds = std::chrono::duration<double>(dur).count();
|
||||
if (dur >= 1s)
|
||||
fmt::format_to(append, "{:.3f}s", seconds);
|
||||
else if (dur >= 1ms)
|
||||
fmt::format_to(append, "{:.3f}ms", seconds * 1000);
|
||||
else if (dur >= 1us)
|
||||
fmt::format_to(append, "{:.3f}µs", seconds * 1'000'000);
|
||||
else
|
||||
fmt::format_to(append, "{:.0f}ns", seconds * 1'000'000'000);
|
||||
}
|
||||
return friendly;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
#pragma once
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <charconv>
|
||||
#include <chrono>
|
||||
#include <cassert>
|
||||
#include <fmt/format.h>
|
||||
#include "epee/span.h" // epee
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "epee/span.h" // epee
|
||||
|
||||
namespace tools {
|
||||
|
||||
|
@ -15,25 +17,27 @@ using namespace std::literals;
|
|||
|
||||
/// Returns true if the first string is equal to the second string, compared case-insensitively.
|
||||
inline bool string_iequal(std::string_view s1, std::string_view s2) {
|
||||
return std::equal(s1.begin(), s1.end(), s2.begin(), s2.end(), [](char a, char b) {
|
||||
return std::tolower(static_cast<unsigned char>(a)) == std::tolower(static_cast<unsigned char>(b)); });
|
||||
return std::equal(s1.begin(), s1.end(), s2.begin(), s2.end(), [](char a, char b) {
|
||||
return std::tolower(static_cast<unsigned char>(a)) ==
|
||||
std::tolower(static_cast<unsigned char>(b));
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns true if the first string matches any of the given strings case-insensitively. Arguments
|
||||
/// must be string literals, std::string, or std::string_views
|
||||
template <typename S1, typename... S>
|
||||
bool string_iequal_any(const S1& s1, const S&... s) {
|
||||
return (... || string_iequal(s1, s));
|
||||
return (... || string_iequal(s1, s));
|
||||
}
|
||||
|
||||
/// Returns true if the first argument begins with the second argument
|
||||
inline bool starts_with(std::string_view str, std::string_view prefix) {
|
||||
return str.substr(0, prefix.size()) == prefix;
|
||||
return str.substr(0, prefix.size()) == prefix;
|
||||
}
|
||||
|
||||
/// Returns true if the first argument ends with the second argument
|
||||
inline bool ends_with(std::string_view str, std::string_view suffix) {
|
||||
return str.size() >= suffix.size() && str.substr(str.size() - suffix.size()) == suffix;
|
||||
return str.size() >= suffix.size() && str.substr(str.size() - suffix.size()) == suffix;
|
||||
}
|
||||
|
||||
/// Splits a string on some delimiter string and returns a vector of string_view's pointing into the
|
||||
|
@ -49,7 +53,8 @@ inline bool ends_with(std::string_view str, std::string_view suffix) {
|
|||
/// auto v = split("-a--b--", "-"); // v is {"", "a", "", "b", "", ""}
|
||||
/// auto v = split("-a--b--", "-", true); // v is {"a", "", "b"}
|
||||
///
|
||||
std::vector<std::string_view> split(std::string_view str, std::string_view delim, bool trim = false);
|
||||
std::vector<std::string_view> split(
|
||||
std::string_view str, std::string_view delim, bool trim = false);
|
||||
|
||||
/// Splits a string on any 1 or more of the given delimiter characters and returns a vector of
|
||||
/// string_view's pointing into the pieces of the original string. If delims is empty this works
|
||||
|
@ -57,56 +62,58 @@ std::vector<std::string_view> split(std::string_view str, std::string_view delim
|
|||
/// pieces).
|
||||
///
|
||||
/// auto v = split_any("abcdedf", "dcx"); // v is {"ab", "e", "f"}
|
||||
std::vector<std::string_view> split_any(std::string_view str, std::string_view delims, bool trim = false);
|
||||
std::vector<std::string_view> split_any(
|
||||
std::string_view str, std::string_view delims, bool trim = false);
|
||||
|
||||
/// Joins [begin, end) with a delimiter and returns the resulting string. Elements can be anything
|
||||
/// that can be formatted. Semi-deprecated: this just uses fmt to join.
|
||||
template <typename It>
|
||||
std::string join(std::string_view delimiter, It begin, It end) {
|
||||
return fmt::format("{}", fmt::join(begin, end, delimiter));
|
||||
return fmt::format("{}", fmt::join(begin, end, delimiter));
|
||||
}
|
||||
|
||||
/// Same as the above, but works on a container. Just use fmt::join.
|
||||
template <typename Container>
|
||||
std::string join(std::string_view delimiter, const Container& c) {
|
||||
return fmt::format("{}", fmt::join(c, delimiter));
|
||||
return fmt::format("{}", fmt::join(c, delimiter));
|
||||
}
|
||||
|
||||
/// Similar to join(), but first applies a transformation to each element.
|
||||
template <typename It, typename UnaryOperation>
|
||||
std::string join_transform(std::string_view delimiter, It begin, It end, UnaryOperation transform) {
|
||||
std::string result;
|
||||
auto append = std::back_inserter(result);
|
||||
if (begin != end)
|
||||
result = fmt::format("{}", transform(*begin++));
|
||||
while (begin != end)
|
||||
fmt::format_to(append, "{}{}", delimiter, transform(*begin++));
|
||||
return result;
|
||||
std::string result;
|
||||
auto append = std::back_inserter(result);
|
||||
if (begin != end)
|
||||
result = fmt::format("{}", transform(*begin++));
|
||||
while (begin != end)
|
||||
fmt::format_to(append, "{}{}", delimiter, transform(*begin++));
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Wrapper around the above that takes a container and passes c.begin(), c.end().
|
||||
template <typename Container, typename UnaryOperation>
|
||||
std::string join_transform(std::string_view delimiter, const Container& c, UnaryOperation&& transform) {
|
||||
return join_transform(delimiter, c.begin(), c.end(), std::forward<UnaryOperation>(transform));
|
||||
std::string join_transform(
|
||||
std::string_view delimiter, const Container& c, UnaryOperation&& transform) {
|
||||
return join_transform(delimiter, c.begin(), c.end(), std::forward<UnaryOperation>(transform));
|
||||
}
|
||||
|
||||
/// Concatenates a bunch of random values together with delim as a separator via fmt::format.
|
||||
/// Returns the result as a string.
|
||||
template <typename T, typename... Ts>
|
||||
std::string join_stuff(std::string_view delim, T&& first, Ts&&... stuff) {
|
||||
std::string result = fmt::format(std::forward<T>(first));
|
||||
auto append = std::back_inserter(result);
|
||||
(fmt::format_to(append, "{}{}", delim, std::forward<Ts>(stuff)), ...);
|
||||
return result;
|
||||
std::string result = fmt::format(std::forward<T>(first));
|
||||
auto append = std::back_inserter(result);
|
||||
(fmt::format_to(append, "{}{}", delim, std::forward<Ts>(stuff)), ...);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Concatenates arguments via fmt::format operator, returns as a string.
|
||||
template <typename... T>
|
||||
std::string concat(T&&... stuff) {
|
||||
std::string result;
|
||||
auto append = std::back_inserter(result);
|
||||
(fmt::format_to(append, "{}", std::forward<T>(stuff)), ...);
|
||||
return result;
|
||||
std::string result;
|
||||
auto append = std::back_inserter(result);
|
||||
(fmt::format_to(append, "{}", std::forward<T>(stuff)), ...);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Simple version of whitespace trimming: mutates the given string view to remove leading
|
||||
|
@ -118,13 +125,13 @@ void trim(std::string_view& s);
|
|||
/// string was consumed.
|
||||
template <typename T>
|
||||
bool parse_int(const std::string_view str, T& value, int base = 10) {
|
||||
T tmp;
|
||||
auto* strend = str.data() + str.size();
|
||||
auto [p, ec] = std::from_chars(str.data(), strend, tmp, base);
|
||||
if (ec != std::errc() || p != strend)
|
||||
return false;
|
||||
value = tmp;
|
||||
return true;
|
||||
T tmp;
|
||||
auto* strend = str.data() + str.size();
|
||||
auto [p, ec] = std::from_chars(str.data(), strend, tmp, base);
|
||||
if (ec != std::errc() || p != strend)
|
||||
return false;
|
||||
value = tmp;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Returns a string_view that views the data of the given object; this is not something you want to
|
||||
|
@ -133,24 +140,26 @@ bool parse_int(const std::string_view str, T& value, int base = 10) {
|
|||
/// that aren't C++-trivial but are still designed to be accessed this way.
|
||||
template <typename T>
|
||||
std::string_view view_guts(const T& val) {
|
||||
static_assert((std::is_standard_layout_v<T> && std::has_unique_object_representations_v<T>)
|
||||
|| epee::is_byte_spannable<T>,
|
||||
"cannot safely access non-trivial class as string_view");
|
||||
return {reinterpret_cast<const char *>(&val), sizeof(val)};
|
||||
static_assert(
|
||||
(std::is_standard_layout_v<T> && std::has_unique_object_representations_v<T>) ||
|
||||
epee::is_byte_spannable<T>,
|
||||
"cannot safely access non-trivial class as string_view");
|
||||
return {reinterpret_cast<const char*>(&val), sizeof(val)};
|
||||
}
|
||||
|
||||
/// Convenience wrapper around the above that also copies the result into a new string
|
||||
template <typename T>
|
||||
std::string copy_guts(const T& val) {
|
||||
return std::string{view_guts(val)};
|
||||
return std::string{view_guts(val)};
|
||||
}
|
||||
|
||||
/// Function to reverse the above view_guts
|
||||
template <typename T>
|
||||
T make_from_guts(std::string_view s) {
|
||||
static_assert((std::is_standard_layout_v<T> && std::has_unique_object_representations_v<T>)
|
||||
|| epee::is_byte_spannable<T>,
|
||||
"cannot safely reconstitute a non-trivial class from data");
|
||||
static_assert(
|
||||
(std::is_standard_layout_v<T> && std::has_unique_object_representations_v<T>) ||
|
||||
epee::is_byte_spannable<T>,
|
||||
"cannot safely reconstitute a non-trivial class from data");
|
||||
if (s.size() != sizeof(T))
|
||||
throw std::runtime_error("Cannot reconstitute type: wrong type content size");
|
||||
T x;
|
||||
|
@ -166,13 +175,13 @@ std::string friendly_duration(std::chrono::nanoseconds dur);
|
|||
/// Converts a duration into a shorter, single-unit fractional display such as `42.3min`
|
||||
std::string short_duration(std::chrono::duration<double> dur);
|
||||
|
||||
/// Given an array of string arguments, look for strings of the format <prefix><value> and return <value>
|
||||
/// Returns empty string view if not found.
|
||||
/// Given an array of string arguments, look for strings of the format <prefix><value> and return
|
||||
/// <value> Returns empty string view if not found.
|
||||
template <typename It>
|
||||
std::string_view find_prefixed_value(It begin, It end, std::string_view prefix)
|
||||
{
|
||||
auto it = std::find_if(begin, end, [&](const auto& s) { return starts_with(s, prefix); });
|
||||
if (it == end) return {};
|
||||
return std::string_view{*it}.substr(prefix.size());
|
||||
}
|
||||
std::string_view find_prefixed_value(It begin, It end, std::string_view prefix) {
|
||||
auto it = std::find_if(begin, end, [&](const auto& s) { return starts_with(s, prefix); });
|
||||
if (it == end)
|
||||
return {};
|
||||
return std::string_view{*it}.substr(prefix.size());
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -25,154 +25,148 @@
|
|||
// 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.
|
||||
#include "epee/misc_log_ex.h"
|
||||
#include "logging/oxen_logger.h"
|
||||
#include "common/threadpool.h"
|
||||
|
||||
#include "cryptonote_config.h"
|
||||
#include "common/util.h"
|
||||
#include "cryptonote_config.h"
|
||||
#include "epee/misc_log_ex.h"
|
||||
#include "logging/oxen_logger.h"
|
||||
|
||||
static thread_local int depth = 0;
|
||||
static thread_local bool is_leaf = false;
|
||||
|
||||
namespace tools
|
||||
{
|
||||
namespace tools {
|
||||
|
||||
threadpool::threadpool(unsigned int max_threads) : running(true), active(0) {
|
||||
create(max_threads);
|
||||
create(max_threads);
|
||||
}
|
||||
|
||||
threadpool::~threadpool() {
|
||||
destroy();
|
||||
destroy();
|
||||
}
|
||||
|
||||
void threadpool::destroy() {
|
||||
try
|
||||
{
|
||||
const std::unique_lock lock{mutex};
|
||||
running = false;
|
||||
has_work.notify_all();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// if the lock throws, we're just do it without a lock and hope,
|
||||
// since the alternative is terminate
|
||||
running = false;
|
||||
has_work.notify_all();
|
||||
}
|
||||
for (size_t i = 0; i<threads.size(); i++) {
|
||||
try { threads[i].join(); }
|
||||
catch (...) { /* ignore */ }
|
||||
}
|
||||
threads.clear();
|
||||
try {
|
||||
const std::unique_lock lock{mutex};
|
||||
running = false;
|
||||
has_work.notify_all();
|
||||
} catch (...) {
|
||||
// if the lock throws, we're just do it without a lock and hope,
|
||||
// since the alternative is terminate
|
||||
running = false;
|
||||
has_work.notify_all();
|
||||
}
|
||||
for (size_t i = 0; i < threads.size(); i++) {
|
||||
try {
|
||||
threads[i].join();
|
||||
} catch (...) { /* ignore */
|
||||
}
|
||||
}
|
||||
threads.clear();
|
||||
}
|
||||
|
||||
void threadpool::recycle() {
|
||||
destroy();
|
||||
create(max);
|
||||
destroy();
|
||||
create(max);
|
||||
}
|
||||
|
||||
void threadpool::create(unsigned int max_threads) {
|
||||
const std::unique_lock lock{mutex};
|
||||
max = max_threads ? max_threads : tools::get_max_concurrency();
|
||||
running = true;
|
||||
for (size_t i = max ? max : 1; i > 0; i--) {
|
||||
threads.emplace_back([this] { run(false); });
|
||||
}
|
||||
const std::unique_lock lock{mutex};
|
||||
max = max_threads ? max_threads : tools::get_max_concurrency();
|
||||
running = true;
|
||||
for (size_t i = max ? max : 1; i > 0; i--) {
|
||||
threads.emplace_back([this] { run(false); });
|
||||
}
|
||||
}
|
||||
|
||||
void threadpool::submit(waiter *obj, std::function<void()> f, bool leaf) {
|
||||
CHECK_AND_ASSERT_THROW_MES(!is_leaf, "A leaf routine is using a thread pool");
|
||||
std::unique_lock lock{mutex};
|
||||
if (!leaf && ((active == max && !queue.empty()) || depth > 0)) {
|
||||
// if all available threads are already running
|
||||
// and there's work waiting, just run in current thread
|
||||
lock.unlock();
|
||||
++depth;
|
||||
is_leaf = leaf;
|
||||
f();
|
||||
--depth;
|
||||
is_leaf = false;
|
||||
} else {
|
||||
if (obj)
|
||||
obj->inc();
|
||||
if (leaf)
|
||||
queue.push_front({obj, f, leaf});
|
||||
else
|
||||
queue.push_back({obj, f, leaf});
|
||||
has_work.notify_one();
|
||||
}
|
||||
void threadpool::submit(waiter* obj, std::function<void()> f, bool leaf) {
|
||||
CHECK_AND_ASSERT_THROW_MES(!is_leaf, "A leaf routine is using a thread pool");
|
||||
std::unique_lock lock{mutex};
|
||||
if (!leaf && ((active == max && !queue.empty()) || depth > 0)) {
|
||||
// if all available threads are already running
|
||||
// and there's work waiting, just run in current thread
|
||||
lock.unlock();
|
||||
++depth;
|
||||
is_leaf = leaf;
|
||||
f();
|
||||
--depth;
|
||||
is_leaf = false;
|
||||
} else {
|
||||
if (obj)
|
||||
obj->inc();
|
||||
if (leaf)
|
||||
queue.push_front({obj, f, leaf});
|
||||
else
|
||||
queue.push_back({obj, f, leaf});
|
||||
has_work.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int threadpool::get_max_concurrency() const {
|
||||
return max;
|
||||
return max;
|
||||
}
|
||||
|
||||
threadpool::waiter::~waiter()
|
||||
{
|
||||
try
|
||||
{
|
||||
threadpool::waiter::~waiter() {
|
||||
try {
|
||||
std::unique_lock lock{mt};
|
||||
if (num)
|
||||
log::error(
|
||||
globallogcat, "wait should have been called before waiter dtor - waiting now");
|
||||
} catch (...) { /* ignore */
|
||||
}
|
||||
try {
|
||||
wait(NULL);
|
||||
} catch (const std::exception& e) {
|
||||
/* ignored */
|
||||
}
|
||||
}
|
||||
|
||||
void threadpool::waiter::wait(threadpool* tpool) {
|
||||
if (tpool)
|
||||
tpool->run(true);
|
||||
std::unique_lock lock{mt};
|
||||
if (num)
|
||||
log::error(globallogcat, "wait should have been called before waiter dtor - waiting now");
|
||||
}
|
||||
catch (...) { /* ignore */ }
|
||||
try
|
||||
{
|
||||
wait(NULL);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
/* ignored */
|
||||
}
|
||||
}
|
||||
|
||||
void threadpool::waiter::wait(threadpool *tpool) {
|
||||
if (tpool)
|
||||
tpool->run(true);
|
||||
std::unique_lock lock{mt};
|
||||
while(num)
|
||||
cv.wait(lock);
|
||||
while (num)
|
||||
cv.wait(lock);
|
||||
}
|
||||
|
||||
void threadpool::waiter::inc() {
|
||||
const std::unique_lock lock{mt};
|
||||
num++;
|
||||
const std::unique_lock lock{mt};
|
||||
num++;
|
||||
}
|
||||
|
||||
void threadpool::waiter::dec() {
|
||||
const std::unique_lock lock{mt};
|
||||
num--;
|
||||
if (!num)
|
||||
cv.notify_all();
|
||||
const std::unique_lock lock{mt};
|
||||
num--;
|
||||
if (!num)
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void threadpool::run(bool flush) {
|
||||
std::unique_lock lock{mutex};
|
||||
while (running) {
|
||||
entry e;
|
||||
while(queue.empty() && running)
|
||||
{
|
||||
if (flush)
|
||||
return;
|
||||
has_work.wait(lock);
|
||||
std::unique_lock lock{mutex};
|
||||
while (running) {
|
||||
entry e;
|
||||
while (queue.empty() && running) {
|
||||
if (flush)
|
||||
return;
|
||||
has_work.wait(lock);
|
||||
}
|
||||
if (!running)
|
||||
break;
|
||||
|
||||
active++;
|
||||
e = std::move(queue.front());
|
||||
queue.pop_front();
|
||||
lock.unlock();
|
||||
++depth;
|
||||
is_leaf = e.leaf;
|
||||
e.f();
|
||||
--depth;
|
||||
is_leaf = false;
|
||||
|
||||
if (e.wo)
|
||||
e.wo->dec();
|
||||
lock.lock();
|
||||
active--;
|
||||
}
|
||||
if (!running) break;
|
||||
|
||||
active++;
|
||||
e = std::move(queue.front());
|
||||
queue.pop_front();
|
||||
lock.unlock();
|
||||
++depth;
|
||||
is_leaf = e.leaf;
|
||||
e.f();
|
||||
--depth;
|
||||
is_leaf = false;
|
||||
|
||||
if (e.wo)
|
||||
e.wo->dec();
|
||||
lock.lock();
|
||||
active--;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -27,66 +27,65 @@
|
|||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace tools
|
||||
{
|
||||
namespace tools {
|
||||
//! A global thread pool
|
||||
class threadpool
|
||||
{
|
||||
public:
|
||||
static threadpool& getInstance() {
|
||||
static threadpool instance;
|
||||
return instance;
|
||||
}
|
||||
static threadpool *getNewForUnitTests(unsigned max_threads = 0) {
|
||||
return new threadpool(max_threads);
|
||||
}
|
||||
class threadpool {
|
||||
public:
|
||||
static threadpool& getInstance() {
|
||||
static threadpool instance;
|
||||
return instance;
|
||||
}
|
||||
static threadpool* getNewForUnitTests(unsigned max_threads = 0) {
|
||||
return new threadpool(max_threads);
|
||||
}
|
||||
|
||||
// The waiter lets the caller know when all of its
|
||||
// tasks are completed.
|
||||
class waiter {
|
||||
std::mutex mt;
|
||||
std::condition_variable cv;
|
||||
int num;
|
||||
public:
|
||||
void inc();
|
||||
void dec();
|
||||
void wait(threadpool *tpool); //! Wait for a set of tasks to finish.
|
||||
waiter() : num(0){}
|
||||
~waiter();
|
||||
};
|
||||
// The waiter lets the caller know when all of its
|
||||
// tasks are completed.
|
||||
class waiter {
|
||||
std::mutex mt;
|
||||
std::condition_variable cv;
|
||||
int num;
|
||||
|
||||
// Submit a task to the pool. The waiter pointer may be
|
||||
// NULL if the caller doesn't care to wait for the
|
||||
// task to finish.
|
||||
void submit(waiter *waiter, std::function<void()> f, bool leaf = false);
|
||||
public:
|
||||
void inc();
|
||||
void dec();
|
||||
void wait(threadpool* tpool); //! Wait for a set of tasks to finish.
|
||||
waiter() : num(0) {}
|
||||
~waiter();
|
||||
};
|
||||
|
||||
// destroy and recreate threads
|
||||
void recycle();
|
||||
// Submit a task to the pool. The waiter pointer may be
|
||||
// NULL if the caller doesn't care to wait for the
|
||||
// task to finish.
|
||||
void submit(waiter* waiter, std::function<void()> f, bool leaf = false);
|
||||
|
||||
unsigned int get_max_concurrency() const;
|
||||
// destroy and recreate threads
|
||||
void recycle();
|
||||
|
||||
~threadpool();
|
||||
void stop();
|
||||
void start(unsigned int max_threads = 0);
|
||||
unsigned int get_max_concurrency() const;
|
||||
|
||||
~threadpool();
|
||||
void stop();
|
||||
void start(unsigned int max_threads = 0);
|
||||
|
||||
private:
|
||||
threadpool(unsigned int max_threads = 0);
|
||||
void destroy();
|
||||
void create(unsigned int max_threads);
|
||||
typedef struct entry {
|
||||
waiter *wo;
|
||||
std::function<void()> f;
|
||||
bool leaf;
|
||||
waiter* wo;
|
||||
std::function<void()> f;
|
||||
bool leaf;
|
||||
} entry;
|
||||
std::deque<entry> queue;
|
||||
std::condition_variable has_work;
|
||||
|
@ -98,4 +97,4 @@ public:
|
|||
void run(bool flush = false);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -25,7 +25,7 @@
|
|||
// 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.
|
||||
//
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
|
@ -34,110 +34,111 @@
|
|||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace serialization
|
||||
{
|
||||
namespace boost { namespace serialization {
|
||||
template <class Archive, class h_key, class hval>
|
||||
inline void save(Archive &a, const std::unordered_map<h_key, hval> &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
size_t s = x.size();
|
||||
a << s;
|
||||
for(auto& v: x)
|
||||
{
|
||||
a << v.first;
|
||||
a << v.second;
|
||||
}
|
||||
inline void save(
|
||||
Archive& a,
|
||||
const std::unordered_map<h_key, hval>& x,
|
||||
const boost::serialization::version_type ver) {
|
||||
size_t s = x.size();
|
||||
a << s;
|
||||
for (auto& v : x) {
|
||||
a << v.first;
|
||||
a << v.second;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive, class h_key, class hval>
|
||||
inline void load(Archive &a, std::unordered_map<h_key, hval> &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
x.clear();
|
||||
size_t s = 0;
|
||||
a >> s;
|
||||
for(size_t i = 0; i != s; i++)
|
||||
{
|
||||
h_key k;
|
||||
hval v;
|
||||
a >> k;
|
||||
a >> v;
|
||||
x.insert(std::pair<h_key, hval>(k, v));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class Archive, class h_key, class hval>
|
||||
inline void save(Archive &a, const std::unordered_multimap<h_key, hval> &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
size_t s = x.size();
|
||||
a << s;
|
||||
for(auto& v: x)
|
||||
{
|
||||
a << v.first;
|
||||
a << v.second;
|
||||
}
|
||||
inline void load(
|
||||
Archive& a,
|
||||
std::unordered_map<h_key, hval>& x,
|
||||
const boost::serialization::version_type ver) {
|
||||
x.clear();
|
||||
size_t s = 0;
|
||||
a >> s;
|
||||
for (size_t i = 0; i != s; i++) {
|
||||
h_key k;
|
||||
hval v;
|
||||
a >> k;
|
||||
a >> v;
|
||||
x.insert(std::pair<h_key, hval>(k, v));
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive, class h_key, class hval>
|
||||
inline void load(Archive &a, std::unordered_multimap<h_key, hval> &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
x.clear();
|
||||
size_t s = 0;
|
||||
a >> s;
|
||||
for(size_t i = 0; i != s; i++)
|
||||
{
|
||||
h_key k;
|
||||
hval v;
|
||||
a >> k;
|
||||
a >> v;
|
||||
x.emplace(k, v);
|
||||
}
|
||||
inline void save(
|
||||
Archive& a,
|
||||
const std::unordered_multimap<h_key, hval>& x,
|
||||
const boost::serialization::version_type ver) {
|
||||
size_t s = x.size();
|
||||
a << s;
|
||||
for (auto& v : x) {
|
||||
a << v.first;
|
||||
a << v.second;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class Archive, class hval>
|
||||
inline void save(Archive &a, const std::unordered_set<hval> &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
size_t s = x.size();
|
||||
a << s;
|
||||
for(auto& v: x)
|
||||
{
|
||||
a << v;
|
||||
}
|
||||
template <class Archive, class h_key, class hval>
|
||||
inline void load(
|
||||
Archive& a,
|
||||
std::unordered_multimap<h_key, hval>& x,
|
||||
const boost::serialization::version_type ver) {
|
||||
x.clear();
|
||||
size_t s = 0;
|
||||
a >> s;
|
||||
for (size_t i = 0; i != s; i++) {
|
||||
h_key k;
|
||||
hval v;
|
||||
a >> k;
|
||||
a >> v;
|
||||
x.emplace(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive, class hval>
|
||||
inline void load(Archive &a, std::unordered_set<hval> &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
x.clear();
|
||||
size_t s = 0;
|
||||
a >> s;
|
||||
for(size_t i = 0; i != s; i++)
|
||||
{
|
||||
hval v;
|
||||
a >> v;
|
||||
x.insert(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class Archive, class h_key, class hval>
|
||||
inline void serialize(Archive &a, std::unordered_map<h_key, hval> &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
split_free(a, x, ver);
|
||||
}
|
||||
|
||||
template <class Archive, class h_key, class hval>
|
||||
inline void serialize(Archive &a, std::unordered_multimap<h_key, hval> &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
split_free(a, x, ver);
|
||||
inline void save(
|
||||
Archive& a,
|
||||
const std::unordered_set<hval>& x,
|
||||
const boost::serialization::version_type ver) {
|
||||
size_t s = x.size();
|
||||
a << s;
|
||||
for (auto& v : x) {
|
||||
a << v;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive, class hval>
|
||||
inline void serialize(Archive &a, std::unordered_set<hval> &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
split_free(a, x, ver);
|
||||
inline void load(
|
||||
Archive& a, std::unordered_set<hval>& x, const boost::serialization::version_type ver) {
|
||||
x.clear();
|
||||
size_t s = 0;
|
||||
a >> s;
|
||||
for (size_t i = 0; i != s; i++) {
|
||||
hval v;
|
||||
a >> v;
|
||||
x.insert(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive, class h_key, class hval>
|
||||
inline void serialize(
|
||||
Archive& a,
|
||||
std::unordered_map<h_key, hval>& x,
|
||||
const boost::serialization::version_type ver) {
|
||||
split_free(a, x, ver);
|
||||
}
|
||||
|
||||
template <class Archive, class h_key, class hval>
|
||||
inline void serialize(
|
||||
Archive& a,
|
||||
std::unordered_multimap<h_key, hval>& x,
|
||||
const boost::serialization::version_type ver) {
|
||||
split_free(a, x, ver);
|
||||
}
|
||||
|
||||
template <class Archive, class hval>
|
||||
inline void serialize(
|
||||
Archive& a, std::unordered_set<hval>& x, const boost::serialization::version_type ver) {
|
||||
split_free(a, x, ver);
|
||||
}
|
||||
}} // namespace boost::serialization
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright (c) 2018, The Loki Project
|
||||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
//
|
||||
// 3. 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
|
||||
|
@ -26,149 +26,147 @@
|
|||
// 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.
|
||||
//
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
#include "util.h"
|
||||
|
||||
#include <fmt/chrono.h>
|
||||
#include <fmt/color.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "crypto/crypto.h"
|
||||
#include "epee/misc_log_ex.h"
|
||||
#include "epee/readline_buffer.h"
|
||||
#include "epee/string_tools.h"
|
||||
#include "epee/wipeable_string.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "util.h"
|
||||
#include "epee/readline_buffer.h"
|
||||
#include "epee/misc_log_ex.h"
|
||||
#include "i18n.h"
|
||||
#include "logging/oxen_logger.h"
|
||||
#include "string_util.h"
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
#ifdef __GLIBC__
|
||||
#include <sys/resource.h>
|
||||
#include <gnu/libc-version.h>
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
namespace tools
|
||||
{
|
||||
static auto logcat = log::Cat("util");
|
||||
namespace tools {
|
||||
static auto logcat = log::Cat("util");
|
||||
|
||||
bool disable_core_dumps()
|
||||
{
|
||||
bool disable_core_dumps() {
|
||||
#ifdef __GLIBC__
|
||||
// disable core dumps in release mode
|
||||
struct rlimit rlimit;
|
||||
rlimit.rlim_cur = rlimit.rlim_max = 0;
|
||||
if (setrlimit(RLIMIT_CORE, &rlimit))
|
||||
{
|
||||
log::warning(logcat, "Failed to disable core dumps");
|
||||
return false;
|
||||
if (setrlimit(RLIMIT_CORE, &rlimit)) {
|
||||
log::warning(logcat, "Failed to disable core dumps");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t get_lockable_memory()
|
||||
{
|
||||
ssize_t get_lockable_memory() {
|
||||
#ifdef __GLIBC__
|
||||
struct rlimit rlim;
|
||||
if (getrlimit(RLIMIT_MEMLOCK, &rlim) < 0)
|
||||
{
|
||||
log::error(logcat, "Failed to determine the lockable memory limit");
|
||||
return -1;
|
||||
if (getrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
|
||||
log::error(logcat, "Failed to determine the lockable memory limit");
|
||||
return -1;
|
||||
}
|
||||
return rlim.rlim_cur;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool on_startup()
|
||||
{
|
||||
bool on_startup() {
|
||||
#ifdef __GLIBC__
|
||||
const char *ver = ::gnu_get_libc_version();
|
||||
const char* ver = ::gnu_get_libc_version();
|
||||
if (!strcmp(ver, "2.25"))
|
||||
log::warning(logcat, fg(fmt::terminal_color::red), "Running with glibc {}, hangs may occur - change glibc version if possible", ver);
|
||||
log::warning(
|
||||
logcat,
|
||||
fg(fmt::terminal_color::red),
|
||||
"Running with glibc {}, hangs may occur - change glibc version if possible",
|
||||
ver);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
namespace
|
||||
{
|
||||
}
|
||||
namespace {
|
||||
std::mutex max_concurrency_lock;
|
||||
unsigned max_concurrency = std::thread::hardware_concurrency();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void set_max_concurrency(unsigned n)
|
||||
{
|
||||
void set_max_concurrency(unsigned n) {
|
||||
if (n < 1)
|
||||
n = std::thread::hardware_concurrency();
|
||||
n = std::thread::hardware_concurrency();
|
||||
unsigned hwc = std::thread::hardware_concurrency();
|
||||
if (n > hwc)
|
||||
n = hwc;
|
||||
n = hwc;
|
||||
std::lock_guard lock{max_concurrency_lock};
|
||||
max_concurrency = n;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned get_max_concurrency()
|
||||
{
|
||||
unsigned get_max_concurrency() {
|
||||
std::lock_guard lock{max_concurrency_lock};
|
||||
return max_concurrency;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_local_address(const std::string &address)
|
||||
{
|
||||
return address == "localhost"sv
|
||||
|| (tools::starts_with(address, "127."sv) && address.find_first_not_of("0123456789."sv) == std::string::npos)
|
||||
|| address == "::1"sv
|
||||
|| address == "[::1]"sv; // There are other uncommon ways to specify localhost (e.g. 0::1, ::0001) but don't worry about them.
|
||||
}
|
||||
bool is_local_address(const std::string& address) {
|
||||
return address == "localhost"sv ||
|
||||
(tools::starts_with(address, "127."sv) &&
|
||||
address.find_first_not_of("0123456789."sv) == std::string::npos) ||
|
||||
address == "::1"sv ||
|
||||
address == "[::1]"sv; // There are other uncommon ways to specify localhost (e.g. 0::1,
|
||||
// ::0001) but don't worry about them.
|
||||
}
|
||||
|
||||
int vercmp(std::string_view v0, std::string_view v1)
|
||||
{
|
||||
int vercmp(std::string_view v0, std::string_view v1) {
|
||||
auto f0 = tools::split_any(v0, ".-");
|
||||
auto f1 = tools::split_any(v1, ".-");
|
||||
const auto max = std::max(f0.size(), f1.size());
|
||||
for (size_t i = 0; i < max; ++i) {
|
||||
if (i >= f0.size())
|
||||
return -1;
|
||||
if (i >= f1.size())
|
||||
return 1;
|
||||
int f0i = 0, f1i = 0;
|
||||
tools::parse_int(f0[i], f0i);
|
||||
tools::parse_int(f1[i], f1i);
|
||||
int n = f0i - f1i;
|
||||
if (n)
|
||||
return n;
|
||||
if (i >= f0.size())
|
||||
return -1;
|
||||
if (i >= f1.size())
|
||||
return 1;
|
||||
int f0i = 0, f1i = 0;
|
||||
tools::parse_int(f0[i], f0i);
|
||||
tools::parse_int(f1[i], f1i);
|
||||
int n = f0i - f1i;
|
||||
if (n)
|
||||
return n;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::pair<uint32_t, uint32_t>> parse_subaddress_lookahead(const std::string& str)
|
||||
{
|
||||
std::optional<std::pair<uint32_t, uint32_t>> parse_subaddress_lookahead(const std::string& str) {
|
||||
auto pos = str.find(":");
|
||||
bool r = pos != std::string::npos;
|
||||
uint32_t major;
|
||||
r = r && epee::string_tools::get_xtype_from_string(major, str.substr(0, pos));
|
||||
uint32_t minor;
|
||||
r = r && epee::string_tools::get_xtype_from_string(minor, str.substr(pos + 1));
|
||||
if (r)
|
||||
{
|
||||
return std::make_pair(major, minor);
|
||||
if (r) {
|
||||
return std::make_pair(major, minor);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string input_line_win()
|
||||
{
|
||||
HANDLE hConIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
std::string input_line_win() {
|
||||
HANDLE hConIn = CreateFileW(
|
||||
L"CONIN$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
nullptr,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
nullptr);
|
||||
DWORD oldMode;
|
||||
|
||||
FlushConsoleInputBuffer(hConIn);
|
||||
|
@ -178,63 +176,61 @@ namespace tools
|
|||
wchar_t buffer[1024];
|
||||
DWORD read;
|
||||
|
||||
ReadConsoleW(hConIn, buffer, sizeof(buffer)/sizeof(wchar_t)-1, &read, nullptr);
|
||||
ReadConsoleW(hConIn, buffer, sizeof(buffer) / sizeof(wchar_t) - 1, &read, nullptr);
|
||||
buffer[read] = 0;
|
||||
|
||||
SetConsoleMode(hConIn, oldMode);
|
||||
CloseHandle(hConIn);
|
||||
|
||||
|
||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, NULL, 0, NULL, NULL);
|
||||
std::string buf(size_needed, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, buffer, -1, &buf[0], size_needed, NULL, NULL);
|
||||
buf.pop_back(); //size_needed includes null that we needed to have space for
|
||||
buf.pop_back(); // size_needed includes null that we needed to have space for
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string get_human_readable_timestamp(std::time_t t)
|
||||
{
|
||||
std::string get_human_readable_timestamp(std::time_t t) {
|
||||
if (t < 1234567890)
|
||||
return "<unknown>";
|
||||
return "<unknown>";
|
||||
return "{:%Y-%m-%d %H:%M:%S} UTC"_format(fmt::gmtime(t));
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_human_readable_timespan(std::chrono::seconds seconds)
|
||||
{
|
||||
std::string get_human_readable_timespan(std::chrono::seconds seconds) {
|
||||
uint64_t ts = seconds.count();
|
||||
if (ts < 60)
|
||||
return std::to_string(ts) + tr(" seconds");
|
||||
return std::to_string(ts) + tr(" seconds");
|
||||
if (ts < 3600)
|
||||
return std::to_string((uint64_t)(ts / 60)) + tr(" minutes");
|
||||
return std::to_string((uint64_t)(ts / 60)) + tr(" minutes");
|
||||
if (ts < 3600 * 24)
|
||||
return std::to_string((uint64_t)(ts / 3600)) + tr(" hours");
|
||||
return std::to_string((uint64_t)(ts / 3600)) + tr(" hours");
|
||||
if (ts < 3600 * 24 * 30.5)
|
||||
return std::to_string((uint64_t)(ts / (3600 * 24))) + tr(" days");
|
||||
return std::to_string((uint64_t)(ts / (3600 * 24))) + tr(" days");
|
||||
if (ts < 3600 * 24 * 365.25)
|
||||
return std::to_string((uint64_t)(ts / (3600 * 24 * 30.5))) + tr(" months");
|
||||
return std::to_string((uint64_t)(ts / (3600 * 24 * 30.5))) + tr(" months");
|
||||
return tr("a long time");
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_human_readable_bytes(uint64_t bytes)
|
||||
{
|
||||
if (bytes < 1000) return std::to_string(bytes) + " B";
|
||||
std::string get_human_readable_bytes(uint64_t bytes) {
|
||||
if (bytes < 1000)
|
||||
return std::to_string(bytes) + " B";
|
||||
constexpr std::array units{" kB", " MB", " GB", " TB"};
|
||||
double b = bytes;
|
||||
for (const auto& suffix : units) {
|
||||
b /= 1000.;
|
||||
if (b < 1000.) {
|
||||
std::ostringstream o;
|
||||
o << std::fixed << std::setprecision(2) << b;
|
||||
return o.str() + suffix;
|
||||
}
|
||||
b /= 1000.;
|
||||
if (b < 1000.) {
|
||||
std::ostringstream o;
|
||||
o << std::fixed << std::setprecision(2) << b;
|
||||
return o.str() + suffix;
|
||||
}
|
||||
}
|
||||
return std::to_string(std::lround(b)) + units.back();
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate a "sync weight" over ranges of blocks in the blockchain, suitable for
|
||||
// calculating sync time estimates
|
||||
uint64_t cumulative_block_sync_weight(cryptonote::network_type nettype, uint64_t start_block, uint64_t num_blocks)
|
||||
{
|
||||
// Calculate a "sync weight" over ranges of blocks in the blockchain, suitable for
|
||||
// calculating sync time estimates
|
||||
uint64_t cumulative_block_sync_weight(
|
||||
cryptonote::network_type nettype, uint64_t start_block, uint64_t num_blocks) {
|
||||
// No detailed data available except for Mainnet: Give back the number of blocks
|
||||
// as a very simple and non-varying block sync weight for ranges of Testnet and
|
||||
// Devnet blocks
|
||||
|
@ -309,5 +305,5 @@ namespace tools
|
|||
}
|
||||
return weight;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue