mirror of
https://github.com/oxen-io/oxen-core.git
synced 2023-12-14 02:22:56 +01:00
run clang format
This commit is contained in:
parent
45db02de78
commit
13409ad00e
|
@ -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,7 +1,6 @@
|
|||
#include "common.hpp"
|
||||
|
||||
PYBIND11_MODULE(pywallet3, m)
|
||||
{
|
||||
PYBIND11_MODULE(pywallet3, m) {
|
||||
wallet::Wallet_Init(m);
|
||||
wallet::Keyring_Init(m);
|
||||
wallet::KeyringManager_Init(m);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
} // 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")
|
||||
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);
|
||||
}
|
||||
.def_readwrite(
|
||||
"append_network_type_to_datadir",
|
||||
&GeneralWalletConfig::append_network_type_to_datadir);
|
||||
}
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
#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)
|
||||
{
|
||||
#include "../common.hpp"
|
||||
|
||||
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) {
|
||||
.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;
|
||||
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;
|
||||
|
@ -23,9 +27,10 @@ namespace wallet
|
|||
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)); }))
|
||||
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)
|
||||
{
|
||||
#include "../common.hpp"
|
||||
|
||||
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)); }))
|
||||
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);
|
||||
}
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
} // 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);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
#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::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;
|
||||
|
@ -21,21 +24,27 @@ void omq_logger(oxenmq::LogLevel level, const char* file, int line, std::string
|
|||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
.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));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wallet
|
||||
|
|
|
@ -27,73 +27,66 @@
|
|||
// 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"
|
||||
};
|
||||
"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-salvage", "Try to salvage a blockchain database if it seems corrupted", false};
|
||||
|
||||
BlockchainDB *new_db()
|
||||
{
|
||||
BlockchainDB* new_db() {
|
||||
return new BlockchainLMDB();
|
||||
}
|
||||
|
||||
void BlockchainDB::init_options(boost::program_options::options_description& desc)
|
||||
{
|
||||
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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
} else {
|
||||
tx_hash = *tx_hash_ptr;
|
||||
}
|
||||
|
||||
bool has_blacklisted_outputs = false;
|
||||
if (tx.version >= cryptonote::txversion::v2_ringct)
|
||||
{
|
||||
if (tx.version >= cryptonote::txversion::v2_ringct) {
|
||||
if (!tx_prunable_hash_ptr)
|
||||
tx_prunable_hash = get_transaction_prunable_hash(tx, &txp.second);
|
||||
else
|
||||
|
@ -101,28 +94,24 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair
|
|||
|
||||
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))
|
||||
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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
} 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))
|
||||
{
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
@ -136,32 +125,30 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair
|
|||
|
||||
// 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (tx.version >= cryptonote::txversion::v3_per_output_unlock_times) {
|
||||
unlock_time = tx.output_unlock_times[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
} 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)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,15 +158,14 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair
|
|||
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;
|
||||
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())
|
||||
|
@ -202,12 +188,10 @@ uint64_t BlockchainDB::add_block( const std::pair<block, std::string>& blck
|
|||
|
||||
int tx_i = 0;
|
||||
crypto::hash tx_hash{};
|
||||
for (const std::pair<transaction, std::string>& tx : txs)
|
||||
{
|
||||
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)
|
||||
{
|
||||
for (const auto& vout : tx.first.vout) {
|
||||
if (vout.amount == 0)
|
||||
++num_rct_outs;
|
||||
}
|
||||
|
@ -217,7 +201,14 @@ uint64_t BlockchainDB::add_block( const std::pair<block, std::string>& blck
|
|||
|
||||
// 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);
|
||||
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;
|
||||
|
@ -225,14 +216,12 @@ uint64_t BlockchainDB::add_block( const std::pair<block, std::string>& blck
|
|||
return prev_height;
|
||||
}
|
||||
|
||||
void BlockchainDB::pop_block(block& blk, std::vector<transaction>& txs)
|
||||
{
|
||||
void BlockchainDB::pop_block(block& blk, std::vector<transaction>& txs) {
|
||||
blk = get_top_block();
|
||||
|
||||
remove_block();
|
||||
|
||||
for (auto it = blk.tx_hashes.rbegin(); it != blk.tx_hashes.rend(); ++it)
|
||||
{
|
||||
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))
|
||||
|
@ -243,14 +232,11 @@ void BlockchainDB::pop_block(block& blk, std::vector<transaction>& txs)
|
|||
remove_transaction(get_transaction_hash(blk.miner_tx));
|
||||
}
|
||||
|
||||
void BlockchainDB::remove_transaction(const crypto::hash& tx_hash)
|
||||
{
|
||||
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))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -259,20 +245,17 @@ void BlockchainDB::remove_transaction(const crypto::hash& tx_hash)
|
|||
remove_transaction_data(tx_hash, tx);
|
||||
}
|
||||
|
||||
block_header BlockchainDB::get_block_header(const crypto::hash& h) const
|
||||
{
|
||||
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 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
|
||||
{
|
||||
bool BlockchainDB::get_tx(const crypto::hash& h, cryptonote::transaction& tx) const {
|
||||
std::string bd;
|
||||
if (!get_tx_blob(h, bd))
|
||||
return false;
|
||||
|
@ -282,43 +265,38 @@ bool BlockchainDB::get_tx(const crypto::hash& h, cryptonote::transaction &tx) co
|
|||
return true;
|
||||
}
|
||||
|
||||
bool BlockchainDB::get_pruned_tx(const crypto::hash& h, cryptonote::transaction &tx) const
|
||||
{
|
||||
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))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
transaction BlockchainDB::get_tx(const crypto::hash& h) const
|
||||
{
|
||||
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
|
||||
{
|
||||
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 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()
|
||||
{
|
||||
void BlockchainDB::reset_stats() {
|
||||
num_calls = 0;
|
||||
time_blk_hash = 0ns;
|
||||
time_tx_exists = 0ns;
|
||||
|
@ -327,9 +305,10 @@ void BlockchainDB::reset_stats()
|
|||
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,11 +316,15 @@ 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)
|
||||
{
|
||||
void BlockchainDB::fixup(cryptonote::network_type) {
|
||||
if (is_read_only()) {
|
||||
log::info(logcat, "Database is opened read only - skipping fixup check");
|
||||
return;
|
||||
|
@ -350,11 +333,13 @@ void BlockchainDB::fixup(cryptonote::network_type)
|
|||
set_batch_transactions(true);
|
||||
}
|
||||
|
||||
bool BlockchainDB::get_immutable_checkpoint(checkpoint_t *immutable_checkpoint, uint64_t block_height) const
|
||||
{
|
||||
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 "
|
||||
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);
|
||||
|
@ -362,21 +347,19 @@ bool BlockchainDB::get_immutable_checkpoint(checkpoint_t *immutable_checkpoint,
|
|||
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_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)
|
||||
{
|
||||
} 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.
|
||||
} else {
|
||||
return false; // NOTE: Only one service node checkpoint recorded, we can override this
|
||||
// checkpoint.
|
||||
}
|
||||
|
||||
if (immutable_checkpoint)
|
||||
|
@ -385,11 +368,9 @@ bool BlockchainDB::get_immutable_checkpoint(checkpoint_t *immutable_checkpoint,
|
|||
return true;
|
||||
}
|
||||
|
||||
uint64_t BlockchainDB::get_tx_block_height(const crypto::hash &h) const
|
||||
{
|
||||
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())
|
||||
{
|
||||
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));
|
||||
|
@ -397,34 +378,33 @@ uint64_t BlockchainDB::get_tx_block_height(const crypto::hash &h) const
|
|||
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
|
||||
{
|
||||
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))
|
||||
{
|
||||
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
|
||||
{
|
||||
try {
|
||||
serialization::binary_string_unarchiver ba{blob};
|
||||
serialization::value(ba, *header);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
} 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,
|
||||
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
|
||||
{
|
||||
uint64_t timestamps_difficulty_height) const {
|
||||
constexpr uint64_t MIN_CHAIN_HEIGHT = 2;
|
||||
if (chain_height < MIN_CHAIN_HEIGHT)
|
||||
return;
|
||||
|
@ -436,35 +416,33 @@ void BlockchainDB::fill_timestamps_and_difficulties_for_pow(cryptonote::network_
|
|||
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)
|
||||
{
|
||||
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).
|
||||
// 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++)
|
||||
{
|
||||
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,
|
||||
add_timestamp_and_difficulty(
|
||||
nettype,
|
||||
chain_height,
|
||||
timestamps,
|
||||
difficulties,
|
||||
get_block_timestamp(top_block_height),
|
||||
get_block_cumulative_difficulty(top_block_height));
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace cryptonote
|
||||
|
|
|
@ -30,9 +30,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
#include "common/command_line.h"
|
||||
#include "common/fs.h"
|
||||
#include "crypto/hash.h"
|
||||
|
@ -99,8 +100,7 @@ namespace service_nodes {
|
|||
struct proof_info;
|
||||
}
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
namespace cryptonote {
|
||||
struct checkpoint_t;
|
||||
|
||||
/** a pair of <transaction hash, output index>, typedef for convenience */
|
||||
|
@ -114,8 +114,7 @@ extern const command_line::arg_descriptor<bool, false> arg_db_salvage;
|
|||
/**
|
||||
* @brief a struct containing output metadata
|
||||
*/
|
||||
struct output_data_t
|
||||
{
|
||||
struct output_data_t {
|
||||
crypto::public_key pubkey; //!< the output's public key (for spend verification)
|
||||
uint64_t unlock_time; //!< the output's unlock time (or height)
|
||||
uint64_t height; //!< the height of the block which created the output
|
||||
|
@ -124,24 +123,21 @@ struct output_data_t
|
|||
#pragma pack(pop)
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct tx_data_t
|
||||
{
|
||||
struct tx_data_t {
|
||||
uint64_t tx_id;
|
||||
uint64_t unlock_time;
|
||||
uint64_t block_id;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct alt_block_data_1_t
|
||||
{
|
||||
struct alt_block_data_1_t {
|
||||
uint64_t height;
|
||||
uint64_t cumulative_weight;
|
||||
uint64_t cumulative_difficulty;
|
||||
uint64_t already_generated_coins;
|
||||
};
|
||||
|
||||
struct alt_block_data_t
|
||||
{
|
||||
struct alt_block_data_t {
|
||||
uint64_t height;
|
||||
uint64_t cumulative_weight;
|
||||
uint64_t cumulative_difficulty;
|
||||
|
@ -152,8 +148,7 @@ struct alt_block_data_t
|
|||
/**
|
||||
* @brief a struct containing txpool per transaction metadata
|
||||
*/
|
||||
struct txpool_tx_meta_t
|
||||
{
|
||||
struct txpool_tx_meta_t {
|
||||
crypto::hash max_used_block_id;
|
||||
crypto::hash last_failed_id;
|
||||
uint64_t weight;
|
||||
|
@ -166,8 +161,8 @@ struct txpool_tx_meta_t
|
|||
uint8_t kept_by_block;
|
||||
uint8_t relayed;
|
||||
uint8_t do_not_relay;
|
||||
uint8_t double_spend_seen: 1;
|
||||
uint8_t bf_padding: 7;
|
||||
uint8_t double_spend_seen : 1;
|
||||
uint8_t bf_padding : 7;
|
||||
|
||||
uint8_t padding[76]; // till 192 bytes
|
||||
};
|
||||
|
@ -185,8 +180,7 @@ struct txpool_tx_meta_t
|
|||
/**
|
||||
* @brief A base class for BlockchainDB exceptions
|
||||
*/
|
||||
class DB_EXCEPTION : public std::runtime_error
|
||||
{
|
||||
class DB_EXCEPTION : public std::runtime_error {
|
||||
public:
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
@ -194,148 +188,133 @@ class DB_EXCEPTION : public std::runtime_error
|
|||
/**
|
||||
* @brief A generic BlockchainDB exception
|
||||
*/
|
||||
class DB_ERROR : public DB_EXCEPTION
|
||||
{
|
||||
class DB_ERROR : public DB_EXCEPTION {
|
||||
public:
|
||||
using DB_EXCEPTION::DB_EXCEPTION;
|
||||
DB_ERROR() : DB_EXCEPTION("Generic DB Error") { }
|
||||
DB_ERROR() : DB_EXCEPTION("Generic DB Error") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief thrown when there is an error starting a DB transaction
|
||||
*/
|
||||
class DB_ERROR_TXN_START : public DB_EXCEPTION
|
||||
{
|
||||
class DB_ERROR_TXN_START : public DB_EXCEPTION {
|
||||
public:
|
||||
using DB_EXCEPTION::DB_EXCEPTION;
|
||||
DB_ERROR_TXN_START() : DB_EXCEPTION("DB Error in starting txn") { }
|
||||
DB_ERROR_TXN_START() : DB_EXCEPTION("DB Error in starting txn") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief thrown when opening the BlockchainDB fails
|
||||
*/
|
||||
class DB_OPEN_FAILURE : public DB_EXCEPTION
|
||||
{
|
||||
class DB_OPEN_FAILURE : public DB_EXCEPTION {
|
||||
public:
|
||||
using DB_EXCEPTION::DB_EXCEPTION;
|
||||
DB_OPEN_FAILURE() : DB_EXCEPTION("Failed to open the db") { }
|
||||
DB_OPEN_FAILURE() : DB_EXCEPTION("Failed to open the db") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief thrown when creating the BlockchainDB fails
|
||||
*/
|
||||
class DB_CREATE_FAILURE : public DB_EXCEPTION
|
||||
{
|
||||
class DB_CREATE_FAILURE : public DB_EXCEPTION {
|
||||
public:
|
||||
using DB_EXCEPTION::DB_EXCEPTION;
|
||||
DB_CREATE_FAILURE() : DB_EXCEPTION("Failed to create the db") { }
|
||||
DB_CREATE_FAILURE() : DB_EXCEPTION("Failed to create the db") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief thrown when synchronizing the BlockchainDB to disk fails
|
||||
*/
|
||||
class DB_SYNC_FAILURE : public DB_EXCEPTION
|
||||
{
|
||||
class DB_SYNC_FAILURE : public DB_EXCEPTION {
|
||||
public:
|
||||
using DB_EXCEPTION::DB_EXCEPTION;
|
||||
DB_SYNC_FAILURE() : DB_EXCEPTION("Failed to sync the db") { }
|
||||
DB_SYNC_FAILURE() : DB_EXCEPTION("Failed to sync the db") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief thrown when a requested block does not exist
|
||||
*/
|
||||
class BLOCK_DNE : public DB_EXCEPTION
|
||||
{
|
||||
class BLOCK_DNE : public DB_EXCEPTION {
|
||||
public:
|
||||
using DB_EXCEPTION::DB_EXCEPTION;
|
||||
BLOCK_DNE() : DB_EXCEPTION("The block requested does not exist") { }
|
||||
BLOCK_DNE() : DB_EXCEPTION("The block requested does not exist") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief thrown when a block's parent does not exist (and it needed to)
|
||||
*/
|
||||
class BLOCK_PARENT_DNE : public DB_EXCEPTION
|
||||
{
|
||||
class BLOCK_PARENT_DNE : public DB_EXCEPTION {
|
||||
public:
|
||||
using DB_EXCEPTION::DB_EXCEPTION;
|
||||
BLOCK_PARENT_DNE() : DB_EXCEPTION("The parent of the block does not exist") { }
|
||||
BLOCK_PARENT_DNE() : DB_EXCEPTION("The parent of the block does not exist") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief thrown when a block exists, but shouldn't, namely when adding a block
|
||||
*/
|
||||
class BLOCK_EXISTS : public DB_EXCEPTION
|
||||
{
|
||||
class BLOCK_EXISTS : public DB_EXCEPTION {
|
||||
public:
|
||||
using DB_EXCEPTION::DB_EXCEPTION;
|
||||
BLOCK_EXISTS() : DB_EXCEPTION("The block to be added already exists!") { }
|
||||
BLOCK_EXISTS() : DB_EXCEPTION("The block to be added already exists!") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief thrown when something is wrong with the block to be added
|
||||
*/
|
||||
class BLOCK_INVALID : public DB_EXCEPTION
|
||||
{
|
||||
class BLOCK_INVALID : public DB_EXCEPTION {
|
||||
public:
|
||||
using DB_EXCEPTION::DB_EXCEPTION;
|
||||
BLOCK_INVALID() : DB_EXCEPTION("The block to be added did not pass validation!") { }
|
||||
BLOCK_INVALID() : DB_EXCEPTION("The block to be added did not pass validation!") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief thrown when a requested transaction does not exist
|
||||
*/
|
||||
class TX_DNE : public DB_EXCEPTION
|
||||
{
|
||||
class TX_DNE : public DB_EXCEPTION {
|
||||
public:
|
||||
using DB_EXCEPTION::DB_EXCEPTION;
|
||||
TX_DNE() : DB_EXCEPTION("The transaction requested does not exist") { }
|
||||
TX_DNE() : DB_EXCEPTION("The transaction requested does not exist") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief thrown when a transaction exists, but shouldn't, namely when adding a block
|
||||
*/
|
||||
class TX_EXISTS : public DB_EXCEPTION
|
||||
{
|
||||
class TX_EXISTS : public DB_EXCEPTION {
|
||||
public:
|
||||
using DB_EXCEPTION::DB_EXCEPTION;
|
||||
TX_EXISTS() : DB_EXCEPTION("The transaction to be added already exists!") { }
|
||||
TX_EXISTS() : DB_EXCEPTION("The transaction to be added already exists!") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief thrown when a requested output does not exist
|
||||
*/
|
||||
class OUTPUT_DNE : public DB_EXCEPTION
|
||||
{
|
||||
class OUTPUT_DNE : public DB_EXCEPTION {
|
||||
public:
|
||||
using DB_EXCEPTION::DB_EXCEPTION;
|
||||
OUTPUT_DNE() : DB_EXCEPTION("The output requested does not exist!") { }
|
||||
OUTPUT_DNE() : DB_EXCEPTION("The output requested does not exist!") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief thrown when an output exists, but shouldn't, namely when adding a block
|
||||
*/
|
||||
class OUTPUT_EXISTS : public DB_EXCEPTION
|
||||
{
|
||||
class OUTPUT_EXISTS : public DB_EXCEPTION {
|
||||
public:
|
||||
using DB_EXCEPTION::DB_EXCEPTION;
|
||||
OUTPUT_EXISTS() : DB_EXCEPTION("The output to be added already exists!") { }
|
||||
OUTPUT_EXISTS() : DB_EXCEPTION("The output to be added already exists!") {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief thrown when a spent key image exists, but shouldn't, namely when adding a block
|
||||
*/
|
||||
class KEY_IMAGE_EXISTS : public DB_EXCEPTION
|
||||
{
|
||||
class KEY_IMAGE_EXISTS : public DB_EXCEPTION {
|
||||
public:
|
||||
using DB_EXCEPTION::DB_EXCEPTION;
|
||||
KEY_IMAGE_EXISTS() : DB_EXCEPTION("The spent key image to be added already exists!") { }
|
||||
KEY_IMAGE_EXISTS() : DB_EXCEPTION("The spent key image to be added already exists!") {}
|
||||
};
|
||||
|
||||
/***********************************
|
||||
* End of Exception Definitions
|
||||
***********************************/
|
||||
|
||||
|
||||
/**
|
||||
* @brief The BlockchainDB backing store interface declaration/contract
|
||||
*
|
||||
|
@ -348,9 +327,8 @@ class KEY_IMAGE_EXISTS : public DB_EXCEPTION
|
|||
* A subclass which encounters an issue should report that issue by throwing
|
||||
* a DB_EXCEPTION which adequately conveys the issue.
|
||||
*/
|
||||
class BlockchainDB
|
||||
{
|
||||
private:
|
||||
class BlockchainDB {
|
||||
private:
|
||||
/*********************************************************************
|
||||
* private virtual members
|
||||
*********************************************************************/
|
||||
|
@ -372,21 +350,23 @@ private:
|
|||
* @param coins_generated the number of coins generated total after this block
|
||||
* @param blk_hash the hash of the block
|
||||
*/
|
||||
virtual 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& blk_hash
|
||||
) = 0;
|
||||
virtual 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& blk_hash) = 0;
|
||||
|
||||
/**
|
||||
* @brief remove data about the top block
|
||||
*
|
||||
* The subclass implementing this will remove the block data from the top
|
||||
* block in the chain. The data to be removed is that which was added in
|
||||
* BlockchainDB::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, const crypto::hash& blk_hash)
|
||||
* BlockchainDB::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, const crypto::hash& blk_hash)
|
||||
*
|
||||
* If any of this cannot be done, the subclass should throw the corresponding
|
||||
* subclass of DB_EXCEPTION
|
||||
|
@ -413,7 +393,11 @@ private:
|
|||
* @param tx_prunable_hash the hash of the prunable part of the transaction
|
||||
* @return the transaction ID
|
||||
*/
|
||||
virtual 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) = 0;
|
||||
virtual 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) = 0;
|
||||
|
||||
/**
|
||||
* @brief remove data about a transaction
|
||||
|
@ -459,7 +443,12 @@ private:
|
|||
* @param commitment the rct commitment to the output amount
|
||||
* @return amount output index
|
||||
*/
|
||||
virtual 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) = 0;
|
||||
virtual 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) = 0;
|
||||
|
||||
/**
|
||||
* @brief store amount output indices for a tx's outputs
|
||||
|
@ -474,7 +463,8 @@ private:
|
|||
* @param tx_id ID of the transaction containing these outputs
|
||||
* @param amount_output_indices the amount output indices of the transaction
|
||||
*/
|
||||
virtual void add_tx_amount_output_indices(const uint64_t tx_id, const std::vector<uint64_t>& amount_output_indices) = 0;
|
||||
virtual void add_tx_amount_output_indices(
|
||||
const uint64_t tx_id, const std::vector<uint64_t>& amount_output_indices) = 0;
|
||||
|
||||
/**
|
||||
* @brief store a spent key
|
||||
|
@ -500,7 +490,6 @@ private:
|
|||
*/
|
||||
virtual void remove_spent_key(const crypto::key_image& k_image) = 0;
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
* private concrete members
|
||||
*********************************************************************/
|
||||
|
@ -527,9 +516,7 @@ private:
|
|||
std::chrono::nanoseconds time_add_block1 = 0ns; //!< a performance metric
|
||||
std::chrono::nanoseconds time_add_transaction = 0ns; //!< a performance metric
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief helper function for add_transactions, to add each individual transaction
|
||||
*
|
||||
|
@ -539,16 +526,20 @@ protected:
|
|||
* @param blk_hash hash of the block which has the transaction
|
||||
* @param tx the transaction to add
|
||||
* @param tx_hash_ptr the hash of the transaction, if already calculated
|
||||
* @param tx_prunable_hash_ptr the hash of the prunable part of the transaction, if already calculated
|
||||
* @param tx_prunable_hash_ptr the hash of the prunable part of the transaction, if already
|
||||
* calculated
|
||||
*/
|
||||
void add_transaction(const crypto::hash& blk_hash, const std::pair<transaction, std::string>& tx, const crypto::hash* tx_hash_ptr = NULL, const crypto::hash* tx_prunable_hash_ptr = NULL);
|
||||
void add_transaction(
|
||||
const crypto::hash& blk_hash,
|
||||
const std::pair<transaction, std::string>& tx,
|
||||
const crypto::hash* tx_hash_ptr = NULL,
|
||||
const crypto::hash* tx_prunable_hash_ptr = NULL);
|
||||
|
||||
mutable std::chrono::nanoseconds time_tx_exists = 0ns; //!< a performance metric
|
||||
std::chrono::nanoseconds time_commit1 = 0ns; //!< a performance metric
|
||||
bool m_auto_remove_logs = true; //!< whether or not to automatically remove old logs
|
||||
|
||||
public:
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief An empty constructor.
|
||||
*/
|
||||
|
@ -596,7 +587,8 @@ public:
|
|||
* @param filename a path referring to the BlockchainDB to open
|
||||
* @param db_flags flags relevant to how to open/use the BlockchainDB
|
||||
*/
|
||||
virtual void open(const fs::path& filename, cryptonote::network_type nettype, const int db_flags = 0) = 0;
|
||||
virtual void open(
|
||||
const fs::path& filename, cryptonote::network_type nettype, const int db_flags = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief Gets the current open/ready state of the BlockchainDB
|
||||
|
@ -668,7 +660,8 @@ public:
|
|||
* which can fail if the existing database file is in an incompatible format.
|
||||
* As such, this function needs to be called before calling open().
|
||||
*
|
||||
* @param folder The path of the folder containing the database file(s) which must not end with slash '/'.
|
||||
* @param folder The path of the folder containing the database file(s) which must not end
|
||||
* with slash '/'.
|
||||
*
|
||||
* @return true if the operation is succesfull
|
||||
*/
|
||||
|
@ -685,7 +678,6 @@ public:
|
|||
*/
|
||||
virtual std::string get_db_name() const = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @brief acquires the BlockchainDB lock
|
||||
*
|
||||
|
@ -736,7 +728,7 @@ public:
|
|||
*
|
||||
* @return true if we started the batch, false if already started
|
||||
*/
|
||||
virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) = 0;
|
||||
virtual bool batch_start(uint64_t batch_num_blocks = 0, uint64_t batch_bytes = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief ends a batch transaction
|
||||
|
@ -814,23 +806,28 @@ public:
|
|||
*
|
||||
* @return the height of the chain post-addition
|
||||
*/
|
||||
virtual 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
|
||||
);
|
||||
virtual 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);
|
||||
|
||||
virtual void update_block_checkpoint(checkpoint_t const &checkpoint) = 0;
|
||||
virtual void update_block_checkpoint(checkpoint_t const& checkpoint) = 0;
|
||||
virtual void remove_block_checkpoint(uint64_t height) = 0;
|
||||
virtual bool get_block_checkpoint (uint64_t height, checkpoint_t &checkpoint) const = 0;
|
||||
virtual bool get_top_checkpoint (checkpoint_t &checkpoint) const = 0;
|
||||
virtual bool get_block_checkpoint(uint64_t height, checkpoint_t& checkpoint) const = 0;
|
||||
virtual bool get_top_checkpoint(checkpoint_t& checkpoint) const = 0;
|
||||
|
||||
// num_desired_checkpoints: set to GET_ALL_CHECKPOINTS to collect as many checkpoints as possible
|
||||
// num_desired_checkpoints: set to GET_ALL_CHECKPOINTS to collect as many checkpoints as
|
||||
// possible
|
||||
static constexpr size_t GET_ALL_CHECKPOINTS = 0;
|
||||
virtual std::vector<checkpoint_t> get_checkpoints_range(uint64_t start, uint64_t end, size_t num_desired_checkpoints = GET_ALL_CHECKPOINTS) const = 0;
|
||||
virtual bool get_immutable_checkpoint(checkpoint_t *immutable_checkpoint, uint64_t block_height) const;
|
||||
virtual std::vector<checkpoint_t> get_checkpoints_range(
|
||||
uint64_t start,
|
||||
uint64_t end,
|
||||
size_t num_desired_checkpoints = GET_ALL_CHECKPOINTS) const = 0;
|
||||
virtual bool get_immutable_checkpoint(
|
||||
checkpoint_t* immutable_checkpoint, uint64_t block_height) const;
|
||||
|
||||
/**
|
||||
* @brief checks if a block exists
|
||||
|
@ -840,7 +837,7 @@ public:
|
|||
*
|
||||
* @return true of the block exists, otherwise false
|
||||
*/
|
||||
virtual bool block_exists(const crypto::hash& h, uint64_t *height = NULL) const = 0;
|
||||
virtual bool block_exists(const crypto::hash& h, uint64_t* height = NULL) const = 0;
|
||||
|
||||
/**
|
||||
* @brief fetches the block with the given hash
|
||||
|
@ -961,7 +958,8 @@ public:
|
|||
*
|
||||
* @return the cumulative number of rct outputs
|
||||
*/
|
||||
virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const = 0;
|
||||
virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(
|
||||
const std::vector<uint64_t>& heights) const = 0;
|
||||
|
||||
/**
|
||||
* @brief fetch the top block's timestamp
|
||||
|
@ -1059,7 +1057,8 @@ public:
|
|||
*
|
||||
* @return the weights
|
||||
*/
|
||||
virtual std::vector<uint64_t> get_long_term_block_weights(uint64_t start_height, size_t count) const = 0;
|
||||
virtual std::vector<uint64_t> get_long_term_block_weights(
|
||||
uint64_t start_height, size_t count) const = 0;
|
||||
|
||||
/**
|
||||
* @brief fetch a block's hash
|
||||
|
@ -1107,18 +1106,20 @@ public:
|
|||
*
|
||||
* @return a vector of block hashes
|
||||
*/
|
||||
virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const = 0;
|
||||
virtual std::vector<crypto::hash> get_hashes_range(
|
||||
const uint64_t& h1, const uint64_t& h2) const = 0;
|
||||
|
||||
/**
|
||||
* @brief fetch the top block's hash
|
||||
*
|
||||
* The subclass should return the hash of the most recent block
|
||||
*
|
||||
* @param block_height if non NULL, returns the height of that block (ie, the blockchain height minus 1)
|
||||
* @param block_height if non NULL, returns the height of that block (ie, the blockchain height
|
||||
* minus 1)
|
||||
*
|
||||
* @return the top block's hash
|
||||
*/
|
||||
virtual crypto::hash top_block_hash(uint64_t *block_height = NULL) const = 0;
|
||||
virtual crypto::hash top_block_hash(uint64_t* block_height = NULL) const = 0;
|
||||
|
||||
/**
|
||||
* @brief fetch the top block
|
||||
|
@ -1138,7 +1139,6 @@ public:
|
|||
*/
|
||||
virtual uint64_t height() const = 0;
|
||||
|
||||
|
||||
/**
|
||||
* <!--
|
||||
* TODO: Rewrite (if necessary) such that all calls to remove_* are
|
||||
|
@ -1161,7 +1161,6 @@ public:
|
|||
*/
|
||||
virtual void pop_block(block& blk, std::vector<transaction>& txs);
|
||||
|
||||
|
||||
/**
|
||||
* @brief check if a transaction with a given hash exists
|
||||
*
|
||||
|
@ -1240,7 +1239,7 @@ public:
|
|||
*
|
||||
* @return true iff the transaction was found
|
||||
*/
|
||||
virtual bool get_tx(const crypto::hash& h, transaction &tx) const;
|
||||
virtual bool get_tx(const crypto::hash& h, transaction& tx) const;
|
||||
|
||||
/**
|
||||
* @brief fetches the transaction base with the given hash
|
||||
|
@ -1251,7 +1250,7 @@ public:
|
|||
*
|
||||
* @return true iff the transaction was found
|
||||
*/
|
||||
virtual bool get_pruned_tx(const crypto::hash& h, transaction &tx) const;
|
||||
virtual bool get_pruned_tx(const crypto::hash& h, transaction& tx) const;
|
||||
|
||||
/**
|
||||
* @brief fetches the transaction blob with the given hash
|
||||
|
@ -1265,7 +1264,7 @@ public:
|
|||
*
|
||||
* @return true iff the transaction was found
|
||||
*/
|
||||
virtual bool get_tx_blob(const crypto::hash& h, std::string &tx) const = 0;
|
||||
virtual bool get_tx_blob(const crypto::hash& h, std::string& tx) const = 0;
|
||||
|
||||
/**
|
||||
* @brief fetches the pruned transaction blob with the given hash
|
||||
|
@ -1279,10 +1278,11 @@ public:
|
|||
*
|
||||
* @return true iff the transaction was found
|
||||
*/
|
||||
virtual bool get_pruned_tx_blob(const crypto::hash& h, std::string &tx) const = 0;
|
||||
virtual bool get_pruned_tx_blob(const crypto::hash& h, std::string& tx) const = 0;
|
||||
|
||||
/**
|
||||
* @brief fetches a number of pruned transaction blob from the given hash, in canonical blockchain order
|
||||
* @brief fetches a number of pruned transaction blob from the given hash, in canonical
|
||||
* blockchain order
|
||||
*
|
||||
* The subclass should return the pruned transactions stored from the one with the given
|
||||
* hash.
|
||||
|
@ -1295,7 +1295,8 @@ public:
|
|||
*
|
||||
* @return true iff the transactions were found
|
||||
*/
|
||||
virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<std::string> &bd) const = 0;
|
||||
virtual bool get_pruned_tx_blobs_from(
|
||||
const crypto::hash& h, size_t count, std::vector<std::string>& bd) const = 0;
|
||||
|
||||
/**
|
||||
* @brief fetches the prunable transaction blob with the given hash
|
||||
|
@ -1310,7 +1311,7 @@ public:
|
|||
*
|
||||
* @return true iff the transaction was found and we have its prunable data
|
||||
*/
|
||||
virtual bool get_prunable_tx_blob(const crypto::hash& h, std::string &tx) const = 0;
|
||||
virtual bool get_prunable_tx_blob(const crypto::hash& h, std::string& tx) const = 0;
|
||||
|
||||
/**
|
||||
* @brief fetches the prunable transaction hash
|
||||
|
@ -1323,7 +1324,8 @@ public:
|
|||
*
|
||||
* @return true iff the transaction was found
|
||||
*/
|
||||
virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const = 0;
|
||||
virtual bool get_prunable_tx_hash(
|
||||
const crypto::hash& tx_hash, crypto::hash& prunable_hash) const = 0;
|
||||
|
||||
/**
|
||||
* @brief fetches the total number of transactions ever
|
||||
|
@ -1379,7 +1381,8 @@ public:
|
|||
*
|
||||
* @return vector of heights corresponding to each hash, or (uint64_t)-1 if not found
|
||||
*/
|
||||
virtual std::vector<uint64_t> get_tx_block_heights(const std::vector<crypto::hash> &h) const = 0;
|
||||
virtual std::vector<uint64_t> get_tx_block_heights(
|
||||
const std::vector<crypto::hash>& h) const = 0;
|
||||
|
||||
// returns the total number of outputs of amount <amount>
|
||||
/**
|
||||
|
@ -1420,7 +1423,10 @@ public:
|
|||
*
|
||||
* @return the requested output data
|
||||
*/
|
||||
virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index, bool include_commitmemt = true) const = 0;
|
||||
virtual output_data_t get_output_key(
|
||||
const uint64_t& amount,
|
||||
const uint64_t& index,
|
||||
bool include_commitmemt = true) const = 0;
|
||||
|
||||
/**
|
||||
* @brief gets an output's tx hash and index
|
||||
|
@ -1446,7 +1452,8 @@ public:
|
|||
*
|
||||
* @return the tx hash and output index
|
||||
*/
|
||||
virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const = 0;
|
||||
virtual tx_out_index get_output_tx_and_index(
|
||||
const uint64_t& amount, const uint64_t& index) const = 0;
|
||||
|
||||
/**
|
||||
* @brief gets some outputs' tx hashes and indices
|
||||
|
@ -1459,7 +1466,10 @@ public:
|
|||
* @param offsets a list of amount-specific output indices
|
||||
* @param indices return-by-reference a list of tx hashes and output indices (as pairs)
|
||||
*/
|
||||
virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<tx_out_index> &indices) const = 0;
|
||||
virtual void get_output_tx_and_index(
|
||||
const uint64_t& amount,
|
||||
const std::vector<uint64_t>& offsets,
|
||||
std::vector<tx_out_index>& indices) const = 0;
|
||||
|
||||
/**
|
||||
* @brief gets outputs' data
|
||||
|
@ -1472,7 +1482,11 @@ public:
|
|||
* @param offsets a list of amount-specific output indices
|
||||
* @param outputs return-by-reference a list of outputs' metadata
|
||||
*/
|
||||
virtual 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 = 0;
|
||||
virtual 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 = 0;
|
||||
|
||||
/*
|
||||
* FIXME: Need to check with git blame and ask what this does to
|
||||
|
@ -1495,7 +1509,8 @@ public:
|
|||
*
|
||||
* @return a list of amount-specific output indices
|
||||
*/
|
||||
virtual std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(const uint64_t tx_id, size_t n_txes = 1) const = 0;
|
||||
virtual std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(
|
||||
const uint64_t tx_id, size_t n_txes = 1) const = 0;
|
||||
|
||||
/**
|
||||
* @brief check if a key image is stored as spent
|
||||
|
@ -1511,7 +1526,8 @@ public:
|
|||
*
|
||||
* @param details the details of the transaction to add
|
||||
*/
|
||||
virtual void add_txpool_tx(const crypto::hash &txid, const std::string &blob, const txpool_tx_meta_t& details) = 0;
|
||||
virtual void add_txpool_tx(
|
||||
const crypto::hash& txid, const std::string& blob, const txpool_tx_meta_t& details) = 0;
|
||||
|
||||
/**
|
||||
* @brief update a txpool transaction's metadata
|
||||
|
@ -1519,7 +1535,7 @@ public:
|
|||
* @param txid the txid of the transaction to update
|
||||
* @param details the details of the transaction to update
|
||||
*/
|
||||
virtual void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t& details) = 0;
|
||||
virtual void update_txpool_tx(const crypto::hash& txid, const txpool_tx_meta_t& details) = 0;
|
||||
|
||||
/**
|
||||
* @brief get the number of transactions in the txpool
|
||||
|
@ -1529,7 +1545,7 @@ public:
|
|||
/**
|
||||
* @brief check whether a txid is in the txpool
|
||||
*/
|
||||
virtual bool txpool_has_tx(const crypto::hash &txid) const = 0;
|
||||
virtual bool txpool_has_tx(const crypto::hash& txid) const = 0;
|
||||
|
||||
/**
|
||||
* @brief remove a txpool transaction
|
||||
|
@ -1546,7 +1562,7 @@ public:
|
|||
*
|
||||
* @return true if the tx meta was found, false otherwise
|
||||
*/
|
||||
virtual bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const = 0;
|
||||
virtual bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t& meta) const = 0;
|
||||
|
||||
/**
|
||||
* @brief get a txpool transaction's blob
|
||||
|
@ -1556,7 +1572,7 @@ public:
|
|||
*
|
||||
* @return true if the txid was in the txpool, false otherwise
|
||||
*/
|
||||
virtual bool get_txpool_tx_blob(const crypto::hash& txid, std::string &bd) const = 0;
|
||||
virtual bool get_txpool_tx_blob(const crypto::hash& txid, std::string& bd) const = 0;
|
||||
|
||||
/**
|
||||
* @brief get a txpool transaction's blob
|
||||
|
@ -1620,7 +1636,11 @@ public:
|
|||
* @param: data: the metadata for the block
|
||||
* @param: blob: the block's blob
|
||||
*/
|
||||
virtual void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const std::string &blob, const std::string *checkpoint) = 0;
|
||||
virtual void add_alt_block(
|
||||
const crypto::hash& blkid,
|
||||
const cryptonote::alt_block_data_t& data,
|
||||
const std::string& blob,
|
||||
const std::string* checkpoint) = 0;
|
||||
|
||||
/**
|
||||
* @brief get an alternative block by hash
|
||||
|
@ -1631,7 +1651,11 @@ public:
|
|||
*
|
||||
* @return true if the block was found in the alternative blocks list, false otherwise
|
||||
*/
|
||||
virtual bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, std::string *blob, std::string *checkpoint) const = 0;
|
||||
virtual bool get_alt_block(
|
||||
const crypto::hash& blkid,
|
||||
alt_block_data_t* data,
|
||||
std::string* blob,
|
||||
std::string* checkpoint) const = 0;
|
||||
|
||||
/**
|
||||
* @brief get the block header from the alternative block db
|
||||
|
@ -1643,14 +1667,18 @@ public:
|
|||
*
|
||||
* @return true if the block was found in the alternative blocks list, false otherwise
|
||||
*/
|
||||
bool get_alt_block_header(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::block_header *header, std::string *checkpoint) const;
|
||||
bool get_alt_block_header(
|
||||
const crypto::hash& blkid,
|
||||
alt_block_data_t* data,
|
||||
cryptonote::block_header* header,
|
||||
std::string* checkpoint) const;
|
||||
|
||||
/**
|
||||
* @brief remove an alternative block
|
||||
*
|
||||
* @param: blkid the block hash
|
||||
*/
|
||||
virtual void remove_alt_block(const crypto::hash &blkid) = 0;
|
||||
virtual void remove_alt_block(const crypto::hash& blkid) = 0;
|
||||
|
||||
/**
|
||||
* @brief get the number of alternative blocks stored
|
||||
|
@ -1675,7 +1703,10 @@ public:
|
|||
*
|
||||
* @return false if the function returns false for any transaction, otherwise true
|
||||
*/
|
||||
virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const std::string*)>, bool include_blob = false, bool include_unrelayed_txes = true) const = 0;
|
||||
virtual bool for_all_txpool_txes(
|
||||
std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const std::string*)>,
|
||||
bool include_blob = false,
|
||||
bool include_unrelayed_txes = true) const = 0;
|
||||
|
||||
/**
|
||||
* @brief runs a function over all key images stored
|
||||
|
@ -1710,7 +1741,10 @@ public:
|
|||
*
|
||||
* @return false if the function returns false for any block, otherwise true
|
||||
*/
|
||||
virtual bool for_blocks_range(const uint64_t& h1, const uint64_t& h2, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const = 0;
|
||||
virtual bool for_blocks_range(
|
||||
const uint64_t& h1,
|
||||
const uint64_t& h2,
|
||||
std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const = 0;
|
||||
|
||||
/**
|
||||
* @brief runs a function over all transactions stored
|
||||
|
@ -1729,7 +1763,9 @@ public:
|
|||
*
|
||||
* @return false if the function returns false for any transaction, otherwise true
|
||||
*/
|
||||
virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const = 0;
|
||||
virtual bool for_all_transactions(
|
||||
std::function<bool(const crypto::hash&, const cryptonote::transaction&)>,
|
||||
bool pruned) const = 0;
|
||||
|
||||
/**
|
||||
* @brief runs a function over all outputs stored
|
||||
|
@ -1748,8 +1784,12 @@ public:
|
|||
*
|
||||
* @return false if the function returns false for any output, otherwise 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 = 0;
|
||||
virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const = 0;
|
||||
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 = 0;
|
||||
virtual bool for_all_outputs(
|
||||
uint64_t amount, const std::function<bool(uint64_t height)>& f) const = 0;
|
||||
|
||||
/**
|
||||
* @brief runs a function over all alternative blocks stored
|
||||
|
@ -1767,7 +1807,13 @@ public:
|
|||
*
|
||||
* @return false if the function returns false for any output, otherwise true
|
||||
*/
|
||||
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 = 0;
|
||||
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 = 0;
|
||||
|
||||
/**
|
||||
* @brief return a histogram of outputs on the blockchain
|
||||
|
@ -1779,9 +1825,18 @@ public:
|
|||
*
|
||||
* @return a set of amount/instances
|
||||
*/
|
||||
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 = 0;
|
||||
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 = 0;
|
||||
|
||||
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 = 0;
|
||||
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 = 0;
|
||||
|
||||
/**
|
||||
* @brief is BlockchainDB in read-only mode?
|
||||
|
@ -1802,25 +1857,28 @@ public:
|
|||
*/
|
||||
virtual void fixup(cryptonote::network_type nettype);
|
||||
|
||||
virtual void get_output_blacklist(std::vector<uint64_t> &blacklist) const = 0;
|
||||
virtual void add_output_blacklist(std::vector<uint64_t> const &blacklist) = 0;
|
||||
virtual void get_output_blacklist(std::vector<uint64_t>& blacklist) const = 0;
|
||||
virtual void add_output_blacklist(std::vector<uint64_t> const& blacklist) = 0;
|
||||
virtual void set_service_node_data(const std::string& data, bool long_term) = 0;
|
||||
virtual bool get_service_node_data(std::string &data, bool long_term) const = 0;
|
||||
virtual bool get_service_node_data(std::string& data, bool long_term) const = 0;
|
||||
virtual void clear_service_node_data() = 0;
|
||||
|
||||
/// Updates the given proof data with the latest stored info for the given service node. Returns
|
||||
/// true if found (and fields updated), false otherwise.
|
||||
virtual bool get_service_node_proof(const crypto::public_key &pubkey, service_nodes::proof_info &proof) const = 0;
|
||||
virtual bool get_service_node_proof(
|
||||
const crypto::public_key& pubkey, service_nodes::proof_info& proof) const = 0;
|
||||
|
||||
/// Returns pubkeys and proof data for all currently stored service nodes.
|
||||
virtual std::unordered_map<crypto::public_key, service_nodes::proof_info> get_all_service_node_proofs() const = 0;
|
||||
virtual std::unordered_map<crypto::public_key, service_nodes::proof_info>
|
||||
get_all_service_node_proofs() const = 0;
|
||||
|
||||
/// Creates or updates the proof data for a service node from a proof.
|
||||
virtual void set_service_node_proof(const crypto::public_key &pubkey, const service_nodes::proof_info &proof) = 0;
|
||||
virtual void set_service_node_proof(
|
||||
const crypto::public_key& pubkey, const service_nodes::proof_info& proof) = 0;
|
||||
|
||||
/// Removes stored serialized proof sn data associated with the given pubkey. Returns true if
|
||||
/// found, false if not found.
|
||||
virtual bool remove_service_node_proof(const crypto::public_key &pubkey) = 0;
|
||||
virtual bool remove_service_node_proof(const crypto::public_key& pubkey) = 0;
|
||||
|
||||
// This function accepts an empty timestamps/difficulties array to fill, or
|
||||
// a prior timestamps/difficulties array that was filled by a previous call to
|
||||
|
@ -1843,9 +1901,10 @@ public:
|
|||
// was invoked and loaded historical timestamp/difficulties into (allowing
|
||||
// this function to be called iteratively on the same input arrays over time).
|
||||
// This should be set to 0 on the initial call.
|
||||
void fill_timestamps_and_difficulties_for_pow(cryptonote::network_type nettype,
|
||||
std::vector<uint64_t> ×tamps,
|
||||
std::vector<uint64_t> &difficulties,
|
||||
void 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;
|
||||
|
||||
|
@ -1863,37 +1922,29 @@ public:
|
|||
|
||||
}; // class BlockchainDB
|
||||
|
||||
class db_txn_guard
|
||||
{
|
||||
public:
|
||||
db_txn_guard(BlockchainDB& db, bool readonly): db(db), readonly(readonly), active(false)
|
||||
{
|
||||
if (readonly)
|
||||
{
|
||||
class db_txn_guard {
|
||||
public:
|
||||
db_txn_guard(BlockchainDB& db, bool readonly) : db(db), readonly(readonly), active(false) {
|
||||
if (readonly) {
|
||||
active = db.block_rtxn_start();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
db.block_wtxn_start();
|
||||
active = true;
|
||||
}
|
||||
}
|
||||
db_txn_guard(BlockchainDB* db, bool readonly) : db_txn_guard(*db, readonly) {}
|
||||
virtual ~db_txn_guard()
|
||||
{
|
||||
virtual ~db_txn_guard() {
|
||||
if (active)
|
||||
stop();
|
||||
}
|
||||
void stop()
|
||||
{
|
||||
void stop() {
|
||||
if (readonly)
|
||||
db.block_rtxn_stop();
|
||||
else
|
||||
db.block_wtxn_stop();
|
||||
active = false;
|
||||
}
|
||||
void abort()
|
||||
{
|
||||
void abort() {
|
||||
if (readonly)
|
||||
db.block_rtxn_abort();
|
||||
else
|
||||
|
@ -1901,24 +1952,24 @@ public:
|
|||
active = false;
|
||||
}
|
||||
|
||||
private:
|
||||
BlockchainDB &db;
|
||||
private:
|
||||
BlockchainDB& db;
|
||||
bool readonly;
|
||||
bool active;
|
||||
};
|
||||
|
||||
class db_rtxn_guard: public db_txn_guard {
|
||||
public:
|
||||
class db_rtxn_guard : public db_txn_guard {
|
||||
public:
|
||||
explicit db_rtxn_guard(BlockchainDB& db) : db_txn_guard{db, true} {}
|
||||
explicit db_rtxn_guard(BlockchainDB* db) : db_rtxn_guard{*db} {}
|
||||
};
|
||||
class db_wtxn_guard: public db_txn_guard {
|
||||
public:
|
||||
class db_wtxn_guard : public db_txn_guard {
|
||||
public:
|
||||
explicit db_wtxn_guard(BlockchainDB& db) : db_txn_guard{db, false} {}
|
||||
explicit db_wtxn_guard(BlockchainDB* db) : db_wtxn_guard{*db} {}
|
||||
};
|
||||
|
||||
BlockchainDB *new_db();
|
||||
BlockchainDB* new_db();
|
||||
|
||||
} // namespace cryptonote
|
||||
|
||||
|
|
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,41 +47,39 @@ 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
|
||||
{
|
||||
struct mdb_rflags {
|
||||
bool m_rf_txn;
|
||||
bool m_rf_blocks;
|
||||
bool m_rf_block_heights;
|
||||
|
@ -108,18 +105,16 @@ struct mdb_rflags
|
|||
bool m_rf_properties;
|
||||
};
|
||||
|
||||
struct mdb_threadinfo
|
||||
{
|
||||
MDB_txn *m_ti_rtxn; // per-thread read txn
|
||||
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();
|
||||
};
|
||||
|
||||
struct mdb_txn_safe
|
||||
{
|
||||
mdb_txn_safe(const bool check=true);
|
||||
struct mdb_txn_safe {
|
||||
mdb_txn_safe(const bool check = true);
|
||||
~mdb_txn_safe();
|
||||
|
||||
void commit(std::string message = "");
|
||||
|
@ -131,15 +126,9 @@ struct mdb_txn_safe
|
|||
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;
|
||||
|
||||
|
@ -157,7 +146,6 @@ struct mdb_txn_safe
|
|||
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,13 +159,13 @@ 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);
|
||||
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;
|
||||
|
||||
|
@ -199,7 +187,7 @@ public:
|
|||
|
||||
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;
|
||||
|
||||
|
@ -211,7 +199,8 @@ public:
|
|||
|
||||
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;
|
||||
|
||||
|
@ -229,15 +218,17 @@ public:
|
|||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -248,77 +239,124 @@ public:
|
|||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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_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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
|
@ -330,7 +368,7 @@ public:
|
|||
void block_rtxn_stop() const override;
|
||||
void block_rtxn_abort() const override;
|
||||
|
||||
bool block_rtxn_start(MDB_txn **mtxn, mdb_txn_cursors **mcur) const;
|
||||
bool block_rtxn_start(MDB_txn** mtxn, mdb_txn_cursors** mcur) const;
|
||||
|
||||
void pop_block(block& blk, std::vector<transaction>& txs) override;
|
||||
|
||||
|
@ -346,49 +384,61 @@ public:
|
|||
*
|
||||
* @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;
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
private:
|
||||
void do_resize(uint64_t size_increase = 0);
|
||||
|
||||
bool need_resize(uint64_t threshold_size=0) const;
|
||||
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 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;
|
||||
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,
|
||||
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;
|
||||
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 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);
|
||||
|
||||
|
@ -410,7 +460,8 @@ private:
|
|||
|
||||
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;
|
||||
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;
|
||||
|
@ -431,21 +482,28 @@ private:
|
|||
|
||||
void cleanup_batch();
|
||||
|
||||
bool get_block_checkpoint_internal(uint64_t height, checkpoint_t &checkpoint, MDB_cursor_op op) const;
|
||||
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 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> ||
|
||||
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>
|
||||
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;
|
||||
|
|
|
@ -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 {
|
||||
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;
|
||||
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()); } }
|
||||
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;
|
||||
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");
|
||||
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()} {
|
||||
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")) {
|
||||
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,
|
||||
|
@ -117,9 +120,9 @@ namespace cryptonote {
|
|||
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()) {
|
||||
|
@ -131,12 +134,10 @@ namespace cryptonote {
|
|||
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
|
||||
};
|
||||
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,10 +149,13 @@ 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")) {
|
||||
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));
|
||||
|
@ -159,11 +163,14 @@ namespace cryptonote {
|
|||
st->reset();
|
||||
}
|
||||
|
||||
auto count = prepared_get<int>("SELECT COUNT(*) FROM batched_payments_accrued WHERE payout_offset NOT BETWEEN 0 AND ?",
|
||||
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";
|
||||
constexpr auto error =
|
||||
"Batching db update to add offsets failed: not all addresses were converted";
|
||||
log::error(logcat, error);
|
||||
throw std::runtime_error{error};
|
||||
}
|
||||
|
@ -171,16 +178,15 @@ namespace cryptonote {
|
|||
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)
|
||||
{
|
||||
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"(
|
||||
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));
|
||||
)",
|
||||
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,37 +235,34 @@ 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;
|
||||
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",
|
||||
"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)
|
||||
{
|
||||
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));
|
||||
|
@ -265,7 +270,8 @@ namespace cryptonote {
|
|||
}
|
||||
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,22 +281,21 @@ 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);
|
||||
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 (?, ?, ?)"
|
||||
|
@ -298,49 +303,62 @@ namespace cryptonote {
|
|||
|
||||
const auto& netconf = get_config(m_nettype);
|
||||
|
||||
for (auto& payment: payments) {
|
||||
auto offset = static_cast<int>(payment.address_info.address.modulus(netconf.BATCHING_INTERVAL));
|
||||
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);
|
||||
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 = ?");
|
||||
|
||||
for (auto& payment: payments) {
|
||||
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);
|
||||
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);
|
||||
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))
|
||||
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",
|
||||
"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));
|
||||
|
||||
|
@ -349,31 +367,31 @@ namespace cryptonote {
|
|||
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);
|
||||
[[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")) {
|
||||
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));
|
||||
|
@ -382,14 +400,19 @@ namespace cryptonote {
|
|||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -405,25 +428,26 @@ namespace cryptonote {
|
|||
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);
|
||||
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(
|
||||
// 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)
|
||||
{
|
||||
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.
|
||||
|
@ -431,7 +455,8 @@ namespace cryptonote {
|
|||
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;
|
||||
|
||||
|
@ -443,15 +468,16 @@ namespace cryptonote {
|
|||
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);
|
||||
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
|
||||
// Takes the block producer and adds its contributors to the batching database for the
|
||||
// transaction fees
|
||||
if (!(this->*add_or_subtract)(payments))
|
||||
return false;
|
||||
}
|
||||
|
@ -459,14 +485,20 @@ namespace cryptonote {
|
|||
|
||||
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) {
|
||||
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);
|
||||
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;
|
||||
|
@ -475,11 +507,15 @@ namespace cryptonote {
|
|||
// 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));
|
||||
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;
|
||||
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))
|
||||
|
@ -487,9 +523,10 @@ namespace cryptonote {
|
|||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockchainSQLite::add_block(const cryptonote::block& block,
|
||||
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);
|
||||
|
@ -508,33 +545,36 @@ namespace cryptonote {
|
|||
}
|
||||
|
||||
if (block_height != height + 1) {
|
||||
log::error(logcat, "Block height ({}) out of sync with batching database ({})", block_height, height);
|
||||
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)
|
||||
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
|
||||
// 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))
|
||||
if (!reward_handler(block, service_nodes_state, /*add=*/true))
|
||||
return false;
|
||||
|
||||
increment_height();
|
||||
|
@ -545,9 +585,10 @@ namespace cryptonote {
|
|||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockchainSQLite::pop_block(const cryptonote::block& block,
|
||||
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);
|
||||
|
||||
|
@ -569,13 +610,9 @@ namespace cryptonote {
|
|||
}
|
||||
|
||||
try {
|
||||
SQLite::Transaction transaction {
|
||||
db,
|
||||
SQLite::TransactionBehavior::IMMEDIATE
|
||||
};
|
||||
SQLite::Transaction transaction{db, SQLite::TransactionBehavior::IMMEDIATE};
|
||||
|
||||
|
||||
if (!reward_handler(block, service_nodes_state, /*add=*/ 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
|
||||
|
@ -588,16 +625,20 @@ namespace cryptonote {
|
|||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockchainSQLite::validate_batch_payment(
|
||||
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());
|
||||
log::error(
|
||||
logcat,
|
||||
"Length of batch payments ({}) does not match block vouts ({})",
|
||||
calculated_payments_from_batching_db.size(),
|
||||
miner_tx_vouts.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -609,18 +650,26 @@ namespace cryptonote {
|
|||
[](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);
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
|
@ -632,56 +681,77 @@ namespace cryptonote {
|
|||
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);
|
||||
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 (?,?,?)");
|
||||
|
||||
std::lock_guard a_s_lock{address_str_cache_mutex};
|
||||
|
||||
for (const auto& payment: paid_amounts) {
|
||||
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))
|
||||
{
|
||||
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;
|
||||
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);
|
||||
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));
|
||||
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);
|
||||
} 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();
|
||||
}
|
||||
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",
|
||||
"SELECT address, amount FROM batched_payments_paid WHERE height_paid = ? ORDER BY "
|
||||
"address",
|
||||
static_cast<int64_t>(block_height));
|
||||
|
||||
for (auto [addr, amt] : paid) {
|
||||
|
@ -691,14 +761,14 @@ namespace cryptonote {
|
|||
}
|
||||
|
||||
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));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cryptonote
|
||||
|
|
|
@ -27,24 +27,22 @@
|
|||
|
||||
#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:
|
||||
class BlockchainSQLite : public db::Database {
|
||||
public:
|
||||
explicit BlockchainSQLite(cryptonote::network_type nettype, fs::path db_path);
|
||||
BlockchainSQLite(const BlockchainSQLite&) = delete;
|
||||
|
||||
|
@ -53,18 +51,22 @@ public:
|
|||
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
|
||||
// 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);
|
||||
|
||||
// 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.
|
||||
// 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:
|
||||
private:
|
||||
bool reward_handler(
|
||||
const cryptonote::block& block,
|
||||
const service_nodes::service_node_list::state_t& service_nodes_state,
|
||||
|
@ -75,16 +77,17 @@ private:
|
|||
const std::string& get_address_str(const account_public_address& addr);
|
||||
std::mutex address_str_cache_mutex;
|
||||
|
||||
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.
|
||||
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.
|
||||
// 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.
|
||||
// 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
|
||||
|
@ -94,36 +97,46 @@ public:
|
|||
//
|
||||
// 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);
|
||||
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.
|
||||
// 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.
|
||||
// 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:
|
||||
|
||||
protected:
|
||||
cryptonote::network_type m_nettype;
|
||||
std::string filename;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace cryptonote
|
||||
|
|
|
@ -30,20 +30,23 @@
|
|||
|
||||
#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"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
struct checkpoint_t;
|
||||
class BaseTestDB: public cryptonote::BlockchainDB {
|
||||
public:
|
||||
#include "blockchain_db.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 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 {}
|
||||
|
@ -51,10 +54,12 @@ public:
|
|||
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 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 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 {}
|
||||
|
@ -65,87 +70,236 @@ public:
|
|||
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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; }
|
||||
|
@ -154,31 +308,68 @@ public:
|
|||
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 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 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 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
|
||||
|
|
|
@ -27,27 +27,28 @@
|
|||
// 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 SCNu64 macro on Mingw
|
||||
#define __STDC_FORMAT_MACROS // NOTE(oxen): Explicitly define the SCNu64 macro on Mingw
|
||||
#endif
|
||||
|
||||
#include <cinttypes>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <boost/archive/portable_binary_iarchive.hpp>
|
||||
#include <boost/archive/portable_binary_oarchive.hpp>
|
||||
#include <cinttypes>
|
||||
#include <fstream>
|
||||
#include "common/unordered_containers_boost_serialization.h"
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#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 "serialization/boost_std_variant.h"
|
||||
#include "common/signal_handler.h"
|
||||
#include "common/unordered_containers_boost_serialization.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 "serialization/boost_std_variant.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace po = boost::program_options;
|
||||
using namespace cryptonote;
|
||||
|
@ -55,217 +56,203 @@ using namespace cryptonote;
|
|||
static auto logcat = log::Cat("bcutil");
|
||||
|
||||
static bool stop_requested = false;
|
||||
static uint64_t cached_txes = 0, cached_blocks = 0, cached_outputs = 0, total_txes = 0, total_blocks = 0, total_outputs = 0;
|
||||
static uint64_t cached_txes = 0, cached_blocks = 0, cached_outputs = 0, total_txes = 0,
|
||||
total_blocks = 0, total_outputs = 0;
|
||||
static bool opt_cache_outputs = false, opt_cache_txes = false, opt_cache_blocks = false;
|
||||
|
||||
struct ancestor
|
||||
{
|
||||
struct ancestor {
|
||||
uint64_t amount;
|
||||
uint64_t offset;
|
||||
|
||||
bool operator==(const ancestor &other) const { return amount == other.amount && offset == other.offset; }
|
||||
bool operator==(const ancestor& other) const {
|
||||
return amount == other.amount && offset == other.offset;
|
||||
}
|
||||
|
||||
template <typename t_archive> void serialize(t_archive &a, const unsigned int ver)
|
||||
{
|
||||
a & amount;
|
||||
a & offset;
|
||||
template <typename t_archive>
|
||||
void serialize(t_archive& a, const unsigned int ver) {
|
||||
a& amount;
|
||||
a& offset;
|
||||
}
|
||||
};
|
||||
BOOST_CLASS_VERSION(ancestor, 0)
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<> struct hash<ancestor>
|
||||
{
|
||||
size_t operator()(const ancestor &a) const
|
||||
{
|
||||
return a.amount ^ a.offset; // not that bad, since amount almost always have a high bit set, and offset doesn't
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<ancestor> {
|
||||
size_t operator()(const ancestor& a) const {
|
||||
return a.amount ^ a.offset; // not that bad, since amount almost always have a high bit
|
||||
// set, and offset doesn't
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
struct tx_data_t
|
||||
{
|
||||
struct tx_data_t {
|
||||
std::vector<std::pair<uint64_t, std::vector<uint64_t>>> vin;
|
||||
std::vector<crypto::public_key> vout;
|
||||
bool coinbase;
|
||||
|
||||
tx_data_t(): coinbase(false) {}
|
||||
tx_data_t(const cryptonote::transaction &tx)
|
||||
{
|
||||
tx_data_t() : coinbase(false) {}
|
||||
tx_data_t(const cryptonote::transaction& tx) {
|
||||
coinbase = tx.vin.size() == 1 && std::holds_alternative<cryptonote::txin_gen>(tx.vin[0]);
|
||||
if (!coinbase)
|
||||
{
|
||||
if (!coinbase) {
|
||||
vin.reserve(tx.vin.size());
|
||||
for (size_t ring = 0; ring < tx.vin.size(); ++ring)
|
||||
{
|
||||
for (size_t ring = 0; ring < tx.vin.size(); ++ring) {
|
||||
if (const auto* txin = std::get_if<cryptonote::txin_to_key>(&tx.vin[ring]))
|
||||
vin.push_back(std::make_pair(txin->amount, cryptonote::relative_output_offsets_to_absolute(txin->key_offsets)));
|
||||
else
|
||||
{
|
||||
vin.push_back(std::make_pair(
|
||||
txin->amount,
|
||||
cryptonote::relative_output_offsets_to_absolute(txin->key_offsets)));
|
||||
else {
|
||||
log::warning(logcat, "Bad vin type in txid {}", get_transaction_hash(tx));
|
||||
throw std::runtime_error("Bad vin type");
|
||||
}
|
||||
}
|
||||
}
|
||||
vout.reserve(tx.vout.size());
|
||||
for (size_t out = 0; out < tx.vout.size(); ++out)
|
||||
{
|
||||
if (const auto* txout = std::get_if<cryptonote::txout_to_key>(&tx.vout[out].target))
|
||||
{
|
||||
for (size_t out = 0; out < tx.vout.size(); ++out) {
|
||||
if (const auto* txout = std::get_if<cryptonote::txout_to_key>(&tx.vout[out].target)) {
|
||||
vout.push_back(txout->key);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
log::warning(logcat, "Bad vout type in txid {}", get_transaction_hash(tx));
|
||||
throw std::runtime_error("Bad vout type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename t_archive> void serialize(t_archive &a, const unsigned int ver)
|
||||
{
|
||||
a & coinbase;
|
||||
a & vin;
|
||||
a & vout;
|
||||
template <typename t_archive>
|
||||
void serialize(t_archive& a, const unsigned int ver) {
|
||||
a& coinbase;
|
||||
a& vin;
|
||||
a& vout;
|
||||
}
|
||||
};
|
||||
|
||||
struct ancestry_state_t
|
||||
{
|
||||
struct ancestry_state_t {
|
||||
uint64_t height;
|
||||
std::unordered_map<crypto::hash, std::unordered_set<ancestor>> ancestry;
|
||||
std::unordered_map<ancestor, crypto::hash> output_cache;
|
||||
std::unordered_map<crypto::hash, ::tx_data_t> tx_cache;
|
||||
std::vector<cryptonote::block> block_cache;
|
||||
|
||||
ancestry_state_t(): height(0) {}
|
||||
ancestry_state_t() : height(0) {}
|
||||
|
||||
template <typename t_archive> void serialize(t_archive &a, const unsigned int ver)
|
||||
{
|
||||
a & height;
|
||||
a & ancestry;
|
||||
a & output_cache;
|
||||
if (ver < 1)
|
||||
{
|
||||
template <typename t_archive>
|
||||
void serialize(t_archive& a, const unsigned int ver) {
|
||||
a& height;
|
||||
a& ancestry;
|
||||
a& output_cache;
|
||||
if (ver < 1) {
|
||||
std::unordered_map<crypto::hash, cryptonote::transaction> old_tx_cache;
|
||||
a & old_tx_cache;
|
||||
a& old_tx_cache;
|
||||
for (const auto& [hash, tx] : old_tx_cache)
|
||||
tx_cache.insert(std::make_pair(hash, ::tx_data_t(tx)));
|
||||
} else {
|
||||
a& tx_cache;
|
||||
}
|
||||
else
|
||||
{
|
||||
a & tx_cache;
|
||||
}
|
||||
if (ver < 2)
|
||||
{
|
||||
if (ver < 2) {
|
||||
std::unordered_map<uint64_t, cryptonote::block> old_block_cache;
|
||||
a & old_block_cache;
|
||||
a& old_block_cache;
|
||||
block_cache.reserve(old_block_cache.size());
|
||||
for (auto& [i, block] : old_block_cache)
|
||||
block_cache.push_back(std::move(block));
|
||||
}
|
||||
else
|
||||
{
|
||||
a & block_cache;
|
||||
} else {
|
||||
a& block_cache;
|
||||
}
|
||||
}
|
||||
};
|
||||
BOOST_CLASS_VERSION(ancestry_state_t, 2)
|
||||
|
||||
static void add_ancestor(std::unordered_map<ancestor, unsigned int> &ancestry, uint64_t amount, uint64_t offset)
|
||||
{
|
||||
std::pair<std::unordered_map<ancestor, unsigned int>::iterator, bool> p = ancestry.insert(std::make_pair(ancestor{amount, offset}, 1));
|
||||
if (!p.second)
|
||||
{
|
||||
static void add_ancestor(
|
||||
std::unordered_map<ancestor, unsigned int>& ancestry, uint64_t amount, uint64_t offset) {
|
||||
std::pair<std::unordered_map<ancestor, unsigned int>::iterator, bool> p =
|
||||
ancestry.insert(std::make_pair(ancestor{amount, offset}, 1));
|
||||
if (!p.second) {
|
||||
++p.first->second;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t get_full_ancestry(const std::unordered_map<ancestor, unsigned int> &ancestry)
|
||||
{
|
||||
static size_t get_full_ancestry(const std::unordered_map<ancestor, unsigned int>& ancestry) {
|
||||
size_t count = 0;
|
||||
for (const auto &i: ancestry)
|
||||
for (const auto& i : ancestry)
|
||||
count += i.second;
|
||||
return count;
|
||||
}
|
||||
|
||||
static size_t get_deduplicated_ancestry(const std::unordered_map<ancestor, unsigned int> &ancestry)
|
||||
{
|
||||
static size_t get_deduplicated_ancestry(
|
||||
const std::unordered_map<ancestor, unsigned int>& ancestry) {
|
||||
return ancestry.size();
|
||||
}
|
||||
|
||||
static void add_ancestry(std::unordered_map<crypto::hash, std::unordered_set<ancestor>> &ancestry, const crypto::hash &txid, const std::unordered_set<ancestor> &ancestors)
|
||||
{
|
||||
std::pair<std::unordered_map<crypto::hash, std::unordered_set<ancestor>>::iterator, bool> p = ancestry.insert(std::make_pair(txid, ancestors));
|
||||
if (!p.second)
|
||||
{
|
||||
for (const auto &e: ancestors)
|
||||
static void add_ancestry(
|
||||
std::unordered_map<crypto::hash, std::unordered_set<ancestor>>& ancestry,
|
||||
const crypto::hash& txid,
|
||||
const std::unordered_set<ancestor>& ancestors) {
|
||||
std::pair<std::unordered_map<crypto::hash, std::unordered_set<ancestor>>::iterator, bool> p =
|
||||
ancestry.insert(std::make_pair(txid, ancestors));
|
||||
if (!p.second) {
|
||||
for (const auto& e : ancestors)
|
||||
p.first->second.insert(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_ancestry(std::unordered_map<crypto::hash, std::unordered_set<ancestor>> &ancestry, const crypto::hash &txid, const ancestor &new_ancestor)
|
||||
{
|
||||
std::pair<std::unordered_map<crypto::hash, std::unordered_set<ancestor>>::iterator, bool> p = ancestry.insert(std::make_pair(txid, std::unordered_set<ancestor>()));
|
||||
static void add_ancestry(
|
||||
std::unordered_map<crypto::hash, std::unordered_set<ancestor>>& ancestry,
|
||||
const crypto::hash& txid,
|
||||
const ancestor& new_ancestor) {
|
||||
std::pair<std::unordered_map<crypto::hash, std::unordered_set<ancestor>>::iterator, bool> p =
|
||||
ancestry.insert(std::make_pair(txid, std::unordered_set<ancestor>()));
|
||||
p.first->second.insert(new_ancestor);
|
||||
}
|
||||
|
||||
static std::unordered_set<ancestor> get_ancestry(const std::unordered_map<crypto::hash, std::unordered_set<ancestor>> &ancestry, const crypto::hash &txid)
|
||||
{
|
||||
std::unordered_map<crypto::hash, std::unordered_set<ancestor>>::const_iterator i = ancestry.find(txid);
|
||||
if (i == ancestry.end())
|
||||
{
|
||||
//log::error(logcat, "txid ancestry not found: {}", txid);
|
||||
//throw std::runtime_error("txid ancestry not found");
|
||||
static std::unordered_set<ancestor> get_ancestry(
|
||||
const std::unordered_map<crypto::hash, std::unordered_set<ancestor>>& ancestry,
|
||||
const crypto::hash& txid) {
|
||||
std::unordered_map<crypto::hash, std::unordered_set<ancestor>>::const_iterator i =
|
||||
ancestry.find(txid);
|
||||
if (i == ancestry.end()) {
|
||||
// log::error(logcat, "txid ancestry not found: {}", txid);
|
||||
// throw std::runtime_error("txid ancestry not found");
|
||||
return std::unordered_set<ancestor>();
|
||||
}
|
||||
return i->second;
|
||||
}
|
||||
|
||||
static bool get_block_from_height(ancestry_state_t &state, BlockchainDB *db, uint64_t height, cryptonote::block &b)
|
||||
{
|
||||
static bool get_block_from_height(
|
||||
ancestry_state_t& state, BlockchainDB* db, uint64_t height, cryptonote::block& b) {
|
||||
++total_blocks;
|
||||
if (state.block_cache.size() > height && !state.block_cache[height].miner_tx.vin.empty())
|
||||
{
|
||||
if (state.block_cache.size() > height && !state.block_cache[height].miner_tx.vin.empty()) {
|
||||
++cached_blocks;
|
||||
b = state.block_cache[height];
|
||||
return true;
|
||||
}
|
||||
std::string bd = db->get_block_blob_from_height(height);
|
||||
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
|
||||
{
|
||||
if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) {
|
||||
log::warning(logcat, "Bad block from db");
|
||||
return false;
|
||||
}
|
||||
if (opt_cache_blocks)
|
||||
{
|
||||
if (opt_cache_blocks) {
|
||||
state.block_cache.resize(height + 1);
|
||||
state.block_cache[height] = b;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool get_transaction(ancestry_state_t &state, BlockchainDB *db, const crypto::hash &txid, ::tx_data_t &tx_data)
|
||||
{
|
||||
static bool get_transaction(
|
||||
ancestry_state_t& state, BlockchainDB* db, const crypto::hash& txid, ::tx_data_t& tx_data) {
|
||||
std::unordered_map<crypto::hash, ::tx_data_t>::const_iterator i = state.tx_cache.find(txid);
|
||||
++total_txes;
|
||||
if (i != state.tx_cache.end())
|
||||
{
|
||||
if (i != state.tx_cache.end()) {
|
||||
++cached_txes;
|
||||
tx_data = i->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string bd;
|
||||
if (!db->get_pruned_tx_blob(txid, bd))
|
||||
{
|
||||
if (!db->get_pruned_tx_blob(txid, bd)) {
|
||||
log::warning(logcat, "Failed to get txid {} from db", txid);
|
||||
return false;
|
||||
}
|
||||
cryptonote::transaction tx;
|
||||
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx))
|
||||
{
|
||||
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx)) {
|
||||
log::warning(logcat, "Bad tx: {}", txid);
|
||||
return false;
|
||||
}
|
||||
|
@ -275,12 +262,16 @@ static bool get_transaction(ancestry_state_t &state, BlockchainDB *db, const cry
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool get_output_txid(ancestry_state_t &state, BlockchainDB *db, uint64_t amount, uint64_t offset, crypto::hash &txid)
|
||||
{
|
||||
static bool get_output_txid(
|
||||
ancestry_state_t& state,
|
||||
BlockchainDB* db,
|
||||
uint64_t amount,
|
||||
uint64_t offset,
|
||||
crypto::hash& txid) {
|
||||
++total_outputs;
|
||||
std::unordered_map<ancestor, crypto::hash>::const_iterator i = state.output_cache.find({amount, offset});
|
||||
if (i != state.output_cache.end())
|
||||
{
|
||||
std::unordered_map<ancestor, crypto::hash>::const_iterator i =
|
||||
state.output_cache.find({amount, offset});
|
||||
if (i != state.output_cache.end()) {
|
||||
++cached_outputs;
|
||||
txid = i->second;
|
||||
return true;
|
||||
|
@ -291,34 +282,30 @@ static bool get_output_txid(ancestry_state_t &state, BlockchainDB *db, uint64_t
|
|||
if (!get_block_from_height(state, db, od.height, b))
|
||||
return false;
|
||||
|
||||
for (size_t out = 0; out < b.miner_tx.vout.size(); ++out)
|
||||
{
|
||||
if (const auto* txout = std::get_if<cryptonote::txout_to_key>(&b.miner_tx.vout[out].target))
|
||||
{
|
||||
if (txout->key == od.pubkey)
|
||||
{
|
||||
for (size_t out = 0; out < b.miner_tx.vout.size(); ++out) {
|
||||
if (const auto* txout =
|
||||
std::get_if<cryptonote::txout_to_key>(&b.miner_tx.vout[out].target)) {
|
||||
if (txout->key == od.pubkey) {
|
||||
txid = cryptonote::get_transaction_hash(b.miner_tx);
|
||||
if (opt_cache_outputs)
|
||||
state.output_cache.insert(std::make_pair(ancestor{amount, offset}, txid));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log::warning(logcat, "Bad vout type in txid {}", cryptonote::get_transaction_hash(b.miner_tx));
|
||||
} else {
|
||||
log::warning(
|
||||
logcat,
|
||||
"Bad vout type in txid {}",
|
||||
cryptonote::get_transaction_hash(b.miner_tx));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const crypto::hash &block_txid: b.tx_hashes)
|
||||
{
|
||||
for (const crypto::hash& block_txid : b.tx_hashes) {
|
||||
::tx_data_t tx_data3;
|
||||
if (!get_transaction(state, db, block_txid, tx_data3))
|
||||
return false;
|
||||
|
||||
for (size_t out = 0; out < tx_data3.vout.size(); ++out)
|
||||
{
|
||||
if (tx_data3.vout[out] == od.pubkey)
|
||||
{
|
||||
for (size_t out = 0; out < tx_data3.vout.size(); ++out) {
|
||||
if (tx_data3.vout[out] == od.pubkey) {
|
||||
txid = block_txid;
|
||||
if (opt_cache_outputs)
|
||||
state.output_cache.insert(std::make_pair(ancestor{amount, offset}, txid));
|
||||
|
@ -329,8 +316,7 @@ static bool get_output_txid(ancestry_state_t &state, BlockchainDB *db, uint64_t
|
|||
return false;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int main(int argc, char* argv[]) {
|
||||
TRY_ENTRY();
|
||||
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
|
@ -340,17 +326,28 @@ int main(int argc, char* argv[])
|
|||
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 ancestry for this txid", ""};
|
||||
const command_line::arg_descriptor<std::string> arg_output = {"output", "Get ancestry for this output (amount/offset format)", ""};
|
||||
const command_line::arg_descriptor<uint64_t> arg_height = {"height", "Get ancestry for all txes at this height", 0};
|
||||
const command_line::arg_descriptor<bool> arg_refresh = {"refresh", "Refresh the whole chain first", false};
|
||||
const command_line::arg_descriptor<bool> arg_cache_outputs = {"cache-outputs", "Cache outputs (memory hungry)", false};
|
||||
const command_line::arg_descriptor<bool> arg_cache_txes = {"cache-txes", "Cache txes (memory hungry)", false};
|
||||
const command_line::arg_descriptor<bool> arg_cache_blocks = {"cache-blocks", "Cache blocks (memory hungry)", false};
|
||||
const command_line::arg_descriptor<bool> arg_include_coinbase = {"include-coinbase", "Including coinbase tx in per height average", false};
|
||||
const command_line::arg_descriptor<bool> arg_show_cache_stats = {"show-cache-stats", "Show cache statistics", false};
|
||||
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 ancestry for this txid", ""};
|
||||
const command_line::arg_descriptor<std::string> arg_output = {
|
||||
"output", "Get ancestry for this output (amount/offset format)", ""};
|
||||
const command_line::arg_descriptor<uint64_t> arg_height = {
|
||||
"height", "Get ancestry for all txes at this height", 0};
|
||||
const command_line::arg_descriptor<bool> arg_refresh = {
|
||||
"refresh", "Refresh the whole chain first", false};
|
||||
const command_line::arg_descriptor<bool> arg_cache_outputs = {
|
||||
"cache-outputs", "Cache outputs (memory hungry)", false};
|
||||
const command_line::arg_descriptor<bool> arg_cache_txes = {
|
||||
"cache-txes", "Cache txes (memory hungry)", false};
|
||||
const command_line::arg_descriptor<bool> arg_cache_blocks = {
|
||||
"cache-blocks", "Cache blocks (memory hungry)", false};
|
||||
const command_line::arg_descriptor<bool> arg_include_coinbase = {
|
||||
"include-coinbase", "Including coinbase tx in per height average", false};
|
||||
const command_line::arg_descriptor<bool> arg_show_cache_stats = {
|
||||
"show-cache-stats", "Show cache statistics", false};
|
||||
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
|
||||
|
@ -371,18 +368,16 @@ int main(int argc, char* argv[])
|
|||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
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)
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
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;
|
||||
|
@ -390,10 +385,11 @@ int main(int argc, char* argv[])
|
|||
auto m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
auto log_file_path = m_config_folder + "oxen-blockchain-ancestry.log";
|
||||
log::Level log_level;
|
||||
if(auto level = oxen::logging::parse_level(command_line::get_arg(vm, arg_log_level).c_str())) {
|
||||
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;
|
||||
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);
|
||||
|
@ -402,7 +398,9 @@ int main(int argc, char* argv[])
|
|||
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;
|
||||
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);
|
||||
std::string opt_output_string = command_line::get_arg(vm, arg_output);
|
||||
uint64_t opt_height = command_line::get_arg(vm, arg_height);
|
||||
|
@ -413,25 +411,22 @@ int main(int argc, char* argv[])
|
|||
bool opt_include_coinbase = command_line::get_arg(vm, arg_include_coinbase);
|
||||
bool opt_show_cache_stats = command_line::get_arg(vm, arg_show_cache_stats);
|
||||
|
||||
if ((!opt_txid_string.empty()) + !!opt_height + !opt_output_string.empty() > 1)
|
||||
{
|
||||
if ((!opt_txid_string.empty()) + !!opt_height + !opt_output_string.empty() > 1) {
|
||||
std::cerr << "Only one of --txid, --height, --output can be given" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
crypto::hash opt_txid{};
|
||||
uint64_t output_amount = 0, output_offset = 0;
|
||||
if (!opt_txid_string.empty())
|
||||
{
|
||||
if (!tools::hex_to_type(opt_txid_string, 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;
|
||||
}
|
||||
}
|
||||
else if (!opt_output_string.empty())
|
||||
{
|
||||
if (sscanf(opt_output_string.c_str(), "%" SCNu64 "/%" SCNu64, &output_amount, &output_offset) != 2)
|
||||
{
|
||||
} else if (!opt_output_string.empty()) {
|
||||
if (sscanf(opt_output_string.c_str(),
|
||||
"%" SCNu64 "/%" SCNu64,
|
||||
&output_amount,
|
||||
&output_offset) != 2) {
|
||||
std::cerr << "Invalid output" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
@ -439,10 +434,9 @@ int main(int argc, char* argv[])
|
|||
|
||||
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)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
@ -451,12 +445,9 @@ int main(int argc, char* argv[])
|
|||
fs::path filename = fs::u8path(opt_data_dir) / db->get_db_name();
|
||||
log::warning(logcat, "Loading blockchain from folder {} ...", filename);
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
db->open(filename, core_storage->nettype(), DBF_RDONLY);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
} catch (const std::exception& e) {
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
|
@ -473,44 +464,37 @@ int main(int argc, char* argv[])
|
|||
log::warning(logcat, "Loading state data from {}", state_file_path);
|
||||
fs::ifstream state_data_in;
|
||||
state_data_in.open(state_file_path, std::ios_base::binary | std::ios_base::in);
|
||||
if (!state_data_in.fail())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!state_data_in.fail()) {
|
||||
try {
|
||||
boost::archive::portable_binary_iarchive a(state_data_in);
|
||||
a >> state;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
log::error(logcat, "Failed to load state data from {}, restarting from scratch", state_file_path);
|
||||
} catch (const std::exception& e) {
|
||||
log::error(
|
||||
logcat,
|
||||
"Failed to load state data from {}, restarting from scratch",
|
||||
state_file_path);
|
||||
state = ancestry_state_t();
|
||||
}
|
||||
state_data_in.close();
|
||||
}
|
||||
|
||||
tools::signal_handler::install([](int type) {
|
||||
stop_requested = true;
|
||||
});
|
||||
tools::signal_handler::install([](int type) { stop_requested = true; });
|
||||
|
||||
// forward method
|
||||
const uint64_t db_height = db->height();
|
||||
if (opt_refresh)
|
||||
{
|
||||
if (opt_refresh) {
|
||||
log::info(logcat, "Starting from height {}", state.height);
|
||||
state.block_cache.reserve(db_height);
|
||||
for (uint64_t h = state.height; h < db_height; ++h)
|
||||
{
|
||||
for (uint64_t h = state.height; h < db_height; ++h) {
|
||||
size_t block_ancestry_size = 0;
|
||||
const std::string bd = db->get_block_blob_from_height(h);
|
||||
++total_blocks;
|
||||
cryptonote::block b;
|
||||
if (!cryptonote::parse_and_validate_block_from_blob(bd, b))
|
||||
{
|
||||
if (!cryptonote::parse_and_validate_block_from_blob(bd, b)) {
|
||||
log::warning(logcat, "Bad block from db");
|
||||
return 1;
|
||||
}
|
||||
if (opt_cache_blocks)
|
||||
{
|
||||
if (opt_cache_blocks) {
|
||||
state.block_cache.resize(h + 1);
|
||||
state.block_cache[h] = b;
|
||||
}
|
||||
|
@ -518,31 +502,26 @@ int main(int argc, char* argv[])
|
|||
txids.reserve(1 + b.tx_hashes.size());
|
||||
if (opt_include_coinbase)
|
||||
txids.push_back(cryptonote::get_transaction_hash(b.miner_tx));
|
||||
for (const auto &h: b.tx_hashes)
|
||||
for (const auto& h : b.tx_hashes)
|
||||
txids.push_back(h);
|
||||
for (const crypto::hash &txid: txids)
|
||||
{
|
||||
for (const crypto::hash& txid : txids) {
|
||||
printf("%lu/%lu \r", (unsigned long)h, (unsigned long)db_height);
|
||||
fflush(stdout);
|
||||
::tx_data_t tx_data;
|
||||
std::unordered_map<crypto::hash, ::tx_data_t>::const_iterator i = state.tx_cache.find(txid);
|
||||
std::unordered_map<crypto::hash, ::tx_data_t>::const_iterator i =
|
||||
state.tx_cache.find(txid);
|
||||
++total_txes;
|
||||
if (i != state.tx_cache.end())
|
||||
{
|
||||
if (i != state.tx_cache.end()) {
|
||||
++cached_txes;
|
||||
tx_data = i->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
std::string bd;
|
||||
if (!db->get_pruned_tx_blob(txid, 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))
|
||||
{
|
||||
if (!cryptonote::parse_and_validate_tx_base_from_blob(bd, tx)) {
|
||||
log::warning(logcat, "Bad tx: {}", txid);
|
||||
return 1;
|
||||
}
|
||||
|
@ -550,28 +529,25 @@ int main(int argc, char* argv[])
|
|||
if (opt_cache_txes)
|
||||
state.tx_cache.insert(std::make_pair(txid, tx_data));
|
||||
}
|
||||
if (tx_data.coinbase)
|
||||
{
|
||||
if (tx_data.coinbase) {
|
||||
add_ancestry(state.ancestry, txid, std::unordered_set<ancestor>());
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t ring = 0; ring < tx_data.vin.size(); ++ring)
|
||||
{
|
||||
} else {
|
||||
for (size_t ring = 0; ring < tx_data.vin.size(); ++ring) {
|
||||
const uint64_t amount = tx_data.vin[ring].first;
|
||||
const std::vector<uint64_t> &absolute_offsets = tx_data.vin[ring].second;
|
||||
for (uint64_t offset: absolute_offsets)
|
||||
{
|
||||
const std::vector<uint64_t>& absolute_offsets = tx_data.vin[ring].second;
|
||||
for (uint64_t offset : absolute_offsets) {
|
||||
add_ancestry(state.ancestry, txid, ancestor{amount, offset});
|
||||
// find the tx which created this output
|
||||
bool found = false;
|
||||
crypto::hash output_txid;
|
||||
if (!get_output_txid(state, db, amount, offset, output_txid))
|
||||
{
|
||||
if (!get_output_txid(state, db, amount, offset, output_txid)) {
|
||||
log::warning(logcat, "Output originating transaction not found");
|
||||
return 1;
|
||||
}
|
||||
add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, output_txid));
|
||||
add_ancestry(
|
||||
state.ancestry,
|
||||
txid,
|
||||
get_ancestry(state.ancestry, output_txid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -579,10 +555,15 @@ int main(int argc, char* argv[])
|
|||
block_ancestry_size += ancestry_size;
|
||||
log::info(logcat, "{}: {}", txid, ancestry_size);
|
||||
}
|
||||
if (!txids.empty())
|
||||
{
|
||||
if (!txids.empty()) {
|
||||
std::string stats_msg;
|
||||
log::info(logcat, "Height {}: {} average over {}{}", h, (block_ancestry_size / txids.size()), txids.size(), stats_msg);
|
||||
log::info(
|
||||
logcat,
|
||||
"Height {}: {} average over {}{}",
|
||||
h,
|
||||
(block_ancestry_size / txids.size()),
|
||||
txids.size(),
|
||||
stats_msg);
|
||||
}
|
||||
state.height = h;
|
||||
if (stop_requested)
|
||||
|
@ -591,73 +572,65 @@ int main(int argc, char* argv[])
|
|||
|
||||
log::warning(logcat, "Saving state data to {}", state_file_path);
|
||||
std::ofstream state_data_out;
|
||||
state_data_out.open(state_file_path, std::ios_base::binary | std::ios_base::out | std::ios::trunc);
|
||||
if (!state_data_out.fail())
|
||||
{
|
||||
try
|
||||
{
|
||||
state_data_out.open(
|
||||
state_file_path, std::ios_base::binary | std::ios_base::out | std::ios::trunc);
|
||||
if (!state_data_out.fail()) {
|
||||
try {
|
||||
boost::archive::portable_binary_oarchive a(state_data_out);
|
||||
a << state;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
} catch (const std::exception& e) {
|
||||
log::error(logcat, "Failed to save state data to {}", state_file_path);
|
||||
}
|
||||
state_data_out.close();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state.height < db_height)
|
||||
{
|
||||
log::warning(logcat, "The state file is only built up to height {}, but the blockchain reached height {}", state.height, db_height);
|
||||
log::warning(logcat, "You may want to run with --refresh if you want to get ancestry for newer data");
|
||||
} else {
|
||||
if (state.height < db_height) {
|
||||
log::warning(
|
||||
logcat,
|
||||
"The state file is only built up to height {}, but the blockchain reached "
|
||||
"height {}",
|
||||
state.height,
|
||||
db_height);
|
||||
log::warning(
|
||||
logcat,
|
||||
"You may want to run with --refresh if you want to get ancestry for newer "
|
||||
"data");
|
||||
}
|
||||
}
|
||||
|
||||
if (!opt_txid_string.empty())
|
||||
{
|
||||
if (!opt_txid_string.empty()) {
|
||||
start_txids.push_back(opt_txid);
|
||||
}
|
||||
else if (!opt_output_string.empty())
|
||||
{
|
||||
} else if (!opt_output_string.empty()) {
|
||||
crypto::hash txid;
|
||||
if (!get_output_txid(state, db, output_amount, output_offset, txid))
|
||||
{
|
||||
if (!get_output_txid(state, db, output_amount, output_offset, txid)) {
|
||||
log::warning(logcat, "Output not found in db");
|
||||
return 1;
|
||||
}
|
||||
start_txids.push_back(txid);
|
||||
}
|
||||
else
|
||||
{
|
||||
} 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))
|
||||
{
|
||||
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)
|
||||
for (const crypto::hash& txid : b.tx_hashes)
|
||||
start_txids.push_back(txid);
|
||||
}
|
||||
|
||||
if (start_txids.empty())
|
||||
{
|
||||
if (start_txids.empty()) {
|
||||
log::warning(logcat, "No transaction(s) to check");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (const crypto::hash &start_txid: start_txids)
|
||||
{
|
||||
for (const crypto::hash& start_txid : start_txids) {
|
||||
log::warning(logcat, "Checking ancestry for txid {}", start_txid);
|
||||
|
||||
std::unordered_map<ancestor, unsigned int> ancestry;
|
||||
|
||||
std::list<crypto::hash> txids;
|
||||
txids.push_back(start_txid);
|
||||
while (!txids.empty())
|
||||
{
|
||||
while (!txids.empty()) {
|
||||
const crypto::hash txid = txids.front();
|
||||
txids.pop_front();
|
||||
|
||||
|
@ -672,25 +645,23 @@ int main(int argc, char* argv[])
|
|||
if (coinbase)
|
||||
continue;
|
||||
|
||||
for (size_t ring = 0; ring < tx_data2.vin.size(); ++ring)
|
||||
{
|
||||
for (size_t ring = 0; ring < tx_data2.vin.size(); ++ring) {
|
||||
{
|
||||
const uint64_t amount = tx_data2.vin[ring].first;
|
||||
auto absolute_offsets = tx_data2.vin[ring].second;
|
||||
for (uint64_t offset: absolute_offsets)
|
||||
{
|
||||
for (uint64_t offset : absolute_offsets) {
|
||||
add_ancestor(ancestry, amount, offset);
|
||||
|
||||
// find the tx which created this output
|
||||
bool found = false;
|
||||
crypto::hash output_txid;
|
||||
if (!get_output_txid(state, db, amount, offset, output_txid))
|
||||
{
|
||||
if (!get_output_txid(state, db, amount, offset, output_txid)) {
|
||||
log::warning(logcat, "Output originating transaction not found");
|
||||
return 1;
|
||||
}
|
||||
|
||||
add_ancestry(state.ancestry, txid, get_ancestry(state.ancestry, output_txid));
|
||||
add_ancestry(
|
||||
state.ancestry, txid, get_ancestry(state.ancestry, output_txid));
|
||||
txids.push_back(output_txid);
|
||||
log::debug(logcat, "adding txid: {}", output_txid);
|
||||
}
|
||||
|
@ -698,10 +669,19 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
}
|
||||
|
||||
log::info(logcat, "Ancestry for {}: {} / {}", start_txid, get_deduplicated_ancestry(ancestry), get_full_ancestry(ancestry));
|
||||
for (const auto &i: ancestry)
|
||||
{
|
||||
log::info(logcat, "{}/{}: {}", cryptonote::print_money(i.first.amount), i.first.offset, i.second);
|
||||
log::info(
|
||||
logcat,
|
||||
"Ancestry for {}: {} / {}",
|
||||
start_txid,
|
||||
get_deduplicated_ancestry(ancestry),
|
||||
get_full_ancestry(ancestry));
|
||||
for (const auto& i : ancestry) {
|
||||
log::info(
|
||||
logcat,
|
||||
"{}/{}: {}",
|
||||
cryptonote::print_money(i.first.amount),
|
||||
i.first.offset,
|
||||
i.second);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -709,7 +689,12 @@ done:
|
|||
core_storage->deinit();
|
||||
|
||||
if (opt_show_cache_stats)
|
||||
log::info(logcat, "cache: txes {}%, blocks {}%, outputs {}%", std::to_string(cached_txes*100./total_txes), std::to_string(cached_blocks*100./total_blocks), std::to_string(cached_outputs*100./total_outputs));
|
||||
log::info(
|
||||
logcat,
|
||||
"cache: txes {}%, blocks {}%, outputs {}%",
|
||||
std::to_string(cached_txes * 100. / total_txes),
|
||||
std::to_string(cached_blocks * 100. / total_blocks),
|
||||
std::to_string(cached_outputs * 100. / total_outputs));
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
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,8 +41,7 @@ using namespace cryptonote;
|
|||
|
||||
static auto logcat = log::Cat("bcutil");
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int main(int argc, char* argv[]) {
|
||||
TRY_ENTRY();
|
||||
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
|
@ -51,11 +50,16 @@ int main(int argc, char* argv[])
|
|||
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_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);
|
||||
|
@ -70,18 +74,16 @@ int main(int argc, char* argv[])
|
|||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
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)
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
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;
|
||||
|
@ -90,10 +92,11 @@ int main(int argc, char* argv[])
|
|||
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())) {
|
||||
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;
|
||||
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);
|
||||
|
@ -101,21 +104,20 @@ int main(int argc, char* argv[])
|
|||
|
||||
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;
|
||||
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)
|
||||
{
|
||||
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))
|
||||
{
|
||||
if (!opt_txid_string.empty()) {
|
||||
if (!tools::hex_to_type(opt_txid_string, opt_txid)) {
|
||||
std::cerr << "Invalid txid" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
@ -123,24 +125,21 @@ int main(int argc, char* argv[])
|
|||
|
||||
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)
|
||||
{
|
||||
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();
|
||||
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
|
||||
{
|
||||
try {
|
||||
db->open(filename, core_storage->nettype(), DBF_RDONLY);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
} catch (const std::exception& e) {
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
|
@ -150,164 +149,153 @@ int main(int argc, char* argv[])
|
|||
log::warning(logcat, "Source blockchain storage initialized OK");
|
||||
|
||||
std::vector<crypto::hash> start_txids;
|
||||
if (!opt_txid_string.empty())
|
||||
{
|
||||
if (!opt_txid_string.empty()) {
|
||||
start_txids.push_back(opt_txid);
|
||||
}
|
||||
else
|
||||
{
|
||||
} 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))
|
||||
{
|
||||
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)
|
||||
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())
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
for (const crypto::hash& txid : txids) {
|
||||
std::string bd;
|
||||
if (!db->get_pruned_tx_blob(txid, 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))
|
||||
{
|
||||
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]))
|
||||
{
|
||||
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]))
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
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));
|
||||
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));
|
||||
} 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)
|
||||
{
|
||||
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);
|
||||
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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
} else {
|
||||
log::warning(
|
||||
logcat, "Bad vout type in txid {}", block_txid);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
if (!found) {
|
||||
log::warning(logcat, "Output originating transaction not found");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
log::warning(logcat, "Bad vin type in txid {}", txid);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!coinbase)
|
||||
{
|
||||
if (!coinbase) {
|
||||
std::swap(txids, new_txids);
|
||||
++depth;
|
||||
}
|
||||
}
|
||||
done:
|
||||
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)
|
||||
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,
|
||||
"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;
|
||||
|
|
|
@ -27,19 +27,18 @@
|
|||
// 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;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int main(int argc, char* argv[]) {
|
||||
using namespace oxen;
|
||||
auto logcat = log::Cat("bcutil");
|
||||
|
||||
|
@ -52,12 +51,16 @@ int main(int argc, char* argv[])
|
|||
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_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);
|
||||
|
@ -73,17 +76,15 @@ int main(int argc, char* argv[])
|
|||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
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)
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
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;
|
||||
|
@ -94,10 +95,11 @@ int main(int argc, char* argv[])
|
|||
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())) {
|
||||
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;
|
||||
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);
|
||||
|
@ -105,8 +107,7 @@ int main(int argc, char* argv[])
|
|||
|
||||
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)
|
||||
{
|
||||
if (opt_testnet && opt_devnet) {
|
||||
std::cerr << "Can't specify more than one of --testnet and --devnet" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
@ -123,10 +124,9 @@ int main(int argc, char* argv[])
|
|||
|
||||
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)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
@ -135,19 +135,21 @@ int main(int argc, char* argv[])
|
|||
auto filename = config_folder / db->get_db_name();
|
||||
|
||||
log::warning(logcat, "Loading blockchain from folder {} ...", filename);
|
||||
try
|
||||
{
|
||||
try {
|
||||
db->open(filename, core_storage->nettype(), DBF_RDONLY);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
} 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);
|
||||
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)
|
||||
{
|
||||
if (core_storage->get_blockchain_pruning_seed() && !opt_blocks_dat) {
|
||||
log::warning(logcat, "Blockchain is pruned, cannot export");
|
||||
return 1;
|
||||
}
|
||||
|
@ -156,13 +158,10 @@ int main(int argc, char* argv[])
|
|||
log::warning(logcat, "Source blockchain storage initialized OK");
|
||||
log::warning(logcat, "Exporting blockchain raw data...");
|
||||
|
||||
if (opt_blocks_dat)
|
||||
{
|
||||
if (opt_blocks_dat) {
|
||||
BlocksdatFile blocksdat;
|
||||
r = blocksdat.store_blockchain_raw(core_storage, NULL, output_file_path, block_stop);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
BootstrapFile bootstrap;
|
||||
r = bootstrap.store_blockchain_raw(core_storage, NULL, output_file_path, block_stop);
|
||||
}
|
||||
|
|
|
@ -27,29 +27,29 @@
|
|||
// 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 <atomic>
|
||||
#include <cstdio>
|
||||
#include <fmt/color.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <unistd.h>
|
||||
#include "blocks/blocks.h"
|
||||
#include "bootstrap_file.h"
|
||||
#include "bootstrap_serialization.h"
|
||||
#include "common/fs-format.h"
|
||||
#include "common/hex.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "cryptonote_core/uptime_proof.h"
|
||||
#include "cryptonote_protocol/quorumnet.h"
|
||||
#include "epee/misc_log_ex.h"
|
||||
#include "logging/oxen_logger.h"
|
||||
#include "bootstrap_file.h"
|
||||
#include "bootstrap_serialization.h"
|
||||
#include "blocks/blocks.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "serialization/binary_utils.h"
|
||||
#include "cryptonote_core/uptime_proof.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "common/hex.h"
|
||||
#include "common/fs-format.h"
|
||||
#include <fmt/color.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace {
|
||||
// CONFIG
|
||||
bool opt_batch = true;
|
||||
bool opt_verify = true; // use add_new_block, which does verification before calling add_block
|
||||
|
@ -79,9 +79,7 @@ const command_line::arg_descriptor<bool> arg_recalculate_difficulty = {
|
|||
// difficulty values (and the chain that kept going violated the correct difficulty, and got
|
||||
// checkpointed multiple times because enough of the network followed it).
|
||||
false};
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace po = boost::program_options;
|
||||
|
||||
|
@ -90,8 +88,7 @@ using namespace cryptonote;
|
|||
static auto logcat = log::Cat("bcutil");
|
||||
|
||||
// db_mode: safe, fast, fastest
|
||||
int get_db_flags_from_mode(const std::string& db_mode)
|
||||
{
|
||||
int get_db_flags_from_mode(const std::string& db_mode) {
|
||||
int db_flags = 0;
|
||||
if (db_mode == "safe")
|
||||
db_flags = DBF_SAFE;
|
||||
|
@ -102,23 +99,19 @@ int get_db_flags_from_mode(const std::string& db_mode)
|
|||
return db_flags;
|
||||
}
|
||||
|
||||
int pop_blocks(cryptonote::core& core, int num_blocks)
|
||||
{
|
||||
int pop_blocks(cryptonote::core& core, int num_blocks) {
|
||||
bool use_batch = opt_batch;
|
||||
|
||||
if (use_batch) core.get_blockchain_storage().get_db().batch_start();
|
||||
|
||||
try
|
||||
{
|
||||
core.get_blockchain_storage().pop_blocks(num_blocks);
|
||||
if (use_batch)
|
||||
{
|
||||
core.get_blockchain_storage().get_db().batch_start();
|
||||
|
||||
try {
|
||||
core.get_blockchain_storage().pop_blocks(num_blocks);
|
||||
if (use_batch) {
|
||||
core.get_blockchain_storage().get_db().batch_stop();
|
||||
core.get_blockchain_storage().get_db().show_stats();
|
||||
}
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
} catch (const std::exception& e) {
|
||||
// There was an error, so don't commit pending data.
|
||||
// Destructor will abort write txn.
|
||||
}
|
||||
|
@ -126,8 +119,7 @@ int pop_blocks(cryptonote::core& core, int num_blocks)
|
|||
return num_blocks;
|
||||
}
|
||||
|
||||
int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &blocks, bool force)
|
||||
{
|
||||
int check_flush(cryptonote::core& core, std::vector<block_complete_entry>& blocks, bool force) {
|
||||
if (blocks.empty())
|
||||
return 0;
|
||||
if (!force && blocks.size() < db_batch_size)
|
||||
|
@ -139,12 +131,13 @@ int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &block
|
|||
return 0;
|
||||
|
||||
std::vector<crypto::hash> hashes;
|
||||
for (const auto &b: blocks)
|
||||
{
|
||||
for (const auto& b : blocks) {
|
||||
cryptonote::block block;
|
||||
if (!parse_and_validate_block_from_blob(b.block, block))
|
||||
{
|
||||
log::error(logcat, "Failed to parse block: {}", tools::type_to_hex(get_blob_hash(b.block)));
|
||||
if (!parse_and_validate_block_from_blob(b.block, block)) {
|
||||
log::error(
|
||||
logcat,
|
||||
"Failed to parse block: {}",
|
||||
tools::type_to_hex(get_blob_hash(b.block)));
|
||||
core.cleanup_handle_incoming_blocks();
|
||||
return 1;
|
||||
}
|
||||
|
@ -154,29 +147,27 @@ int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &block
|
|||
|
||||
// TODO(doyle): Checkpointing
|
||||
std::vector<block> pblocks;
|
||||
if (!core.prepare_handle_incoming_blocks(blocks, pblocks))
|
||||
{
|
||||
if (!core.prepare_handle_incoming_blocks(blocks, pblocks)) {
|
||||
log::error(logcat, "Failed to prepare to add blocks");
|
||||
return 1;
|
||||
}
|
||||
if (!pblocks.empty() && pblocks.size() != blocks.size())
|
||||
{
|
||||
if (!pblocks.empty() && pblocks.size() != blocks.size()) {
|
||||
log::error(logcat, "Unexpected parsed blocks size");
|
||||
core.cleanup_handle_incoming_blocks();
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t blockidx = 0;
|
||||
for(const block_complete_entry& block_entry: blocks)
|
||||
{
|
||||
for (const block_complete_entry& block_entry : blocks) {
|
||||
// process transactions
|
||||
for(auto& tx_blob: block_entry.txs)
|
||||
{
|
||||
for (auto& tx_blob : block_entry.txs) {
|
||||
tx_verification_context tvc{};
|
||||
core.handle_incoming_tx(tx_blob, tvc, tx_pool_options::from_block());
|
||||
if(tvc.m_verifivation_failed)
|
||||
{
|
||||
log::error(logcat, "transaction verification failed, tx_id = {}", tools::type_to_hex(get_blob_hash(tx_blob)));
|
||||
if (tvc.m_verifivation_failed) {
|
||||
log::error(
|
||||
logcat,
|
||||
"transaction verification failed, tx_id = {}",
|
||||
tools::type_to_hex(get_blob_hash(tx_blob)));
|
||||
core.cleanup_handle_incoming_blocks();
|
||||
return 1;
|
||||
}
|
||||
|
@ -186,16 +177,22 @@ int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &block
|
|||
|
||||
block_verification_context bvc{};
|
||||
|
||||
core.handle_incoming_block(block_entry.block, pblocks.empty() ? NULL : &pblocks[blockidx++], bvc, nullptr /*checkpoint*/, false); // <--- process block
|
||||
core.handle_incoming_block(
|
||||
block_entry.block,
|
||||
pblocks.empty() ? NULL : &pblocks[blockidx++],
|
||||
bvc,
|
||||
nullptr /*checkpoint*/,
|
||||
false); // <--- process block
|
||||
|
||||
if(bvc.m_verifivation_failed)
|
||||
{
|
||||
log::error(logcat, "Block verification failed, id = {}", tools::type_to_hex(get_blob_hash(block_entry.block)));
|
||||
if (bvc.m_verifivation_failed) {
|
||||
log::error(
|
||||
logcat,
|
||||
"Block verification failed, id = {}",
|
||||
tools::type_to_hex(get_blob_hash(block_entry.block)));
|
||||
core.cleanup_handle_incoming_blocks();
|
||||
return 1;
|
||||
}
|
||||
if(bvc.m_marked_as_orphaned)
|
||||
{
|
||||
if (bvc.m_marked_as_orphaned) {
|
||||
log::error(logcat, "Block received at sync phase was marked as orphaned");
|
||||
core.cleanup_handle_incoming_blocks();
|
||||
return 1;
|
||||
|
@ -209,15 +206,14 @@ int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &block
|
|||
return 0;
|
||||
}
|
||||
|
||||
int import_from_file(cryptonote::core& core, const fs::path& import_file_path, uint64_t block_stop=0)
|
||||
{
|
||||
int import_from_file(
|
||||
cryptonote::core& core, const fs::path& import_file_path, uint64_t block_stop = 0) {
|
||||
// Reset stats, in case we're using newly created db, accumulating stats
|
||||
// from addition of genesis block.
|
||||
// This aligns internal db counts with importer counts.
|
||||
core.get_blockchain_storage().get_db().reset_stats();
|
||||
|
||||
if (std::error_code ec; !fs::exists(import_file_path, ec))
|
||||
{
|
||||
if (std::error_code ec; !fs::exists(import_file_path, ec)) {
|
||||
log::error(logcat, "bootstrap file not found: {}", import_file_path);
|
||||
return false;
|
||||
}
|
||||
|
@ -231,10 +227,13 @@ int import_from_file(cryptonote::core& core, const fs::path& import_file_path, u
|
|||
std::streampos pos;
|
||||
// BootstrapFile bootstrap(import_file_path);
|
||||
uint64_t total_source_blocks = bootstrap.count_blocks(import_file_path, pos, seek_height);
|
||||
log::info(logcat, "bootstrap file last block number: {} (zero-based height) total blocks: {}", total_source_blocks-1, total_source_blocks);
|
||||
log::info(
|
||||
logcat,
|
||||
"bootstrap file last block number: {} (zero-based height) total blocks: {}",
|
||||
total_source_blocks - 1,
|
||||
total_source_blocks);
|
||||
|
||||
if (total_source_blocks-1 <= start_height)
|
||||
{
|
||||
if (total_source_blocks - 1 <= start_height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -242,8 +241,7 @@ int import_from_file(cryptonote::core& core, const fs::path& import_file_path, u
|
|||
|
||||
fs::ifstream import_file{import_file_path, std::ios::binary};
|
||||
|
||||
if (import_file.fail())
|
||||
{
|
||||
if (import_file.fail()) {
|
||||
log::error(logcat, "import_file.open() fail");
|
||||
return false;
|
||||
}
|
||||
|
@ -261,8 +259,7 @@ int import_from_file(cryptonote::core& core, const fs::path& import_file_path, u
|
|||
// Note that a new blockchain will start with block number 0 (total blocks: 1)
|
||||
// due to genesis block being added at initialization.
|
||||
|
||||
if (! block_stop)
|
||||
{
|
||||
if (!block_stop) {
|
||||
block_stop = total_source_blocks - 1;
|
||||
}
|
||||
|
||||
|
@ -284,17 +281,15 @@ int import_from_file(cryptonote::core& core, const fs::path& import_file_path, u
|
|||
{
|
||||
bool q2 = false;
|
||||
import_file.seekg(pos);
|
||||
bytes_read = bootstrap.count_bytes(import_file, start_height-seek_height, h, q2);
|
||||
if (q2)
|
||||
{
|
||||
bytes_read = bootstrap.count_bytes(import_file, start_height - seek_height, h, q2);
|
||||
if (q2) {
|
||||
quit = 2;
|
||||
goto quitting;
|
||||
}
|
||||
h = start_height;
|
||||
}
|
||||
|
||||
if (use_batch)
|
||||
{
|
||||
if (use_batch) {
|
||||
uint64_t bytes, h2;
|
||||
bool q2;
|
||||
pos = import_file.tellg();
|
||||
|
@ -304,12 +299,11 @@ int import_from_file(cryptonote::core& core, const fs::path& import_file_path, u
|
|||
import_file.seekg(pos);
|
||||
core.get_blockchain_storage().get_db().batch_start(db_batch_size, bytes);
|
||||
}
|
||||
while (! quit)
|
||||
{
|
||||
while (!quit) {
|
||||
uint32_t chunk_size;
|
||||
import_file.read(buffer1, sizeof(chunk_size));
|
||||
// TODO: bootstrap.read_chunk();
|
||||
if (! import_file) {
|
||||
if (!import_file) {
|
||||
std::cout << refresh_string;
|
||||
log::info(logcat, "End of file reached");
|
||||
quit = 1;
|
||||
|
@ -324,49 +318,50 @@ int import_from_file(cryptonote::core& core, const fs::path& import_file_path, u
|
|||
}
|
||||
log::debug(logcat, "chunk_size: {}", chunk_size);
|
||||
|
||||
if (chunk_size > BUFFER_SIZE)
|
||||
{
|
||||
log::warning(logcat, "WARNING: chunk_size {} > BUFFER_SIZE {}", chunk_size, BUFFER_SIZE);
|
||||
if (chunk_size > BUFFER_SIZE) {
|
||||
log::warning(
|
||||
logcat, "WARNING: chunk_size {} > BUFFER_SIZE {}", chunk_size, BUFFER_SIZE);
|
||||
throw std::runtime_error("Aborting: chunk size exceeds buffer size");
|
||||
}
|
||||
if (chunk_size > CHUNK_SIZE_WARNING_THRESHOLD)
|
||||
{
|
||||
if (chunk_size > CHUNK_SIZE_WARNING_THRESHOLD) {
|
||||
log::info(logcat, "NOTE: chunk_size {} > {}", chunk_size, CHUNK_SIZE_WARNING_THRESHOLD);
|
||||
}
|
||||
else if (chunk_size == 0) {
|
||||
} else if (chunk_size == 0) {
|
||||
log::error(logcat, "ERROR: chunk_size == 0");
|
||||
return 2;
|
||||
}
|
||||
import_file.read(buffer_block, chunk_size);
|
||||
if (! import_file) {
|
||||
if (import_file.eof())
|
||||
{
|
||||
if (!import_file) {
|
||||
if (import_file.eof()) {
|
||||
std::cout << refresh_string;
|
||||
log::info(logcat, "End of file reached - file was truncated");
|
||||
quit = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
log::error(logcat, "ERROR: unexpected end of file: bytes read before error: {} of chunk_size {}", import_file.gcount(), chunk_size);
|
||||
} else {
|
||||
log::error(
|
||||
logcat,
|
||||
"ERROR: unexpected end of file: bytes read before error: {} of chunk_size "
|
||||
"{}",
|
||||
import_file.gcount(),
|
||||
chunk_size);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
bytes_read += chunk_size;
|
||||
log::debug(logcat, "Total bytes read: {}", bytes_read);
|
||||
|
||||
if (h > block_stop)
|
||||
{
|
||||
std::cout << refresh_string << "block " << h-1
|
||||
<< " / " << block_stop
|
||||
<< "\n" << std::endl;
|
||||
log::info(logcat, "Specified block number reached - stopping. block: {} total blocks: {}", h-1, h);
|
||||
if (h > block_stop) {
|
||||
std::cout << refresh_string << "block " << h - 1 << " / " << block_stop << "\n"
|
||||
<< std::endl;
|
||||
log::info(
|
||||
logcat,
|
||||
"Specified block number reached - stopping. block: {} total blocks: {}",
|
||||
h - 1,
|
||||
h);
|
||||
quit = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
bootstrap::block_package bp;
|
||||
try {
|
||||
serialization::parse_binary(std::string_view{buffer_block, chunk_size}, bp);
|
||||
|
@ -376,49 +371,39 @@ int import_from_file(cryptonote::core& core, const fs::path& import_file_path, u
|
|||
|
||||
int display_interval = 1000;
|
||||
int progress_interval = 10;
|
||||
// NOTE: use of NUM_BLOCKS_PER_CHUNK is a placeholder in case multi-block chunks are later supported.
|
||||
for (int chunk_ind = 0; chunk_ind < NUM_BLOCKS_PER_CHUNK; ++chunk_ind)
|
||||
{
|
||||
// NOTE: use of NUM_BLOCKS_PER_CHUNK is a placeholder in case multi-block chunks are
|
||||
// later supported.
|
||||
for (int chunk_ind = 0; chunk_ind < NUM_BLOCKS_PER_CHUNK; ++chunk_ind) {
|
||||
++h;
|
||||
if ((h-1) % display_interval == 0)
|
||||
{
|
||||
if ((h - 1) % display_interval == 0) {
|
||||
std::cout << refresh_string;
|
||||
log::debug(logcat, "loading block number {}", h-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
log::debug(logcat, "loading block number {}", h-1);
|
||||
log::debug(logcat, "loading block number {}", h - 1);
|
||||
} else {
|
||||
log::debug(logcat, "loading block number {}", h - 1);
|
||||
}
|
||||
b = bp.block;
|
||||
log::debug(logcat, "block prev_id: {}\n", b.prev_id);
|
||||
|
||||
if ((h-1) % progress_interval == 0)
|
||||
{
|
||||
std::cout << refresh_string << "block " << h-1
|
||||
<< " / " << block_stop
|
||||
<< "\r" << std::flush;
|
||||
if ((h - 1) % progress_interval == 0) {
|
||||
std::cout << refresh_string << "block " << h - 1 << " / " << block_stop << "\r"
|
||||
<< std::flush;
|
||||
}
|
||||
|
||||
if (opt_verify)
|
||||
{
|
||||
if (opt_verify) {
|
||||
std::string block;
|
||||
cryptonote::block_to_blob(bp.block, block);
|
||||
std::vector<std::string> txs;
|
||||
for (const auto &tx: bp.txs)
|
||||
{
|
||||
for (const auto& tx : bp.txs) {
|
||||
txs.push_back(std::string());
|
||||
cryptonote::tx_to_blob(tx, txs.back());
|
||||
}
|
||||
blocks.push_back({block, txs});
|
||||
int ret = check_flush(core, blocks, false);
|
||||
if (ret)
|
||||
{
|
||||
if (ret) {
|
||||
quit = 2; // make sure we don't commit partial block data
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
std::vector<std::pair<transaction, std::string>> txs;
|
||||
std::vector<transaction> archived_txs;
|
||||
|
||||
|
@ -426,8 +411,7 @@ int import_from_file(cryptonote::core& core, const fs::path& import_file_path, u
|
|||
|
||||
// tx number 1: coinbase tx
|
||||
// tx number 2 onwards: archived_txs
|
||||
for (const transaction &tx : archived_txs)
|
||||
{
|
||||
for (const transaction& tx : archived_txs) {
|
||||
// add blocks with verification.
|
||||
// for Blockchain and blockchain_storage add_new_block().
|
||||
// for add_block() method, without (much) processing.
|
||||
|
@ -447,33 +431,37 @@ int import_from_file(cryptonote::core& core, const fs::path& import_file_path, u
|
|||
cumulative_difficulty = bp.cumulative_difficulty;
|
||||
coins_generated = bp.coins_generated;
|
||||
|
||||
try
|
||||
{
|
||||
uint64_t long_term_block_weight = core.get_blockchain_storage().get_next_long_term_block_weight(block_weight);
|
||||
core.get_blockchain_storage().get_db().add_block(std::make_pair(b, block_to_blob(b)), block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
try {
|
||||
uint64_t long_term_block_weight =
|
||||
core.get_blockchain_storage().get_next_long_term_block_weight(
|
||||
block_weight);
|
||||
core.get_blockchain_storage().get_db().add_block(
|
||||
std::make_pair(b, block_to_blob(b)),
|
||||
block_weight,
|
||||
long_term_block_weight,
|
||||
cumulative_difficulty,
|
||||
coins_generated,
|
||||
txs);
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << refresh_string;
|
||||
log::error(logcat, "Error adding block to blockchain: {}", e.what());
|
||||
quit = 2; // make sure we don't commit partial block data
|
||||
break;
|
||||
}
|
||||
|
||||
if (use_batch)
|
||||
{
|
||||
if ((h-1) % db_batch_size == 0)
|
||||
{
|
||||
if (use_batch) {
|
||||
if ((h - 1) % db_batch_size == 0) {
|
||||
uint64_t bytes, h2;
|
||||
bool q2;
|
||||
std::cout << refresh_string;
|
||||
// zero-based height
|
||||
std::cout << "\n[- batch commit at height " << h-1 << " -]\n";
|
||||
std::cout << "\n[- batch commit at height " << h - 1 << " -]\n";
|
||||
core.get_blockchain_storage().get_db().batch_stop();
|
||||
pos = import_file.tellg();
|
||||
bytes = bootstrap.count_bytes(import_file, db_batch_size, h2, q2);
|
||||
import_file.seekg(pos);
|
||||
core.get_blockchain_storage().get_db().batch_start(db_batch_size, bytes);
|
||||
core.get_blockchain_storage().get_db().batch_start(
|
||||
db_batch_size, bytes);
|
||||
std::cout << "\n";
|
||||
core.get_blockchain_storage().get_db().show_stats();
|
||||
}
|
||||
|
@ -481,9 +469,7 @@ int import_from_file(cryptonote::core& core, const fs::path& import_file_path, u
|
|||
}
|
||||
++num_imported;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << refresh_string;
|
||||
log::error(logcat, "exception while reading from file, height={}: {}", h, e.what());
|
||||
return 2;
|
||||
|
@ -493,22 +479,17 @@ int import_from_file(cryptonote::core& core, const fs::path& import_file_path, u
|
|||
quitting:
|
||||
import_file.close();
|
||||
|
||||
if (opt_verify)
|
||||
{
|
||||
if (opt_verify) {
|
||||
int ret = check_flush(core, blocks, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (use_batch)
|
||||
{
|
||||
if (quit > 1)
|
||||
{
|
||||
if (use_batch) {
|
||||
if (quit > 1) {
|
||||
// There was an error, so don't commit pending data.
|
||||
// Destructor will abort write txn.
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
core.get_blockchain_storage().get_db().batch_stop();
|
||||
}
|
||||
}
|
||||
|
@ -517,14 +498,13 @@ quitting:
|
|||
log::info(logcat, "Number of blocks imported: {}", num_imported);
|
||||
if (h > 0)
|
||||
// TODO: if there was an error, the last added block is probably at zero-based height h-2
|
||||
log::info(logcat, "Finished at block: {} total blocks: {}", h-1, h);
|
||||
log::info(logcat, "Finished at block: {} total blocks: {}", h - 1, h);
|
||||
|
||||
std::cout << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int main(int argc, char* argv[]) {
|
||||
TRY_ENTRY();
|
||||
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
|
@ -539,23 +519,28 @@ int main(int argc, char* argv[])
|
|||
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_input_file = {"input-file", "Specify input 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};
|
||||
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_input_file = {
|
||||
"input-file", "Specify input 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<uint64_t> arg_batch_size = {"batch-size", "", db_batch_size};
|
||||
const command_line::arg_descriptor<uint64_t> arg_pop_blocks = {"pop-blocks", "Remove blocks from end of blockchain", num_blocks};
|
||||
const command_line::arg_descriptor<uint64_t> arg_pop_blocks = {
|
||||
"pop-blocks", "Remove blocks from end of blockchain", num_blocks};
|
||||
const command_line::arg_descriptor<bool> arg_count_blocks = {
|
||||
"count-blocks"
|
||||
, "Count blocks in bootstrap file and exit"
|
||||
, false
|
||||
};
|
||||
const command_line::arg_descriptor<bool> arg_noverify = {"dangerous-unverified-import",
|
||||
"Blindly trust the import file and use potentially malicious blocks and transactions during import (only enable if you exported the file yourself)", false};
|
||||
const command_line::arg_descriptor<bool> arg_batch = {"batch",
|
||||
"Batch transactions for faster import", true};
|
||||
const command_line::arg_descriptor<bool> arg_resume = {"resume",
|
||||
"Resume from current height if output database already exists", true};
|
||||
"count-blocks", "Count blocks in bootstrap file and exit", false};
|
||||
const command_line::arg_descriptor<bool> arg_noverify = {
|
||||
"dangerous-unverified-import",
|
||||
"Blindly trust the import file and use potentially malicious blocks and transactions "
|
||||
"during import (only enable if you exported the file yourself)",
|
||||
false};
|
||||
const command_line::arg_descriptor<bool> arg_batch = {
|
||||
"batch", "Batch transactions for faster import", true};
|
||||
const command_line::arg_descriptor<bool> arg_resume = {
|
||||
"resume", "Resume from current height if output database already exists", true};
|
||||
|
||||
command_line::add_arg(desc_cmd_sett, arg_input_file);
|
||||
command_line::add_arg(desc_cmd_sett, arg_log_level);
|
||||
|
@ -570,24 +555,22 @@ int main(int argc, char* argv[])
|
|||
|
||||
// call add_options() directly for these arguments since
|
||||
// command_line helpers support only boolean switch, not boolean argument
|
||||
desc_cmd_sett.add_options()
|
||||
(arg_noverify.name, make_semantic(arg_noverify), arg_noverify.description)
|
||||
(arg_batch.name, make_semantic(arg_batch), arg_batch.description)
|
||||
(arg_resume.name, make_semantic(arg_resume), arg_resume.description)
|
||||
;
|
||||
desc_cmd_sett.add_options()(
|
||||
arg_noverify.name, make_semantic(arg_noverify), arg_noverify.description)(
|
||||
arg_batch.name, make_semantic(arg_batch), arg_batch.description)(
|
||||
arg_resume.name, make_semantic(arg_resume), arg_resume.description);
|
||||
|
||||
po::options_description desc_options("Allowed options");
|
||||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
cryptonote::core::init_options(desc_options);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
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)
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
opt_verify = !command_line::get_arg(vm, arg_noverify);
|
||||
|
@ -596,50 +579,45 @@ int main(int argc, char* argv[])
|
|||
block_stop = command_line::get_arg(vm, arg_block_stop);
|
||||
db_batch_size = command_line::get_arg(vm, arg_batch_size);
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
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 (! opt_batch && !command_line::is_arg_defaulted(vm, arg_batch_size))
|
||||
{
|
||||
if (!opt_batch && !command_line::is_arg_defaulted(vm, arg_batch_size)) {
|
||||
std::cerr << "Error: batch-size set, but batch option not enabled\n";
|
||||
return 1;
|
||||
}
|
||||
if (! db_batch_size)
|
||||
{
|
||||
if (!db_batch_size) {
|
||||
std::cerr << "Error: batch-size must be > 0\n";
|
||||
return 1;
|
||||
}
|
||||
if (opt_verify && command_line::is_arg_defaulted(vm, arg_batch_size))
|
||||
{
|
||||
if (opt_verify && command_line::is_arg_defaulted(vm, arg_batch_size)) {
|
||||
// usually want batch size default lower if verify on, so progress can be
|
||||
// frequently saved.
|
||||
//
|
||||
// currently, with Windows, default batch size is low, so ignore
|
||||
// default db_batch_size_verify unless it's even lower
|
||||
if (db_batch_size > db_batch_size_verify)
|
||||
{
|
||||
if (db_batch_size > db_batch_size_verify) {
|
||||
db_batch_size = db_batch_size_verify;
|
||||
}
|
||||
}
|
||||
|
||||
opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
||||
opt_devnet = command_line::get_arg(vm, cryptonote::arg_devnet_on);
|
||||
if (opt_testnet && opt_devnet)
|
||||
{
|
||||
if (opt_testnet && opt_devnet) {
|
||||
std::cerr << "Error: Can't specify more than one of --testnet and --devnet\n";
|
||||
return 1;
|
||||
}
|
||||
m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
auto log_file_path = m_config_folder + "oxen-blockchain-import.log";
|
||||
log::Level log_level;
|
||||
if(auto level = oxen::logging::parse_level(command_line::get_arg(vm, arg_log_level).c_str())) {
|
||||
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;
|
||||
std::cerr << "Incorrect log level: " << command_line::get_arg(vm, arg_log_level).c_str()
|
||||
<< std::endl;
|
||||
throw std::runtime_error{"Incorrect log level"};
|
||||
}
|
||||
|
||||
|
@ -653,8 +631,7 @@ int main(int argc, char* argv[])
|
|||
else
|
||||
import_file_path = fs::u8path(m_config_folder) / "export" / BLOCKCHAIN_RAW;
|
||||
|
||||
if (command_line::has_arg(vm, arg_count_blocks))
|
||||
{
|
||||
if (command_line::has_arg(vm, arg_count_blocks)) {
|
||||
BootstrapFile bootstrap;
|
||||
bootstrap.count_blocks(import_file_path);
|
||||
return 0;
|
||||
|
@ -662,12 +639,9 @@ int main(int argc, char* argv[])
|
|||
|
||||
log::info(logcat, "database: LMDB");
|
||||
log::info(logcat, "verify: {}", opt_verify);
|
||||
if (opt_batch)
|
||||
{
|
||||
if (opt_batch) {
|
||||
log::info(logcat, "batch: {} batch size: {}", opt_batch, db_batch_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
log::info(logcat, "batch: {}", opt_batch);
|
||||
}
|
||||
log::info(logcat, "resume: {}", opt_resume);
|
||||
|
@ -676,23 +650,26 @@ int main(int argc, char* argv[])
|
|||
log::info(logcat, "bootstrap file path: {}", import_file_path);
|
||||
log::info(logcat, "database path: {}", m_config_folder);
|
||||
|
||||
if (!opt_verify)
|
||||
{
|
||||
log::warning(logcat, fg(fmt::terminal_color::red), "\n\
|
||||
if (!opt_verify) {
|
||||
log::warning(
|
||||
logcat,
|
||||
fg(fmt::terminal_color::red),
|
||||
"\n\
|
||||
Import is set to proceed WITHOUT VERIFICATION.\n\
|
||||
This is a DANGEROUS operation: if the file was tampered with in transit, or obtained from a malicious source,\n\
|
||||
you could end up with a compromised database. It is recommended to NOT use {}.\n\
|
||||
*****************************************************************************************\n\
|
||||
You have 90 seconds to press ^C or terminate this program before unverified import starts\n\
|
||||
*****************************************************************************************", arg_noverify.name);
|
||||
*****************************************************************************************",
|
||||
arg_noverify.name);
|
||||
sleep(90);
|
||||
}
|
||||
|
||||
//TODO: currently using cryptonote_protocol stub for this kind of test, use real validation of relayed objects
|
||||
// TODO: currently using cryptonote_protocol stub for this kind of test, use real validation of
|
||||
// relayed objects
|
||||
cryptonote::core core;
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
|
||||
#if defined(PER_BLOCK_CHECKPOINT)
|
||||
const GetCheckpointsCallback& get_checkpoints = blocks::GetCheckpointsData;
|
||||
|
@ -702,19 +679,23 @@ int main(int argc, char* argv[])
|
|||
|
||||
quorumnet::init_core_callbacks();
|
||||
|
||||
if (!core.init(vm, nullptr, get_checkpoints))
|
||||
{
|
||||
if (!core.init(vm, nullptr, get_checkpoints)) {
|
||||
std::cerr << "Failed to initialize core\n";
|
||||
return 1;
|
||||
}
|
||||
core.get_blockchain_storage().get_db().set_batch_transactions(true);
|
||||
|
||||
if (!command_line::is_arg_defaulted(vm, arg_pop_blocks))
|
||||
{
|
||||
if (!command_line::is_arg_defaulted(vm, arg_pop_blocks)) {
|
||||
num_blocks = command_line::get_arg(vm, arg_pop_blocks);
|
||||
log::info(logcat, "height: {}", core.get_blockchain_storage().get_current_blockchain_height());
|
||||
log::info(
|
||||
logcat,
|
||||
"height: {}",
|
||||
core.get_blockchain_storage().get_current_blockchain_height());
|
||||
pop_blocks(core, num_blocks);
|
||||
log::info(logcat, "height: {}", core.get_blockchain_storage().get_current_blockchain_height());
|
||||
log::info(
|
||||
logcat,
|
||||
"height: {}",
|
||||
core.get_blockchain_storage().get_current_blockchain_height());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -728,10 +709,9 @@ int main(int argc, char* argv[])
|
|||
// - disk sync if needed
|
||||
//
|
||||
core.deinit();
|
||||
}
|
||||
catch (const DB_ERROR& e)
|
||||
{
|
||||
std::cout << std::string("Error loading blockchain db: ") + e.what() + " -- shutting down now\n";
|
||||
} catch (const DB_ERROR& e) {
|
||||
std::cout << std::string("Error loading blockchain db: ") + e.what() +
|
||||
" -- shutting down now\n";
|
||||
core.deinit();
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
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) { }
|
||||
m_mempool(m_blockchain) {}
|
||||
};
|
||||
|
||||
#endif // BLOCKCHAIN_OBJECTS_H
|
||||
|
|
|
@ -26,20 +26,22 @@
|
|||
// 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 <array>
|
||||
#include <lmdb.h>
|
||||
#include "common/command_line.h"
|
||||
#include "common/pruning.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/fs.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "cryptonote_core/blockchain.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "blockchain_db/lmdb/db_lmdb.h"
|
||||
#include "blockchain_objects.h"
|
||||
#include "common/command_line.h"
|
||||
#include "common/fs.h"
|
||||
#include "common/pruning.h"
|
||||
#include "common/string_util.h"
|
||||
#include "cryptonote_core/blockchain.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "version.h"
|
||||
|
||||
#define MDB_val_set(var, val) MDB_val var = {sizeof(val), (void *)&val}
|
||||
#define MDB_val_set(var, val) MDB_val var = {sizeof(val), (void*)&val}
|
||||
|
||||
namespace po = boost::program_options;
|
||||
using namespace cryptonote;
|
||||
|
@ -52,16 +54,20 @@ static fs::path db_path;
|
|||
static uint64_t records_per_sync = 128;
|
||||
static const size_t slack = 512 * 1024 * 1024;
|
||||
|
||||
static std::error_code replace_file(const fs::path& replacement_name, const fs::path& replaced_name)
|
||||
{
|
||||
static std::error_code replace_file(
|
||||
const fs::path& replacement_name, const fs::path& replaced_name) {
|
||||
std::error_code ec = fs::rename(replacement_name, replaced_name);
|
||||
if (ec)
|
||||
log::error(logcat, "Error renaming {} to {}: {}", replacement_name, replaced_name, ec.message());
|
||||
log::error(
|
||||
logcat,
|
||||
"Error renaming {} to {}: {}",
|
||||
replacement_name,
|
||||
replaced_name,
|
||||
ec.message());
|
||||
return ec;
|
||||
}
|
||||
|
||||
static void open(MDB_env *&env, const fs::path &path, uint64_t db_flags, bool readonly)
|
||||
{
|
||||
static void open(MDB_env*& env, const fs::path& path, uint64_t db_flags, bool readonly) {
|
||||
int dbr;
|
||||
int flags = 0;
|
||||
|
||||
|
@ -73,33 +79,34 @@ static void open(MDB_env *&env, const fs::path &path, uint64_t db_flags, bool re
|
|||
flags |= MDB_RDONLY;
|
||||
|
||||
dbr = mdb_env_create(&env);
|
||||
if (dbr) throw std::runtime_error("Failed to create LDMB environment: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to create LDMB environment: " + std::string(mdb_strerror(dbr)));
|
||||
dbr = mdb_env_set_maxdbs(env, 32);
|
||||
if (dbr) throw std::runtime_error("Failed to set max env dbs: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to set max env dbs: " + std::string(mdb_strerror(dbr)));
|
||||
dbr = mdb_env_open(env, path.string().c_str(), flags, 0664);
|
||||
if (dbr) throw std::runtime_error("Failed to open database file '"
|
||||
+ path.string() + "': " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to open database file '" + path.string() +
|
||||
"': " + std::string(mdb_strerror(dbr)));
|
||||
}
|
||||
|
||||
static void close(MDB_env *env)
|
||||
{
|
||||
static void close(MDB_env* env) {
|
||||
mdb_env_close(env);
|
||||
}
|
||||
|
||||
static void add_size(MDB_env *env, uint64_t bytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
static void add_size(MDB_env* env, uint64_t bytes) {
|
||||
try {
|
||||
auto si = fs::space(db_path);
|
||||
if(si.available < bytes)
|
||||
{
|
||||
log::error(logcat, "!! WARNING: Insufficient free space to extend database !!: ",
|
||||
if (si.available < bytes) {
|
||||
log::error(
|
||||
logcat,
|
||||
"!! WARNING: Insufficient free space to extend database !!: ",
|
||||
(si.available >> 20L) << " MB available, " << (bytes >> 20L) << " MB needed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
} catch (...) {
|
||||
// print something but proceed.
|
||||
log::warning(logcat, "Unable to query free disk space.");
|
||||
}
|
||||
|
@ -114,13 +121,18 @@ static void add_size(MDB_env *env, uint64_t bytes)
|
|||
|
||||
int result = mdb_env_set_mapsize(env, new_mapsize);
|
||||
if (result)
|
||||
throw std::runtime_error("Failed to set new mapsize to " + std::to_string(new_mapsize) + ": " + std::string(mdb_strerror(result)));
|
||||
throw std::runtime_error(
|
||||
"Failed to set new mapsize to " + std::to_string(new_mapsize) + ": " +
|
||||
std::string(mdb_strerror(result)));
|
||||
|
||||
log::info(logcat, "LMDB Mapsize increased. Old: {}MiB, New: {}MiB", mei.me_mapsize / (1024 * 1024), new_mapsize / (1024 * 1024));
|
||||
log::info(
|
||||
logcat,
|
||||
"LMDB Mapsize increased. Old: {}MiB, New: {}MiB",
|
||||
mei.me_mapsize / (1024 * 1024),
|
||||
new_mapsize / (1024 * 1024));
|
||||
}
|
||||
|
||||
static void check_resize(MDB_env *env, size_t bytes)
|
||||
{
|
||||
static void check_resize(MDB_env* env, size_t bytes) {
|
||||
MDB_envinfo mei;
|
||||
MDB_stat mst;
|
||||
|
||||
|
@ -132,21 +144,28 @@ static void check_resize(MDB_env *env, size_t bytes)
|
|||
add_size(env, size_used + bytes + 2 * slack - mei.me_mapsize);
|
||||
}
|
||||
|
||||
static bool resize_point(size_t nrecords, MDB_env *env, MDB_txn **txn, size_t &bytes)
|
||||
{
|
||||
static bool resize_point(size_t nrecords, MDB_env* env, MDB_txn** txn, size_t& bytes) {
|
||||
if (nrecords % records_per_sync && bytes <= slack / 2)
|
||||
return false;
|
||||
int dbr = mdb_txn_commit(*txn);
|
||||
if (dbr) throw std::runtime_error("Failed to commit txn: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to commit txn: " + std::string(mdb_strerror(dbr)));
|
||||
check_resize(env, bytes);
|
||||
dbr = mdb_txn_begin(env, NULL, 0, txn);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
|
||||
bytes = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void copy_table(MDB_env *env0, MDB_env *env1, const char *table, unsigned int flags, unsigned int putflags, int (*cmp)(const MDB_val*, const MDB_val*)=0)
|
||||
{
|
||||
static void copy_table(
|
||||
MDB_env* env0,
|
||||
MDB_env* env1,
|
||||
const char* table,
|
||||
unsigned int flags,
|
||||
unsigned int putflags,
|
||||
int (*cmp)(const MDB_val*, const MDB_val*) = 0) {
|
||||
MDB_dbi dbi0, dbi1;
|
||||
MDB_txn *txn0, *txn1;
|
||||
MDB_cursor *cur0, *cur1;
|
||||
|
@ -156,69 +175,95 @@ static void copy_table(MDB_env *env0, MDB_env *env1, const char *table, unsigned
|
|||
log::info(logcat, "Copying {}", table);
|
||||
|
||||
OXEN_DEFER {
|
||||
if (tx_active1) mdb_txn_abort(txn1);
|
||||
if (tx_active0) mdb_txn_abort(txn0);
|
||||
if (tx_active1)
|
||||
mdb_txn_abort(txn1);
|
||||
if (tx_active0)
|
||||
mdb_txn_abort(txn0);
|
||||
};
|
||||
|
||||
dbr = mdb_txn_begin(env0, NULL, MDB_RDONLY, &txn0);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
|
||||
tx_active0 = true;
|
||||
dbr = mdb_txn_begin(env1, NULL, 0, &txn1);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
|
||||
tx_active1 = true;
|
||||
|
||||
dbr = mdb_dbi_open(txn0, table, flags, &dbi0);
|
||||
if (dbr) throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
if (cmp)
|
||||
((flags & MDB_DUPSORT) ? mdb_set_dupsort : mdb_set_compare)(txn0, dbi0, cmp);
|
||||
|
||||
dbr = mdb_dbi_open(txn1, table, flags, &dbi1);
|
||||
if (dbr) throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
if (cmp)
|
||||
((flags & MDB_DUPSORT) ? mdb_set_dupsort : mdb_set_compare)(txn1, dbi1, cmp);
|
||||
|
||||
dbr = mdb_txn_commit(txn1);
|
||||
if (dbr) throw std::runtime_error("Failed to commit txn: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to commit txn: " + std::string(mdb_strerror(dbr)));
|
||||
tx_active1 = false;
|
||||
MDB_stat stats;
|
||||
dbr = mdb_env_stat(env0, &stats);
|
||||
if (dbr) throw std::runtime_error("Failed to stat " + std::string(table) + " LMDB table: " + std::string(mdb_strerror(dbr)));
|
||||
check_resize(env1, (stats.ms_branch_pages + stats.ms_overflow_pages + stats.ms_leaf_pages) * stats.ms_psize);
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to stat " + std::string(table) +
|
||||
" LMDB table: " + std::string(mdb_strerror(dbr)));
|
||||
check_resize(
|
||||
env1,
|
||||
(stats.ms_branch_pages + stats.ms_overflow_pages + stats.ms_leaf_pages) *
|
||||
stats.ms_psize);
|
||||
dbr = mdb_txn_begin(env1, NULL, 0, &txn1);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
|
||||
tx_active1 = true;
|
||||
|
||||
dbr = mdb_drop(txn1, dbi1, 0);
|
||||
if (dbr) throw std::runtime_error("Failed to empty " + std::string(table) + " LMDB table: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to empty " + std::string(table) +
|
||||
" LMDB table: " + std::string(mdb_strerror(dbr)));
|
||||
|
||||
dbr = mdb_cursor_open(txn0, dbi0, &cur0);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
dbr = mdb_cursor_open(txn1, dbi1, &cur1);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
|
||||
MDB_val k;
|
||||
MDB_val v;
|
||||
MDB_cursor_op op = MDB_FIRST;
|
||||
size_t nrecords = 0, bytes = 0;
|
||||
while (1)
|
||||
{
|
||||
while (1) {
|
||||
int ret = mdb_cursor_get(cur0, &k, &v, op);
|
||||
op = MDB_NEXT;
|
||||
if (ret == MDB_NOTFOUND)
|
||||
break;
|
||||
if (ret)
|
||||
throw std::runtime_error("Failed to enumerate " + std::string(table) + " records: " + std::string(mdb_strerror(ret)));
|
||||
throw std::runtime_error(
|
||||
"Failed to enumerate " + std::string(table) +
|
||||
" records: " + std::string(mdb_strerror(ret)));
|
||||
|
||||
bytes += k.mv_size + v.mv_size;
|
||||
if (resize_point(++nrecords, env1, &txn1, bytes))
|
||||
{
|
||||
if (resize_point(++nrecords, env1, &txn1, bytes)) {
|
||||
dbr = mdb_cursor_open(txn1, dbi1, &cur1);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
}
|
||||
|
||||
ret = mdb_cursor_put(cur1, &k, &v, putflags);
|
||||
if (ret)
|
||||
throw std::runtime_error("Failed to write " + std::string(table) + " record: " + std::string(mdb_strerror(ret)));
|
||||
throw std::runtime_error(
|
||||
"Failed to write " + std::string(table) +
|
||||
" record: " + std::string(mdb_strerror(ret)));
|
||||
}
|
||||
|
||||
mdb_cursor_close(cur1);
|
||||
|
@ -231,134 +276,171 @@ static void copy_table(MDB_env *env0, MDB_env *env1, const char *table, unsigned
|
|||
mdb_dbi_close(env0, dbi0);
|
||||
}
|
||||
|
||||
static bool is_v1_tx(MDB_cursor *c_txs_pruned, MDB_val *tx_id)
|
||||
{
|
||||
static bool is_v1_tx(MDB_cursor* c_txs_pruned, MDB_val* tx_id) {
|
||||
MDB_val v;
|
||||
int ret = mdb_cursor_get(c_txs_pruned, tx_id, &v, MDB_SET);
|
||||
if (ret)
|
||||
throw std::runtime_error("Failed to find transaction pruned data: " + std::string(mdb_strerror(ret)));
|
||||
throw std::runtime_error(
|
||||
"Failed to find transaction pruned data: " + std::string(mdb_strerror(ret)));
|
||||
if (v.mv_size == 0)
|
||||
throw std::runtime_error("Invalid transaction pruned data");
|
||||
return cryptonote::is_v1_tx(std::string_view{(const char*)v.mv_data, v.mv_size});
|
||||
}
|
||||
|
||||
static void prune(MDB_env *env0, MDB_env *env1)
|
||||
{
|
||||
MDB_dbi dbi0_blocks, dbi0_txs_pruned, dbi0_txs_prunable, dbi0_tx_indices, dbi1_txs_prunable, dbi1_txs_prunable_tip, dbi1_properties;
|
||||
static void prune(MDB_env* env0, MDB_env* env1) {
|
||||
MDB_dbi dbi0_blocks, dbi0_txs_pruned, dbi0_txs_prunable, dbi0_tx_indices, dbi1_txs_prunable,
|
||||
dbi1_txs_prunable_tip, dbi1_properties;
|
||||
MDB_txn *txn0, *txn1;
|
||||
MDB_cursor *cur0_txs_pruned, *cur0_txs_prunable, *cur0_tx_indices, *cur1_txs_prunable, *cur1_txs_prunable_tip;
|
||||
MDB_cursor *cur0_txs_pruned, *cur0_txs_prunable, *cur0_tx_indices, *cur1_txs_prunable,
|
||||
*cur1_txs_prunable_tip;
|
||||
bool tx_active0 = false, tx_active1 = false;
|
||||
int dbr;
|
||||
|
||||
log::info(logcat, "Creating pruned txs_prunable");
|
||||
|
||||
OXEN_DEFER {
|
||||
if (tx_active1) mdb_txn_abort(txn1);
|
||||
if (tx_active0) mdb_txn_abort(txn0);
|
||||
if (tx_active1)
|
||||
mdb_txn_abort(txn1);
|
||||
if (tx_active0)
|
||||
mdb_txn_abort(txn0);
|
||||
};
|
||||
|
||||
dbr = mdb_txn_begin(env0, NULL, MDB_RDONLY, &txn0);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
|
||||
tx_active0 = true;
|
||||
dbr = mdb_txn_begin(env1, NULL, 0, &txn1);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
|
||||
tx_active1 = true;
|
||||
|
||||
dbr = mdb_dbi_open(txn0, "txs_pruned", MDB_INTEGERKEY, &dbi0_txs_pruned);
|
||||
if (dbr) throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
mdb_set_compare(txn0, dbi0_txs_pruned, BlockchainLMDB::compare_uint64);
|
||||
dbr = mdb_cursor_open(txn0, dbi0_txs_pruned, &cur0_txs_pruned);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
|
||||
dbr = mdb_dbi_open(txn0, "txs_prunable", MDB_INTEGERKEY, &dbi0_txs_prunable);
|
||||
if (dbr) throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
mdb_set_compare(txn0, dbi0_txs_prunable, BlockchainLMDB::compare_uint64);
|
||||
dbr = mdb_cursor_open(txn0, dbi0_txs_prunable, &cur0_txs_prunable);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
|
||||
dbr = mdb_dbi_open(txn0, "tx_indices", MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi0_tx_indices);
|
||||
if (dbr) throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
dbr = mdb_dbi_open(
|
||||
txn0, "tx_indices", MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi0_tx_indices);
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
mdb_set_dupsort(txn0, dbi0_tx_indices, BlockchainLMDB::compare_hash32);
|
||||
dbr = mdb_cursor_open(txn0, dbi0_tx_indices, &cur0_tx_indices);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
|
||||
dbr = mdb_dbi_open(txn1, "txs_prunable", MDB_INTEGERKEY, &dbi1_txs_prunable);
|
||||
if (dbr) throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
mdb_set_compare(txn1, dbi1_txs_prunable, BlockchainLMDB::compare_uint64);
|
||||
dbr = mdb_cursor_open(txn1, dbi1_txs_prunable, &cur1_txs_prunable);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
|
||||
dbr = mdb_dbi_open(txn1, "txs_prunable_tip", MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi1_txs_prunable_tip);
|
||||
if (dbr) throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
dbr = mdb_dbi_open(
|
||||
txn1,
|
||||
"txs_prunable_tip",
|
||||
MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED,
|
||||
&dbi1_txs_prunable_tip);
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
mdb_set_dupsort(txn1, dbi1_txs_prunable_tip, BlockchainLMDB::compare_uint64);
|
||||
dbr = mdb_cursor_open(txn1, dbi1_txs_prunable_tip, &cur1_txs_prunable_tip);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
|
||||
dbr = mdb_drop(txn1, dbi1_txs_prunable, 0);
|
||||
if (dbr) throw std::runtime_error("Failed to empty LMDB table: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to empty LMDB table: " + std::string(mdb_strerror(dbr)));
|
||||
dbr = mdb_drop(txn1, dbi1_txs_prunable_tip, 0);
|
||||
if (dbr) throw std::runtime_error("Failed to empty LMDB table: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to empty LMDB table: " + std::string(mdb_strerror(dbr)));
|
||||
|
||||
dbr = mdb_dbi_open(txn1, "properties", 0, &dbi1_properties);
|
||||
if (dbr) throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
|
||||
MDB_val k, v;
|
||||
uint32_t pruning_seed = tools::make_pruning_seed(tools::get_random_stripe(), PRUNING_LOG_STRIPES);
|
||||
uint32_t pruning_seed =
|
||||
tools::make_pruning_seed(tools::get_random_stripe(), PRUNING_LOG_STRIPES);
|
||||
static char pruning_seed_key[] = "pruning_seed";
|
||||
k.mv_data = pruning_seed_key;
|
||||
k.mv_size = strlen("pruning_seed") + 1;
|
||||
v.mv_data = (void*)&pruning_seed;
|
||||
v.mv_size = sizeof(pruning_seed);
|
||||
dbr = mdb_put(txn1, dbi1_properties, &k, &v, 0);
|
||||
if (dbr) throw std::runtime_error("Failed to save pruning seed: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to save pruning seed: " + std::string(mdb_strerror(dbr)));
|
||||
|
||||
MDB_stat stats;
|
||||
dbr = mdb_dbi_open(txn0, "blocks", 0, &dbi0_blocks);
|
||||
if (dbr) throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error("Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
|
||||
dbr = mdb_stat(txn0, dbi0_blocks, &stats);
|
||||
if (dbr) throw std::runtime_error("Failed to query size of blocks: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to query size of blocks: " + std::string(mdb_strerror(dbr)));
|
||||
mdb_dbi_close(env0, dbi0_blocks);
|
||||
const uint64_t blockchain_height = stats.ms_entries;
|
||||
size_t nrecords = 0, bytes = 0;
|
||||
|
||||
MDB_cursor_op op = MDB_FIRST;
|
||||
while (1)
|
||||
{
|
||||
while (1) {
|
||||
int ret = mdb_cursor_get(cur0_tx_indices, &k, &v, op);
|
||||
op = MDB_NEXT;
|
||||
if (ret == MDB_NOTFOUND)
|
||||
break;
|
||||
if (ret) throw std::runtime_error("Failed to enumerate records: " + std::string(mdb_strerror(ret)));
|
||||
if (ret)
|
||||
throw std::runtime_error(
|
||||
"Failed to enumerate records: " + std::string(mdb_strerror(ret)));
|
||||
|
||||
const txindex *ti = (const txindex*)v.mv_data;
|
||||
const txindex* ti = (const txindex*)v.mv_data;
|
||||
const uint64_t block_height = ti->data.block_id;
|
||||
MDB_val_set(kk, ti->data.tx_id);
|
||||
if (block_height + PRUNING_TIP_BLOCKS >= blockchain_height)
|
||||
{
|
||||
if (block_height + PRUNING_TIP_BLOCKS >= blockchain_height) {
|
||||
log::debug(logcat, "{}/{} is in tip", block_height, blockchain_height);
|
||||
MDB_val_set(vv, block_height);
|
||||
dbr = mdb_cursor_put(cur1_txs_prunable_tip, &kk, &vv, 0);
|
||||
if (dbr) throw std::runtime_error("Failed to write prunable tx tip data: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to write prunable tx tip data: " + std::string(mdb_strerror(dbr)));
|
||||
bytes += kk.mv_size + vv.mv_size;
|
||||
}
|
||||
if (tools::has_unpruned_block(block_height, blockchain_height, pruning_seed) || is_v1_tx(cur0_txs_pruned, &kk))
|
||||
{
|
||||
if (tools::has_unpruned_block(block_height, blockchain_height, pruning_seed) ||
|
||||
is_v1_tx(cur0_txs_pruned, &kk)) {
|
||||
MDB_val vv;
|
||||
dbr = mdb_cursor_get(cur0_txs_prunable, &kk, &vv, MDB_SET);
|
||||
if (dbr) throw std::runtime_error("Failed to read prunable tx data: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to read prunable tx data: " + std::string(mdb_strerror(dbr)));
|
||||
bytes += kk.mv_size + vv.mv_size;
|
||||
if (resize_point(++nrecords, env1, &txn1, bytes))
|
||||
{
|
||||
if (resize_point(++nrecords, env1, &txn1, bytes)) {
|
||||
dbr = mdb_cursor_open(txn1, dbi1_txs_prunable, &cur1_txs_prunable);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
dbr = mdb_cursor_open(txn1, dbi1_txs_prunable_tip, &cur1_txs_prunable_tip);
|
||||
if (dbr) throw std::runtime_error("Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to create LMDB cursor: " + std::string(mdb_strerror(dbr)));
|
||||
}
|
||||
dbr = mdb_cursor_put(cur1_txs_prunable, &kk, &vv, 0);
|
||||
if (dbr) throw std::runtime_error("Failed to write prunable tx data: " + std::string(mdb_strerror(dbr)));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dbr)
|
||||
throw std::runtime_error(
|
||||
"Failed to write prunable tx data: " + std::string(mdb_strerror(dbr)));
|
||||
} else {
|
||||
log::debug(logcat, "{}/{} should be pruned, dropping", block_height, blockchain_height);
|
||||
}
|
||||
}
|
||||
|
@ -380,11 +462,10 @@ static void prune(MDB_env *env0, MDB_env *env1)
|
|||
mdb_dbi_close(env0, dbi0_tx_indices);
|
||||
}
|
||||
|
||||
static bool parse_db_sync_mode(std::string db_sync_mode, uint64_t &db_flags)
|
||||
{
|
||||
static bool parse_db_sync_mode(std::string db_sync_mode, uint64_t& db_flags) {
|
||||
auto options = tools::split_any(db_sync_mode, " :", true);
|
||||
|
||||
for(const auto &option : options)
|
||||
for (const auto& option : options)
|
||||
log::debug(logcat, "option: {}", option);
|
||||
|
||||
// default to fast:async:1
|
||||
|
@ -392,36 +473,27 @@ static bool parse_db_sync_mode(std::string db_sync_mode, uint64_t &db_flags)
|
|||
|
||||
db_flags = 0;
|
||||
|
||||
if(options.size() == 0)
|
||||
{
|
||||
if (options.size() == 0) {
|
||||
// default to fast:async:1
|
||||
db_flags = DEFAULT_FLAGS;
|
||||
}
|
||||
|
||||
bool safemode = false;
|
||||
if(options.size() >= 1)
|
||||
{
|
||||
if(options[0] == "safe")
|
||||
{
|
||||
if (options.size() >= 1) {
|
||||
if (options[0] == "safe") {
|
||||
safemode = true;
|
||||
db_flags = DBF_SAFE;
|
||||
}
|
||||
else if(options[0] == "fast")
|
||||
{
|
||||
} else if (options[0] == "fast") {
|
||||
db_flags = DBF_FAST;
|
||||
}
|
||||
else if(options[0] == "fastest")
|
||||
{
|
||||
} else if (options[0] == "fastest") {
|
||||
db_flags = DBF_FASTEST;
|
||||
records_per_sync = 1000; // default to fastest:async:1000
|
||||
}
|
||||
else
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
if(options.size() >= 2 && !safemode)
|
||||
{
|
||||
char *endptr;
|
||||
if (options.size() >= 2 && !safemode) {
|
||||
char* endptr;
|
||||
std::string bpsstr{options[1]};
|
||||
uint64_t bps = strtoull(bpsstr.c_str(), &endptr, 0);
|
||||
if (*endptr != '\0')
|
||||
|
@ -432,8 +504,7 @@ static bool parse_db_sync_mode(std::string db_sync_mode, uint64_t &db_flags)
|
|||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int main(int argc, char* argv[]) {
|
||||
TRY_ENTRY();
|
||||
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
|
@ -445,14 +516,16 @@ int main(int argc, char* argv[])
|
|||
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", ""};
|
||||
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_db_sync_mode = {
|
||||
"db-sync-mode"
|
||||
, "Specify sync option, using format [safe|fast|fastest]:[nrecords_per_sync]."
|
||||
, "fast:1000"
|
||||
};
|
||||
const command_line::arg_descriptor<bool> arg_copy_pruned_database = {"copy-pruned-database", "Copy database anyway if already pruned"};
|
||||
"db-sync-mode",
|
||||
"Specify sync option, using format [safe|fast|fastest]:[nrecords_per_sync].",
|
||||
"fast:1000"};
|
||||
const command_line::arg_descriptor<bool> arg_copy_pruned_database = {
|
||||
"copy-pruned-database", "Copy database anyway if already pruned"};
|
||||
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_data_dir);
|
||||
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
|
||||
|
@ -466,18 +539,16 @@ int main(int argc, char* argv[])
|
|||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
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)
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
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;
|
||||
|
@ -493,7 +564,9 @@ int main(int argc, char* argv[])
|
|||
|
||||
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;
|
||||
network_type net_type = opt_testnet ? network_type::TESTNET
|
||||
: opt_devnet ? network_type::DEVNET
|
||||
: network_type::MAINNET;
|
||||
bool opt_copy_pruned_database = command_line::get_arg(vm, arg_copy_pruned_database);
|
||||
std::string data_dir = command_line::get_arg(vm, cryptonote::arg_data_dir);
|
||||
while (tools::ends_with(data_dir, "/") || tools::ends_with(data_dir, "\\"))
|
||||
|
@ -501,8 +574,7 @@ int main(int argc, char* argv[])
|
|||
|
||||
std::string db_sync_mode = command_line::get_arg(vm, arg_db_sync_mode);
|
||||
uint64_t db_flags = 0;
|
||||
if (!parse_db_sync_mode(db_sync_mode, db_flags))
|
||||
{
|
||||
if (!parse_db_sync_mode(db_sync_mode, db_flags)) {
|
||||
log::error(logcat, "Invalid db sync mode: {}", db_sync_mode);
|
||||
return 1;
|
||||
}
|
||||
|
@ -511,7 +583,8 @@ int main(int argc, char* argv[])
|
|||
|
||||
// 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()
|
||||
// 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.
|
||||
|
@ -519,55 +592,45 @@ int main(int argc, char* argv[])
|
|||
// because unlike blockchain_storage constructor, which takes a pointer to
|
||||
// tx_memory_pool, Blockchain's constructor takes tx_memory_pool object.
|
||||
log::info(logcat, "Initializing source blockchain (BlockchainDB)");
|
||||
std::array<Blockchain *, 2> core_storage;
|
||||
std::array<Blockchain*, 2> core_storage;
|
||||
fs::path paths[2];
|
||||
bool already_pruned = false;
|
||||
for (size_t n = 0; n < core_storage.size(); ++n)
|
||||
{
|
||||
blockchain_objects_t *blockchain_objects = new blockchain_objects_t();
|
||||
for (size_t n = 0; n < core_storage.size(); ++n) {
|
||||
blockchain_objects_t* blockchain_objects = new blockchain_objects_t();
|
||||
core_storage[n] = &(blockchain_objects->m_blockchain);
|
||||
|
||||
BlockchainDB* db = new_db();
|
||||
if (db == NULL)
|
||||
{
|
||||
if (db == NULL) {
|
||||
log::error(logcat, "Failed to initialize a database");
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
|
||||
if (n == 1)
|
||||
{
|
||||
if (n == 1) {
|
||||
paths[1] = fs::u8path(data_dir) / (db->get_db_name() + "-pruned");
|
||||
if (fs::exists(paths[1]))
|
||||
{
|
||||
if (!fs::is_directory(paths[1]))
|
||||
{
|
||||
log::error(logcat, "LMDB needs a directory path, but a file was passed: {}", paths[1].string());
|
||||
if (fs::exists(paths[1])) {
|
||||
if (!fs::is_directory(paths[1])) {
|
||||
log::error(
|
||||
logcat,
|
||||
"LMDB needs a directory path, but a file was passed: {}",
|
||||
paths[1].string());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!fs::create_directories(paths[1]))
|
||||
{
|
||||
} else {
|
||||
if (!fs::create_directories(paths[1])) {
|
||||
log::error(logcat, "Failed to create directory: {}", paths[1].string());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
db_path = paths[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
paths[0] = fs::u8path(data_dir) / db->get_db_name();
|
||||
}
|
||||
|
||||
log::info(logcat, "Loading blockchain from folder {} ...", paths[n]);
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
db->open(paths[n].string(), core_storage[n]->nettype(), n == 0 ? DBF_RDONLY : 0);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
} catch (const std::exception& e) {
|
||||
log::error(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
|
@ -576,11 +639,12 @@ int main(int argc, char* argv[])
|
|||
std::string source_dest = n == 0 ? "source" : "pruned";
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize " << source_dest << " blockchain storage");
|
||||
log::info(logcat, "{} blockchain storage initialized OK", source_dest);
|
||||
if (n == 0 && core_storage[0]->get_blockchain_pruning_seed())
|
||||
{
|
||||
if (!opt_copy_pruned_database)
|
||||
{
|
||||
log::error(logcat, "Blockchain is already pruned, use --{} to copy it anyway", arg_copy_pruned_database.name);
|
||||
if (n == 0 && core_storage[0]->get_blockchain_pruning_seed()) {
|
||||
if (!opt_copy_pruned_database) {
|
||||
log::error(
|
||||
logcat,
|
||||
"Blockchain is already pruned, use --{} to copy it anyway",
|
||||
arg_copy_pruned_database.name);
|
||||
return 1;
|
||||
}
|
||||
already_pruned = true;
|
||||
|
@ -596,38 +660,90 @@ int main(int argc, char* argv[])
|
|||
open(env0, paths[0], db_flags, true);
|
||||
open(env1, paths[1], db_flags, false);
|
||||
copy_table(env0, env1, "blocks", MDB_INTEGERKEY, MDB_APPEND);
|
||||
copy_table(env0, env1, "block_info", MDB_INTEGERKEY | MDB_DUPSORT| MDB_DUPFIXED, MDB_APPENDDUP, BlockchainLMDB::compare_uint64);
|
||||
copy_table(env0, env1, "block_heights", MDB_INTEGERKEY | MDB_DUPSORT| MDB_DUPFIXED, 0, BlockchainLMDB::compare_hash32);
|
||||
//copy_table(env0, env1, "txs", MDB_INTEGERKEY);
|
||||
copy_table(
|
||||
env0,
|
||||
env1,
|
||||
"block_info",
|
||||
MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED,
|
||||
MDB_APPENDDUP,
|
||||
BlockchainLMDB::compare_uint64);
|
||||
copy_table(
|
||||
env0,
|
||||
env1,
|
||||
"block_heights",
|
||||
MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED,
|
||||
0,
|
||||
BlockchainLMDB::compare_hash32);
|
||||
// copy_table(env0, env1, "txs", MDB_INTEGERKEY);
|
||||
copy_table(env0, env1, "txs_pruned", MDB_INTEGERKEY, MDB_APPEND);
|
||||
copy_table(env0, env1, "txs_prunable_hash", MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, MDB_APPEND);
|
||||
copy_table(
|
||||
env0,
|
||||
env1,
|
||||
"txs_prunable_hash",
|
||||
MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED,
|
||||
MDB_APPEND);
|
||||
// not copied: prunable, prunable_tip
|
||||
copy_table(env0, env1, "tx_indices", MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, 0, BlockchainLMDB::compare_hash32);
|
||||
copy_table(
|
||||
env0,
|
||||
env1,
|
||||
"tx_indices",
|
||||
MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED,
|
||||
0,
|
||||
BlockchainLMDB::compare_hash32);
|
||||
copy_table(env0, env1, "tx_outputs", MDB_INTEGERKEY, MDB_APPEND);
|
||||
copy_table(env0, env1, "output_txs", MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, MDB_APPENDDUP, BlockchainLMDB::compare_uint64);
|
||||
copy_table(env0, env1, "output_amounts", MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, MDB_APPENDDUP, BlockchainLMDB::compare_uint64);
|
||||
copy_table(env0, env1, "spent_keys", MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, MDB_NODUPDATA, BlockchainLMDB::compare_hash32);
|
||||
copy_table(
|
||||
env0,
|
||||
env1,
|
||||
"output_txs",
|
||||
MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED,
|
||||
MDB_APPENDDUP,
|
||||
BlockchainLMDB::compare_uint64);
|
||||
copy_table(
|
||||
env0,
|
||||
env1,
|
||||
"output_amounts",
|
||||
MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED,
|
||||
MDB_APPENDDUP,
|
||||
BlockchainLMDB::compare_uint64);
|
||||
copy_table(
|
||||
env0,
|
||||
env1,
|
||||
"spent_keys",
|
||||
MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED,
|
||||
MDB_NODUPDATA,
|
||||
BlockchainLMDB::compare_hash32);
|
||||
copy_table(env0, env1, "txpool_meta", 0, MDB_NODUPDATA, BlockchainLMDB::compare_hash32);
|
||||
copy_table(env0, env1, "txpool_blob", 0, MDB_NODUPDATA, BlockchainLMDB::compare_hash32);
|
||||
copy_table(env0, env1, "hf_versions", MDB_INTEGERKEY, MDB_APPEND);
|
||||
copy_table(env0, env1, "properties", 0, 0, BlockchainLMDB::compare_string);
|
||||
if (already_pruned)
|
||||
{
|
||||
copy_table(env0, env1, "txs_prunable", MDB_INTEGERKEY, MDB_APPEND, BlockchainLMDB::compare_uint64);
|
||||
copy_table(env0, env1, "txs_prunable_tip", MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, MDB_NODUPDATA, BlockchainLMDB::compare_uint64);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (already_pruned) {
|
||||
copy_table(
|
||||
env0,
|
||||
env1,
|
||||
"txs_prunable",
|
||||
MDB_INTEGERKEY,
|
||||
MDB_APPEND,
|
||||
BlockchainLMDB::compare_uint64);
|
||||
copy_table(
|
||||
env0,
|
||||
env1,
|
||||
"txs_prunable_tip",
|
||||
MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED,
|
||||
MDB_NODUPDATA,
|
||||
BlockchainLMDB::compare_uint64);
|
||||
} else {
|
||||
prune(env0, env1);
|
||||
}
|
||||
close(env1);
|
||||
close(env0);
|
||||
|
||||
log::info(logcat, "Swapping databases, pre-pruning blockchain will be left in {}", paths[0].string() + "-old and can be removed if desired");
|
||||
log::info(
|
||||
logcat,
|
||||
"Swapping databases, pre-pruning blockchain will be left in {}",
|
||||
paths[0].string() + "-old and can be removed if desired");
|
||||
fs::path old = paths[0];
|
||||
old += "-old";
|
||||
if (replace_file(paths[0], old) || replace_file(paths[1], paths[0]))
|
||||
{
|
||||
if (replace_file(paths[0], old) || replace_file(paths[1], paths[0])) {
|
||||
log::error(logcat, "Blockchain pruned OK, but renaming failed");
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -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,25 +42,22 @@ using namespace cryptonote;
|
|||
|
||||
static auto logcat = log::Cat("bcutil");
|
||||
|
||||
static std::map<uint64_t, uint64_t> load_outputs(const fs::path& filename)
|
||||
{
|
||||
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");
|
||||
#else
|
||||
fopen(filename.c_str(), "r");
|
||||
#endif
|
||||
|
||||
if (!f)
|
||||
{
|
||||
if (!f) {
|
||||
log::error(logcat, "Failed to load outputs from {}: {}", filename, strerror(errno));
|
||||
return {};
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
while (1) {
|
||||
char s[256];
|
||||
if (!fgets(s, sizeof(s), f))
|
||||
break;
|
||||
|
@ -73,25 +70,19 @@ static std::map<uint64_t, uint64_t> load_outputs(const fs::path& filename)
|
|||
continue;
|
||||
std::pair<uint64_t, uint64_t> output;
|
||||
uint64_t offset, num_offsets;
|
||||
if (sscanf(s, "@%" PRIu64, &amount) == 1)
|
||||
{
|
||||
if (sscanf(s, "@%" PRIu64, &amount) == 1) {
|
||||
continue;
|
||||
}
|
||||
if (amount == std::numeric_limits<uint64_t>::max())
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
} else if (sscanf(s, "%" PRIu64, &offset) == 1) {
|
||||
outputs[amount] += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
log::error(logcat, "Bad format in {}", filename);
|
||||
continue;
|
||||
}
|
||||
|
@ -100,8 +91,7 @@ static std::map<uint64_t, uint64_t> load_outputs(const fs::path& filename)
|
|||
return outputs;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int main(int argc, char* argv[]) {
|
||||
TRY_ENTRY();
|
||||
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
|
@ -113,11 +103,15 @@ int main(int argc, char* argv[])
|
|||
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", ""};
|
||||
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"};
|
||||
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);
|
||||
|
@ -132,18 +126,16 @@ int main(int argc, char* argv[])
|
|||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
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)
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
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;
|
||||
|
@ -159,7 +151,9 @@ int main(int argc, char* argv[])
|
|||
|
||||
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;
|
||||
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);
|
||||
|
||||
|
@ -167,23 +161,20 @@ int main(int argc, char* argv[])
|
|||
|
||||
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)
|
||||
{
|
||||
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();
|
||||
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
|
||||
{
|
||||
try {
|
||||
db->open(filename, core_storage->nettype(), 0);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
} catch (const std::exception& e) {
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
|
@ -193,19 +184,20 @@ int main(int argc, char* argv[])
|
|||
log::warning(logcat, "Source blockchain storage initialized OK");
|
||||
|
||||
std::map<uint64_t, uint64_t> known_spent_outputs;
|
||||
if (input.empty())
|
||||
{
|
||||
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)
|
||||
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)
|
||||
{
|
||||
for (const auto& out : tx.vout) {
|
||||
uint64_t amount = out.amount;
|
||||
if (miner_tx && tx.version >= txversion::v2_ringct)
|
||||
amount = 0;
|
||||
|
@ -217,15 +209,13 @@ int main(int argc, char* argv[])
|
|||
outputs[amount].first++;
|
||||
}
|
||||
return true;
|
||||
}, true);
|
||||
},
|
||||
true);
|
||||
|
||||
for (const auto &i: outputs)
|
||||
{
|
||||
for (const auto& i : outputs) {
|
||||
known_spent_outputs[i.first] = i.second.second;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
log::warning(logcat, "Loading known spent data...");
|
||||
known_spent_outputs = load_outputs(input);
|
||||
}
|
||||
|
@ -233,22 +223,20 @@ int main(int argc, char* argv[])
|
|||
log::warning(logcat, "Pruning known spent data...");
|
||||
|
||||
bool stop_requested = false;
|
||||
tools::signal_handler::install([&stop_requested](int type) {
|
||||
stop_requested = true;
|
||||
});
|
||||
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)
|
||||
{
|
||||
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 (i->first == 0) {
|
||||
if (opt_verbose)
|
||||
log::info(logcat, "Ignoring output value {}, with {} outputs", i->first, num_outputs);
|
||||
log::info(
|
||||
logcat, "Ignoring output value {}, with {} outputs", i->first, num_outputs);
|
||||
continue;
|
||||
}
|
||||
num_eligible_outputs += num_outputs;
|
||||
|
@ -257,9 +245,11 @@ int main(int argc, char* argv[])
|
|||
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);
|
||||
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)
|
||||
|
|
|
@ -26,27 +26,28 @@
|
|||
// 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[])
|
||||
{
|
||||
int main(int argc, char* argv[]) {
|
||||
static auto logcat = log::Cat("bcutil");
|
||||
|
||||
TRY_ENTRY();
|
||||
|
@ -59,14 +60,22 @@ int main(int argc, char* argv[])
|
|||
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_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);
|
||||
|
@ -84,18 +93,16 @@ int main(int argc, char* argv[])
|
|||
desc_options.add(desc_cmd_only).add(desc_cmd_sett);
|
||||
|
||||
po::variables_map vm;
|
||||
bool r = command_line::handle_error_helper(desc_options, [&]()
|
||||
{
|
||||
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)
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
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;
|
||||
|
@ -104,10 +111,11 @@ int main(int argc, char* argv[])
|
|||
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())) {
|
||||
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;
|
||||
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);
|
||||
|
@ -116,7 +124,9 @@ int main(int argc, char* argv[])
|
|||
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;
|
||||
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);
|
||||
|
@ -126,10 +136,9 @@ int main(int argc, char* argv[])
|
|||
|
||||
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)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
@ -137,12 +146,9 @@ int main(int argc, char* argv[])
|
|||
const fs::path filename = fs::u8path(opt_data_dir) / db->get_db_name();
|
||||
log::warning(logcat, "Loading blockchain from folder {} ...", filename);
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
db->open(filename, core_storage->nettype(), DBF_RDONLY);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
} catch (const std::exception& e) {
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
|
@ -151,29 +157,28 @@ int main(int argc, char* argv[])
|
|||
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;
|
||||
});
|
||||
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
|
||||
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
|
||||
|
@ -188,7 +193,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
|
|||
if (do_hours) {
|
||||
char buf[8];
|
||||
unsigned int i;
|
||||
for (i=0; i<24; i++) {
|
||||
for (i = 0; i < 24; i++) {
|
||||
sprintf(buf, "\t%02u:00", i);
|
||||
std::cout << buf;
|
||||
}
|
||||
|
@ -207,12 +212,10 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
|
|||
uint32_t txhr[24] = {0};
|
||||
unsigned int i;
|
||||
|
||||
for (uint64_t h = block_start; h < block_stop; ++h)
|
||||
{
|
||||
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))
|
||||
{
|
||||
if (!cryptonote::parse_and_validate_block_from_blob(bd, blk)) {
|
||||
log::warning(logcat, "Bad block from db");
|
||||
return 1;
|
||||
}
|
||||
|
@ -223,13 +226,15 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
|
|||
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}))
|
||||
{
|
||||
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;
|
||||
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;
|
||||
|
@ -238,41 +243,46 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
|
|||
if (!tottxs)
|
||||
tottxs = 1;
|
||||
if (do_inputs) {
|
||||
std::cout << "\t" << (maxins ? minins : 0) << "\t" << maxins << "\t" << totins / tottxs;
|
||||
minins = 10; maxins = 0; totins = 0;
|
||||
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;
|
||||
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;
|
||||
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++) {
|
||||
for (i = 0; i < 24; i++) {
|
||||
std::cout << "\t" << txhr[i];
|
||||
txhr[i] = 0;
|
||||
}
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
skip:
|
||||
skip:
|
||||
currsz += bd.size();
|
||||
for (const auto& tx_id : blk.tx_hashes)
|
||||
{
|
||||
if (!tx_id)
|
||||
{
|
||||
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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
if (!parse_and_validate_tx_base_from_blob(bd, tx)) {
|
||||
log::warning(logcat, "Bad txn from db");
|
||||
return 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,40 +41,41 @@ using namespace cryptonote;
|
|||
|
||||
static auto logcat = log::Cat("quorum_cop");
|
||||
|
||||
struct output_data
|
||||
{
|
||||
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; }
|
||||
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
|
||||
{
|
||||
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);
|
||||
return reinterpret_cast<const std::size_t&>(h);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
struct reference
|
||||
{
|
||||
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) {}
|
||||
reference(uint64_t h, uint64_t rs, uint64_t p) : height(h), ring_size(rs), position(p) {}
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int main(int argc, char* argv[]) {
|
||||
TRY_ENTRY();
|
||||
|
||||
epee::string_tools::set_module_name_and_folder(argv[0]);
|
||||
|
@ -82,9 +83,12 @@ int main(int argc, char* argv[])
|
|||
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};
|
||||
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);
|
||||
|
@ -101,18 +105,18 @@ int main(int argc, char* argv[])
|
|||
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);
|
||||
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)
|
||||
if (!r)
|
||||
return 1;
|
||||
|
||||
if (command_line::get_arg(vm, command_line::arg_help))
|
||||
{
|
||||
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;
|
||||
|
@ -121,10 +125,11 @@ int main(int argc, char* argv[])
|
|||
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())) {
|
||||
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;
|
||||
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);
|
||||
|
@ -132,14 +137,17 @@ int main(int argc, char* argv[])
|
|||
|
||||
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;
|
||||
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()
|
||||
// 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.
|
||||
|
@ -149,11 +157,10 @@ int main(int argc, char* argv[])
|
|||
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;
|
||||
Blockchain* core_storage = &blockchain_objects.m_blockchain;
|
||||
tx_memory_pool& m_mempool = blockchain_objects.m_mempool;
|
||||
BlockchainDB *db = new_db();
|
||||
if (db == NULL)
|
||||
{
|
||||
BlockchainDB* db = new_db();
|
||||
if (db == NULL) {
|
||||
log::error(logcat, "Failed to initialize a database");
|
||||
throw std::runtime_error("Failed to initialize a database");
|
||||
}
|
||||
|
@ -162,12 +169,9 @@ int main(int argc, char* argv[])
|
|||
const fs::path filename = fs::u8path(input);
|
||||
log::warning(logcat, "Loading blockchain from folder {} ...", filename);
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
db->open(filename, core_storage->nettype(), DBF_RDONLY);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
} catch (const std::exception& e) {
|
||||
log::warning(logcat, "Error opening database: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
|
@ -181,17 +185,17 @@ int main(int argc, char* argv[])
|
|||
|
||||
size_t done = 0;
|
||||
std::unordered_map<output_data, std::list<reference>> outputs;
|
||||
std::unordered_map<uint64_t,uint64_t> indices;
|
||||
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]);
|
||||
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)
|
||||
{
|
||||
for (const auto& out : tx.vout) {
|
||||
if (opt_rct_only && out.amount)
|
||||
continue;
|
||||
uint64_t index = indices[out.amount]++;
|
||||
|
@ -200,39 +204,39 @@ int main(int argc, char* argv[])
|
|||
itb.first->first.info(coinbase, height);
|
||||
}
|
||||
|
||||
for (const auto &in: tx.vin)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
},
|
||||
true);
|
||||
|
||||
std::unordered_map<uint64_t, uint64_t> counts;
|
||||
size_t total = 0;
|
||||
for (const auto &out: outputs)
|
||||
{
|
||||
for (const auto& out : outputs) {
|
||||
counts[out.second.size()]++;
|
||||
total++;
|
||||
}
|
||||
if (total > 0)
|
||||
{
|
||||
for (const auto &c: counts)
|
||||
{
|
||||
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);
|
||||
log::info(
|
||||
logcat,
|
||||
"{} outputs used {} times ({}%)",
|
||||
std::to_string(c.second),
|
||||
c.first,
|
||||
percent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
log::info(logcat, "No outputs to process");
|
||||
}
|
||||
|
||||
|
|
|
@ -30,11 +30,9 @@
|
|||
|
||||
#include "version.h"
|
||||
|
||||
|
||||
// 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"
|
||||
|
||||
|
|
|
@ -28,36 +28,27 @@
|
|||
// 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;
|
||||
|
||||
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)
|
||||
{
|
||||
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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
} else {
|
||||
if (!fs::create_directory(dir_path)) {
|
||||
log::error(logcat, "Failed to create directory {}", dir_path);
|
||||
return false;
|
||||
}
|
||||
|
@ -68,7 +59,8 @@ bool BlocksdatFile::open_writer(const fs::path& file_path, uint64_t block_stop)
|
|||
|
||||
log::info(logcat, "creating file");
|
||||
|
||||
m_raw_data_file->open(file_path.string(), std::ios_base::binary | std::ios_base::out | std::ios::trunc);
|
||||
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;
|
||||
|
||||
|
@ -77,9 +69,7 @@ bool BlocksdatFile::open_writer(const fs::path& file_path, uint64_t block_stop)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool BlocksdatFile::initialize_file(uint64_t block_stop)
|
||||
{
|
||||
bool BlocksdatFile::initialize_file(uint64_t block_stop) {
|
||||
const uint32_t nblocks = (block_stop + 1) / HASH_OF_HASHES_STEP;
|
||||
unsigned char nblocksc[4];
|
||||
|
||||
|
@ -97,21 +87,20 @@ bool BlocksdatFile::initialize_file(uint64_t block_stop)
|
|||
return true;
|
||||
}
|
||||
|
||||
void BlocksdatFile::write_block(const crypto::hash& block_hash)
|
||||
{
|
||||
void BlocksdatFile::write_block(const crypto::hash& block_hash) {
|
||||
m_hashes.push_back(block_hash);
|
||||
while (m_hashes.size() >= HASH_OF_HASHES_STEP)
|
||||
{
|
||||
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));
|
||||
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()
|
||||
{
|
||||
bool BlocksdatFile::close() {
|
||||
if (m_raw_data_file->fail())
|
||||
return false;
|
||||
|
||||
|
@ -120,9 +109,11 @@ bool BlocksdatFile::close()
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool BlocksdatFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_memory_pool* _tx_pool, fs::path& output_file, uint64_t requested_block_stop)
|
||||
{
|
||||
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;
|
||||
|
@ -130,25 +121,24 @@ bool BlocksdatFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_mem
|
|||
|
||||
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,
|
||||
"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
|
||||
{
|
||||
} 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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
@ -162,10 +152,9 @@ bool BlocksdatFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_mem
|
|||
}
|
||||
// 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";
|
||||
std::cout << "block " << m_cur_height - 1 << "/" << block_stop << "\n";
|
||||
|
||||
log::info(logcat, "Number of blocks exported: {}", num_blocks_written);
|
||||
|
||||
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:
|
||||
|
||||
bool store_blockchain_raw(cryptonote::Blockchain* cs, cryptonote::tx_memory_pool* txp,
|
||||
fs::path& output_file, uint64_t use_block_height=0);
|
||||
|
||||
protected:
|
||||
|
||||
protected:
|
||||
Blockchain* m_blockchain_storage;
|
||||
|
||||
std::ofstream * m_raw_data_file;
|
||||
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:
|
||||
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;
|
||||
};
|
||||
|
|
|
@ -27,45 +27,34 @@
|
|||
// 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;
|
||||
|
||||
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)
|
||||
{
|
||||
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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
} else {
|
||||
if (!fs::create_directory(dir_path)) {
|
||||
log::error(logcat, "Failed to create directory {}", dir_path);
|
||||
return false;
|
||||
}
|
||||
|
@ -77,28 +66,34 @@ bool BootstrapFile::open_writer(const fs::path& file_path)
|
|||
bool do_initialize_file = false;
|
||||
uint64_t num_blocks = 0;
|
||||
|
||||
if (! fs::exists(file_path))
|
||||
{
|
||||
if (!fs::exists(file_path)) {
|
||||
log::debug(logcat, "creating file");
|
||||
do_initialize_file = true;
|
||||
num_blocks = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
num_blocks = count_blocks(file_path.string());
|
||||
log::debug(logcat, "appending to existing file with height: {} total blocks: {}", num_blocks-1, num_blocks);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
m_output_stream =
|
||||
new boost::iostreams::stream<boost::iostreams::back_insert_device<buffer_type>>(
|
||||
m_buffer);
|
||||
if (m_output_stream == nullptr)
|
||||
return false;
|
||||
|
||||
|
@ -108,9 +103,7 @@ bool BootstrapFile::open_writer(const fs::path& file_path)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool BootstrapFile::initialize_file()
|
||||
{
|
||||
bool BootstrapFile::initialize_file() {
|
||||
const uint32_t file_magic = blockchain_raw_magic;
|
||||
|
||||
std::string blob;
|
||||
|
@ -132,7 +125,8 @@ bool BootstrapFile::initialize_file()
|
|||
bbi.block_last_pos = 0;
|
||||
|
||||
buffer_type buffer2;
|
||||
boost::iostreams::stream<boost::iostreams::back_insert_device<buffer_type>> output_stream_header(buffer2);
|
||||
boost::iostreams::stream<boost::iostreams::back_insert_device<buffer_type>>
|
||||
output_stream_header(buffer2);
|
||||
|
||||
uint32_t bd_size = 0;
|
||||
|
||||
|
@ -143,7 +137,8 @@ bool BootstrapFile::initialize_file()
|
|||
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());
|
||||
throw std::runtime_error(
|
||||
"Error in serialization of bootstrap::file_info size: "s + e.what());
|
||||
}
|
||||
output_stream_header << blob;
|
||||
output_stream_header << bd;
|
||||
|
@ -155,27 +150,27 @@ bool BootstrapFile::initialize_file()
|
|||
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());
|
||||
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 << 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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (chunk_size > BUFFER_SIZE) {
|
||||
log::warning(logcat, "WARNING: chunk_size {} > BUFFER_SIZE {}", chunk_size, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
|
@ -187,8 +182,7 @@ void BootstrapFile::flush_chunk()
|
|||
}
|
||||
*m_raw_data_file << blob;
|
||||
|
||||
if (m_max_chunk < chunk_size)
|
||||
{
|
||||
if (m_max_chunk < chunk_size) {
|
||||
m_max_chunk = chunk_size;
|
||||
}
|
||||
long pos_before = m_raw_data_file->tellp();
|
||||
|
@ -196,20 +190,25 @@ void BootstrapFile::flush_chunk()
|
|||
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);
|
||||
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);
|
||||
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)
|
||||
{
|
||||
void BootstrapFile::write_block(block& block) {
|
||||
bootstrap::block_package bp;
|
||||
bp.block = block;
|
||||
|
||||
|
@ -217,12 +216,9 @@ void BootstrapFile::write_block(block& block)
|
|||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
@ -233,13 +229,15 @@ void BootstrapFile::write_block(block& block)
|
|||
// 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.
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
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;
|
||||
|
@ -250,8 +248,7 @@ void BootstrapFile::write_block(block& block)
|
|||
m_output_stream->write((const char*)bd.data(), bd.size());
|
||||
}
|
||||
|
||||
bool BootstrapFile::close()
|
||||
{
|
||||
bool BootstrapFile::close() {
|
||||
if (m_raw_data_file->fail())
|
||||
return false;
|
||||
|
||||
|
@ -261,17 +258,18 @@ bool BootstrapFile::close()
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_memory_pool* _tx_pool, fs::path& output_file, uint64_t requested_block_stop)
|
||||
{
|
||||
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))
|
||||
{
|
||||
if (!BootstrapFile::open_writer(output_file)) {
|
||||
log::error(logcat, "failed to open raw file for write");
|
||||
return false;
|
||||
}
|
||||
|
@ -282,19 +280,19 @@ bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_mem
|
|||
// 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,
|
||||
"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
|
||||
{
|
||||
} 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)
|
||||
{
|
||||
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);
|
||||
|
@ -308,14 +306,14 @@ bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_mem
|
|||
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)
|
||||
{
|
||||
// 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";
|
||||
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)
|
||||
|
@ -324,14 +322,13 @@ bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_mem
|
|||
return BootstrapFile::close();
|
||||
}
|
||||
|
||||
uint64_t BootstrapFile::seek_to_first_chunk(fs::ifstream& import_file)
|
||||
{
|
||||
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)
|
||||
if (!import_file)
|
||||
throw std::runtime_error("Error reading expected number of bytes");
|
||||
str1.assign(buf1, sizeof(file_magic));
|
||||
|
||||
|
@ -341,19 +338,17 @@ uint64_t BootstrapFile::seek_to_first_chunk(fs::ifstream& import_file)
|
|||
throw std::runtime_error("Error in deserialization of file_magic: "s + e.what());
|
||||
}
|
||||
|
||||
if (file_magic != blockchain_raw_magic)
|
||||
{
|
||||
if (file_magic != blockchain_raw_magic) {
|
||||
log::error(logcat, "bootstrap file not recognized");
|
||||
throw std::runtime_error("Aborting");
|
||||
}
|
||||
else
|
||||
} 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)
|
||||
if (!import_file)
|
||||
throw std::runtime_error("Error reading expected number of bytes");
|
||||
try {
|
||||
serialization::parse_binary(str1, buflen_file_info);
|
||||
|
@ -365,7 +360,7 @@ uint64_t BootstrapFile::seek_to_first_chunk(fs::ifstream& import_file)
|
|||
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)
|
||||
if (!import_file)
|
||||
throw std::runtime_error("Error reading expected number of bytes");
|
||||
str1.assign(buf1, buflen_file_info);
|
||||
bootstrap::file_info bfi;
|
||||
|
@ -374,7 +369,11 @@ uint64_t BootstrapFile::seek_to_first_chunk(fs::ifstream& import_file)
|
|||
} 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 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);
|
||||
|
||||
|
@ -384,15 +383,14 @@ uint64_t BootstrapFile::seek_to_first_chunk(fs::ifstream& import_file)
|
|||
return full_header_size;
|
||||
}
|
||||
|
||||
uint64_t BootstrapFile::count_bytes(fs::ifstream& import_file, uint64_t blocks, uint64_t& h, bool& quit)
|
||||
{
|
||||
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)
|
||||
{
|
||||
while (1) {
|
||||
import_file.read(buf1, sizeof(chunk_size));
|
||||
if (!import_file) {
|
||||
std::cout << refresh_string;
|
||||
|
@ -409,27 +407,45 @@ uint64_t BootstrapFile::count_bytes(fs::ifstream& import_file, uint64_t blocks,
|
|||
}
|
||||
log::debug(logcat, "chunk_size: {}", chunk_size);
|
||||
|
||||
if (chunk_size > BUFFER_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);
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
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);
|
||||
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) {
|
||||
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);
|
||||
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;
|
||||
|
@ -440,8 +456,7 @@ uint64_t BootstrapFile::count_bytes(fs::ifstream& import_file, uint64_t blocks,
|
|||
return bytes_read;
|
||||
}
|
||||
|
||||
uint64_t BootstrapFile::count_blocks(const fs::path& import_file_path)
|
||||
{
|
||||
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);
|
||||
|
@ -450,10 +465,9 @@ uint64_t BootstrapFile::count_blocks(const fs::path& import_file_path)
|
|||
// 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))
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
@ -461,8 +475,7 @@ uint64_t BootstrapFile::count_blocks(const fs::path& import_file_path, std::stre
|
|||
|
||||
uint64_t start_height = seek_height;
|
||||
uint64_t h = 0;
|
||||
if (import_file.fail())
|
||||
{
|
||||
if (import_file.fail()) {
|
||||
log::error(logcat, "import_file.open() fail");
|
||||
throw std::runtime_error("Aborting");
|
||||
}
|
||||
|
@ -475,19 +488,16 @@ uint64_t BootstrapFile::count_blocks(const fs::path& import_file_path, std::stre
|
|||
uint64_t bytes_read = 0, blocks;
|
||||
int progress_interval = 10;
|
||||
|
||||
while (! quit)
|
||||
{
|
||||
if (start_height && h + progress_interval >= start_height - 1)
|
||||
{
|
||||
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 << "\r"
|
||||
<< "block height: " << h - 1 << " \r" << std::flush;
|
||||
|
||||
// std::cout << refresh_string;
|
||||
log::debug(logcat, "Number bytes scanned: {}", bytes_read);
|
||||
|
|
|
@ -28,49 +28,45 @@
|
|||
|
||||
#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:
|
||||
|
||||
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, 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);
|
||||
|
||||
bool store_blockchain_raw(cryptonote::Blockchain* cs, cryptonote::tx_memory_pool* txp,
|
||||
fs::path& output_file, uint64_t use_block_height=0);
|
||||
|
||||
protected:
|
||||
bool store_blockchain_raw(
|
||||
cryptonote::Blockchain* cs,
|
||||
cryptonote::tx_memory_pool* txp,
|
||||
fs::path& output_file,
|
||||
uint64_t use_block_height = 0);
|
||||
|
||||
protected:
|
||||
Blockchain* m_blockchain_storage;
|
||||
|
||||
tx_memory_pool* m_tx_pool;
|
||||
typedef std::vector<char> buffer_type;
|
||||
std::ofstream * m_raw_data_file;
|
||||
std::ofstream* m_raw_data_file;
|
||||
buffer_type m_buffer;
|
||||
boost::iostreams::stream<boost::iostreams::back_insert_device<buffer_type>>* m_output_stream;
|
||||
|
||||
|
@ -81,8 +77,7 @@ protected:
|
|||
void write_block(block& block);
|
||||
void flush_chunk();
|
||||
|
||||
private:
|
||||
|
||||
private:
|
||||
uint64_t m_height;
|
||||
uint64_t m_cur_height; // tracks current height during export
|
||||
uint32_t m_max_chunk;
|
||||
|
|
|
@ -31,14 +31,9 @@
|
|||
#include "cryptonote_basic/cryptonote_boost_serialization.h"
|
||||
#include "cryptonote_basic/difficulty.h"
|
||||
|
||||
namespace cryptonote { namespace bootstrap {
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
namespace bootstrap
|
||||
{
|
||||
|
||||
struct file_info
|
||||
{
|
||||
struct file_info {
|
||||
uint8_t major_version;
|
||||
uint8_t minor_version;
|
||||
uint32_t header_size;
|
||||
|
@ -50,8 +45,7 @@ namespace cryptonote
|
|||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct blocks_info
|
||||
{
|
||||
struct blocks_info {
|
||||
// block heights of file's first and last blocks, zero-based indexes
|
||||
uint64_t block_first;
|
||||
uint64_t block_last;
|
||||
|
@ -66,8 +60,7 @@ namespace cryptonote
|
|||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct block_package
|
||||
{
|
||||
struct block_package {
|
||||
cryptonote::block block;
|
||||
std::vector<transaction> txs;
|
||||
size_t block_weight;
|
||||
|
@ -83,6 +76,4 @@ namespace cryptonote
|
|||
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,21 +323,31 @@ 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"
|
||||
"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";
|
||||
}
|
||||
|
@ -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,36 +31,38 @@
|
|||
|
||||
#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);
|
||||
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[] =
|
||||
{
|
||||
height_to_hash const HARDCODED_MAINNET_CHECKPOINTS[] = {
|
||||
{0, "08ff156d993012b0bdf2816c4bee47c9bbc7930593b70ee02574edddf15ee933"},
|
||||
{1, "647997953a5ea9b5ab329c2291d4cbb08eed587c287e451eeeb2c79bab9b940f"},
|
||||
{10, "4a7cd8b9bff380d48d6f3533a5e0509f8589cc77d18218b3f7218846e77738fc"},
|
||||
|
@ -72,75 +74,71 @@ namespace cryptonote
|
|||
{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;
|
||||
|
||||
if (nettype == network_type::MAINNET)
|
||||
{
|
||||
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];
|
||||
height_to_hash const& entry = HARDCODED_MAINNET_CHECKPOINTS[last_index];
|
||||
|
||||
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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
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
|
||||
{
|
||||
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());
|
||||
} 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!");
|
||||
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
|
||||
{
|
||||
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;
|
||||
|
@ -148,32 +146,33 @@ namespace cryptonote
|
|||
}
|
||||
|
||||
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 batch_started = false;
|
||||
try
|
||||
{
|
||||
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());
|
||||
} 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();
|
||||
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)
|
||||
if (height < service_nodes::CHECKPOINT_STORE_PERSISTENTLY_INTERVAL ||
|
||||
info.block.major_version < hf::hf12_checkpointing)
|
||||
return;
|
||||
|
||||
uint64_t end_cull_height = 0;
|
||||
|
@ -182,77 +181,85 @@ namespace cryptonote
|
|||
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)
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
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());
|
||||
} 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))
|
||||
{
|
||||
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
|
||||
{
|
||||
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;
|
||||
|
||||
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)
|
||||
if (!found)
|
||||
return true;
|
||||
|
||||
bool result = checkpoint.check(h);
|
||||
|
@ -260,10 +267,10 @@ namespace cryptonote
|
|||
*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;
|
||||
|
||||
|
@ -271,15 +278,15 @@ namespace cryptonote
|
|||
return false;
|
||||
|
||||
{
|
||||
std::vector<checkpoint_t> const first_checkpoint = m_db->get_checkpoints_range(0, blockchain_height, 1);
|
||||
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))
|
||||
{
|
||||
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);
|
||||
|
@ -288,20 +295,18 @@ namespace cryptonote
|
|||
m_immutable_height = std::max(immutable_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;
|
||||
|
||||
return result;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool checkpoints::init(network_type nettype, BlockchainDB *db)
|
||||
{
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
bool checkpoints::init(network_type nettype, BlockchainDB* db) {
|
||||
*this = {};
|
||||
m_db = db;
|
||||
m_nettype = nettype;
|
||||
|
@ -309,18 +314,15 @@ namespace cryptonote
|
|||
if (db->is_read_only())
|
||||
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];
|
||||
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,38 +31,33 @@
|
|||
#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
|
||||
{
|
||||
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
|
||||
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)
|
||||
{
|
||||
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";
|
||||
|
@ -77,45 +72,44 @@ namespace cryptonote
|
|||
FIELD(signatures)
|
||||
FIELD(prev_height)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
};
|
||||
|
||||
struct height_to_hash
|
||||
{
|
||||
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 {
|
||||
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
|
||||
{
|
||||
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
|
||||
|
|
|
@ -26,20 +26,22 @@
|
|||
// 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)
|
||||
{
|
||||
static void local_abort(const char* msg) {
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
#ifdef NDEBUG
|
||||
_exit(1);
|
||||
|
@ -48,18 +50,16 @@ static void local_abort(const char *msg)
|
|||
#endif
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
uint64_t magic;
|
||||
void *raw;
|
||||
void* raw;
|
||||
size_t bytes;
|
||||
size_t align;
|
||||
} control;
|
||||
|
||||
void *aligned_malloc(size_t bytes, size_t align)
|
||||
{
|
||||
void* aligned_malloc(size_t bytes, size_t align) {
|
||||
void *raw, *ptr;
|
||||
control *ctrl;
|
||||
control* ctrl;
|
||||
|
||||
if (!is_power_of_2(align))
|
||||
return NULL;
|
||||
|
@ -71,7 +71,7 @@ void *aligned_malloc(size_t bytes, size_t align)
|
|||
raw = malloc(bytes + sizeof(control) + align);
|
||||
if (!raw)
|
||||
return NULL;
|
||||
ptr = (void*)(((uintptr_t)raw + align + sizeof(control) - 1) & ~(align-1));
|
||||
ptr = (void*)(((uintptr_t)raw + align + sizeof(control) - 1) & ~(align - 1));
|
||||
ctrl = ((control*)ptr) - 1;
|
||||
ctrl->magic = MAGIC;
|
||||
ctrl->raw = raw;
|
||||
|
@ -80,15 +80,13 @@ void *aligned_malloc(size_t bytes, size_t align)
|
|||
return ptr;
|
||||
}
|
||||
|
||||
void *aligned_realloc(void *ptr, size_t bytes, size_t align)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!bytes) {
|
||||
aligned_free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -113,7 +111,7 @@ void *aligned_realloc(void *ptr, size_t bytes, size_t align)
|
|||
raw = malloc(bytes + sizeof(control) + ctrl->align);
|
||||
if (!raw)
|
||||
return NULL;
|
||||
ptr2 = (void*)(((uintptr_t)raw + ctrl->align + sizeof(control) - 1) & ~(ctrl->align-1));
|
||||
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;
|
||||
|
@ -125,11 +123,10 @@ void *aligned_realloc(void *ptr, size_t bytes, size_t align)
|
|||
return ptr2;
|
||||
}
|
||||
|
||||
void aligned_free(void *ptr)
|
||||
{
|
||||
void aligned_free(void* ptr) {
|
||||
if (!ptr)
|
||||
return;
|
||||
control *ctrl = ((control*)ptr) - 1;
|
||||
control* ctrl = ((control*)ptr) - 1;
|
||||
if (ctrl->magic == MAGIC_FREED)
|
||||
local_abort("Double free detected");
|
||||
if (ctrl->magic != MAGIC)
|
||||
|
|
|
@ -32,9 +32,9 @@
|
|||
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);
|
||||
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
|
||||
}
|
||||
|
|
|
@ -32,30 +32,26 @@
|
|||
|
||||
#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
|
||||
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())
|
||||
{
|
||||
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)
|
||||
{
|
||||
for (size_t i = 0; i < permutation.size(); ++i) {
|
||||
size_t current = i;
|
||||
while (i != permutation[current])
|
||||
{
|
||||
while (i != permutation[current]) {
|
||||
size_t next = permutation[current];
|
||||
swap(current, next);
|
||||
permutation[current] = current;
|
||||
|
@ -65,16 +61,14 @@ void apply_permutation(std::vector<size_t> permutation, const F &swap)
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void apply_permutation(const std::vector<size_t> &permutation, std::vector<T> &v)
|
||||
{
|
||||
if (permutation.size() != v.size())
|
||||
{
|
||||
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]); });
|
||||
apply_permutation(permutation, [&v](size_t i0, size_t i1) { std::swap(v[i0], v[i1]); });
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -32,46 +32,42 @@
|
|||
|
||||
#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;
|
||||
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 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 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
|
||||
{
|
||||
struct reverse_alphabet_table {
|
||||
std::array<int8_t, 256> from_b58_lut;
|
||||
constexpr reverse_alphabet_table() noexcept : 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
|
||||
{
|
||||
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)
|
||||
{
|
||||
uint64_t uint_8be_to_64(const uint8_t* data, size_t size) {
|
||||
assert(1 <= size && size <= sizeof(uint64_t));
|
||||
|
||||
uint64_t res = 0;
|
||||
|
@ -79,22 +75,19 @@ namespace tools
|
|||
return SWAP64BE(res);
|
||||
}
|
||||
|
||||
void uint_64_to_8be(uint64_t num, size_t size, uint8_t* data)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
while (0 < num) {
|
||||
uint64_t remainder = num % alphabet.size();
|
||||
num /= alphabet.size();
|
||||
res[i] = alphabet[remainder];
|
||||
|
@ -102,8 +95,7 @@ namespace tools
|
|||
}
|
||||
}
|
||||
|
||||
bool decode_block(const char* block, size_t size, char* res)
|
||||
{
|
||||
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];
|
||||
|
@ -112,8 +104,7 @@ namespace tools
|
|||
|
||||
uint64_t res_num = 0;
|
||||
uint64_t order = 1;
|
||||
for (size_t i = size - 1; i < size; --i)
|
||||
{
|
||||
for (size_t i = size - 1; i < size; --i) {
|
||||
auto digit = reverse_alphabet[block[i]];
|
||||
if (digit < 0)
|
||||
return false; // Invalid symbol
|
||||
|
@ -127,42 +118,45 @@ namespace tools
|
|||
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)
|
||||
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)
|
||||
{
|
||||
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];
|
||||
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]);
|
||||
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]);
|
||||
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())
|
||||
{
|
||||
bool decode(std::string_view enc, std::string& data) {
|
||||
if (enc.empty()) {
|
||||
data.clear();
|
||||
return true;
|
||||
}
|
||||
|
@ -175,15 +169,18 @@ namespace tools
|
|||
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]))
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
@ -191,8 +188,7 @@ namespace tools
|
|||
return true;
|
||||
}
|
||||
|
||||
std::string encode_addr(uint64_t tag, std::string_view data)
|
||||
{
|
||||
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());
|
||||
|
@ -201,12 +197,13 @@ namespace tools
|
|||
return encode(buf);
|
||||
}
|
||||
|
||||
bool decode_addr(std::string_view addr, uint64_t& tag, std::string& data)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
|
@ -214,13 +211,15 @@ namespace tools
|
|||
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;
|
||||
if (expected_checksum != checksum)
|
||||
return false;
|
||||
|
||||
int read = tools::read_varint(addr_data.begin(), addr_data.end(), tag);
|
||||
if (read <= 0) return false;
|
||||
if (read <= 0)
|
||||
return false;
|
||||
|
||||
data = addr_data.substr(read);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace base58
|
||||
} // namespace tools
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -31,18 +31,16 @@
|
|||
#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())
|
||||
|
@ -57,23 +55,19 @@ namespace tools
|
|||
|
||||
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
|
||||
{
|
||||
try {
|
||||
// first try reading in portable mode
|
||||
boost::archive::portable_binary_iarchive{data_file} >> obj;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
} catch (...) {
|
||||
// if failed, try reading in unportable mode
|
||||
auto unportable = file_path;
|
||||
unportable += ".unportable";
|
||||
|
@ -86,5 +80,5 @@ namespace tools
|
|||
}
|
||||
return !data_file.fail();
|
||||
CATCH_ENTRY_L0("unserialize_obj_from_file", false);
|
||||
}
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -32,8 +32,7 @@
|
|||
|
||||
namespace tools {
|
||||
|
||||
uint64_t combinations_count(uint32_t k, uint32_t 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");
|
||||
}
|
||||
|
@ -47,4 +46,4 @@ uint64_t combinations_count(uint32_t k, uint32_t n)
|
|||
return c;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -30,23 +30,23 @@
|
|||
|
||||
#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);
|
||||
|
||||
private:
|
||||
private:
|
||||
void doCombine(size_t from, size_t k);
|
||||
|
||||
std::vector<T> origin;
|
||||
|
@ -54,16 +54,13 @@ private:
|
|||
std::vector<size_t> current;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
std::vector<std::vector<T>> Combinator<T>::combine(size_t k)
|
||||
{
|
||||
if (k > origin.size())
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (k == 0) {
|
||||
throw std::runtime_error("k must be greater than zero");
|
||||
}
|
||||
|
||||
|
@ -72,20 +69,18 @@ std::vector<std::vector<T>> Combinator<T>::combine(size_t k)
|
|||
return combinations;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Combinator<T>::doCombine(size_t from, size_t k)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
for (auto ind : current) {
|
||||
comb.push_back(origin[ind]);
|
||||
}
|
||||
combinations.push_back(comb);
|
||||
|
@ -95,4 +90,4 @@ void Combinator<T>::doCombine(size_t from, size_t k)
|
|||
current.pop_back();
|
||||
}
|
||||
|
||||
} //namespace tools
|
||||
} // namespace tools
|
||||
|
|
|
@ -29,18 +29,18 @@
|
|||
// 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,8 +51,8 @@ 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() {
|
||||
|
@ -64,11 +64,12 @@ std::pair<unsigned, unsigned> terminal_size() {
|
|||
|
||||
#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;
|
||||
|
||||
|
@ -76,13 +77,13 @@ std::pair<unsigned, unsigned> boost_option_sizes() {
|
|||
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;
|
||||
}
|
||||
|
||||
void clear_screen()
|
||||
{
|
||||
void clear_screen() {
|
||||
#ifdef HAVE_READLINE
|
||||
rdln::clear_screen();
|
||||
#else
|
||||
|
@ -91,40 +92,36 @@ void clear_screen()
|
|||
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
|
||||
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))
|
||||
{
|
||||
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
|
||||
#endif
|
||||
}
|
||||
|
||||
bool handle_error_helper(const boost::program_options::options_description& desc, std::function<bool()> parser) {
|
||||
try
|
||||
{
|
||||
bool handle_error_helper(
|
||||
const boost::program_options::options_description& desc, std::function<bool()> parser) {
|
||||
try {
|
||||
return parser();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Failed to parse arguments: " << e.what() << std::endl;
|
||||
std::cerr << desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
} catch (...) {
|
||||
std::cerr << "Failed to parse arguments: unknown exception" << std::endl;
|
||||
std::cerr << desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace command_line
|
||||
|
|
|
@ -30,69 +30,75 @@
|
|||
|
||||
#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,28 +121,27 @@ 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);
|
||||
return semantic;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
namespace {
|
||||
template <typename T>
|
||||
std::string arg_stringify(const T& a) {
|
||||
return "{}"_format(a);
|
||||
|
@ -146,35 +150,36 @@ namespace command_line
|
|||
std::string arg_stringify(const std::vector<T>& 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);
|
||||
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)
|
||||
{
|
||||
for (size_t i = 0; i < depval.size(); ++i) {
|
||||
depval.fill(false);
|
||||
depval[i] = true;
|
||||
fmt::format_to(std::back_inserter(default_display),
|
||||
fmt::format_to(
|
||||
std::back_inserter(default_display),
|
||||
", {} if '{}'",
|
||||
arg_stringify(arg.depf(depval, true, arg.default_value)),
|
||||
arg.ref[i]->name);
|
||||
|
@ -184,132 +189,141 @@ namespace command_line
|
|||
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);
|
||||
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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
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]);
|
||||
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
|
||||
|
|
|
@ -30,10 +30,9 @@
|
|||
|
||||
#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);
|
||||
void explicit_bzero(void* s, size_t len) {
|
||||
memset(s, '\0', len);
|
||||
/* Compiler barrier. */
|
||||
asm volatile ("" ::: "memory");
|
||||
asm volatile("" ::: "memory");
|
||||
}
|
||||
|
||||
// Redefine explicit_bzero_chk
|
||||
void
|
||||
__explicit_bzero_chk (void *dst, size_t len, size_t dstlen)
|
||||
{
|
||||
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);
|
||||
if (__glibc_unlikely(dstlen < len))
|
||||
__chk_fail();
|
||||
memset(dst, '\0', len);
|
||||
/* Compiler barrier. */
|
||||
asm volatile ("" ::: "memory");
|
||||
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))
|
||||
{
|
||||
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();
|
||||
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;
|
||||
default: break;
|
||||
}
|
||||
return "Unknown basic_category() value";
|
||||
}
|
||||
|
||||
virtual std::error_condition default_error_condition(int value) const noexcept override final
|
||||
{
|
||||
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))
|
||||
{
|
||||
switch (common_error(value)) {
|
||||
case common_error::kInvalidArgument:
|
||||
case common_error::kInvalidErrorCode:
|
||||
return std::errc::invalid_argument;
|
||||
default:
|
||||
break;
|
||||
case common_error::kInvalidErrorCode: return std::errc::invalid_argument;
|
||||
default: break;
|
||||
}
|
||||
return std::error_condition{value, *this};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const category instance{};
|
||||
}
|
||||
const category instance{};
|
||||
} // namespace
|
||||
|
||||
std::error_category const& common_category() noexcept
|
||||
{
|
||||
std::error_category const& common_category() noexcept {
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,8 +28,7 @@
|
|||
|
||||
#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>`
|
||||
|
@ -37,15 +36,11 @@ enum class common_error : int
|
|||
|
||||
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,25 +26,21 @@
|
|||
// 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)
|
||||
{
|
||||
#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)
|
||||
{
|
||||
if (msg) {
|
||||
error_msg.append(msg);
|
||||
if (file)
|
||||
error_msg.append(" (");
|
||||
}
|
||||
if (file)
|
||||
{
|
||||
if (file) {
|
||||
error_msg.append("thrown at ");
|
||||
|
||||
// remove path, get just filename + extension
|
||||
|
@ -57,12 +53,11 @@ namespace detail
|
|||
error_msg.push_back(')');
|
||||
return error_msg;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void expect::throw_(std::error_code ec, const char* msg, const char* file, unsigned line)
|
||||
{
|
||||
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};
|
||||
}
|
||||
} // detail
|
||||
}
|
||||
} // namespace detail
|
||||
|
|
|
@ -37,17 +37,15 @@
|
|||
|
||||
//! 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__ ; \
|
||||
do { \
|
||||
const ::expect<void> result = __VA_ARGS__; \
|
||||
if (!result) \
|
||||
return result.error(); \
|
||||
} while (0)
|
||||
|
@ -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
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
};
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
@ -306,53 +279,41 @@ public:
|
|||
\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)
|
||||
{
|
||||
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
|
||||
|
|
|
@ -30,44 +30,47 @@
|
|||
// 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>
|
||||
#include <shlobj.h>
|
||||
#include <strsafe.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/file.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/stat.h>
|
||||
#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;
|
||||
|
||||
|
@ -80,29 +83,25 @@ namespace tools {
|
|||
if (ret < 0)
|
||||
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;
|
||||
const bool fail =
|
||||
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, std::addressof(temp)) == 0;
|
||||
process.reset(temp);
|
||||
if (fail)
|
||||
return {};
|
||||
|
@ -114,7 +113,8 @@ namespace tools {
|
|||
return {};
|
||||
|
||||
std::unique_ptr<char[]> sid{new char[sid_size]};
|
||||
if (!GetTokenInformation(process.get(), TokenOwner, sid.get(), sid_size, std::addressof(sid_size)))
|
||||
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;
|
||||
|
@ -125,35 +125,36 @@ namespace tools {
|
|||
if (!InitializeAcl(reinterpret_cast<PACL>(dacl.get()), daclSize, ACL_REVISION))
|
||||
return {};
|
||||
|
||||
if (!AddAccessAllowedAce(reinterpret_cast<PACL>(dacl.get()), ACL_REVISION, (READ_CONTROL | FILE_GENERIC_READ | DELETE), psid))
|
||||
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 {};
|
||||
|
||||
if (!SetSecurityDescriptorDacl(std::addressof(descriptor), true, reinterpret_cast<PACL>(dacl.get()), false))
|
||||
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(
|
||||
std::unique_ptr<void, close_handle> file{CreateFileW(
|
||||
name.c_str(),
|
||||
GENERIC_WRITE, FILE_SHARE_READ,
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_READ,
|
||||
std::addressof(attributes),
|
||||
CREATE_NEW, (FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE),
|
||||
nullptr
|
||||
)
|
||||
};
|
||||
if (file)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (0 <= fd) {
|
||||
file.release();
|
||||
std::FILE* real_file = _fdopen(fd, "w");
|
||||
if (!real_file)
|
||||
{
|
||||
if (!real_file) {
|
||||
_close(fd);
|
||||
}
|
||||
return {real_file, std::move(name)};
|
||||
|
@ -161,11 +162,9 @@ namespace tools {
|
|||
}
|
||||
#else
|
||||
const int fdr = open(name.c_str(), (O_RDONLY | O_CREAT), S_IRUSR);
|
||||
if (0 <= fdr)
|
||||
{
|
||||
if (0 <= fdr) {
|
||||
struct stat rstats = {};
|
||||
if (fstat(fdr, std::addressof(rstats)) != 0)
|
||||
{
|
||||
if (fstat(fdr, std::addressof(rstats)) != 0) {
|
||||
close(fdr);
|
||||
return {};
|
||||
}
|
||||
|
@ -174,148 +173,135 @@ namespace tools {
|
|||
fchmod(fdr, rstats.st_mode);
|
||||
close(fdr);
|
||||
|
||||
if (0 <= fdw)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 (file)
|
||||
return {file, std::move(name)};
|
||||
}
|
||||
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)
|
||||
{
|
||||
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());
|
||||
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 {
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
} 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);
|
||||
#else
|
||||
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))
|
||||
{
|
||||
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>()));
|
||||
std::string content(
|
||||
(std::istreambuf_iterator<char>(in)), (std::istreambuf_iterator<char>()));
|
||||
contents = std::move(content);
|
||||
return true;
|
||||
} catch (...) {
|
||||
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 {
|
||||
|
@ -325,6 +311,6 @@ namespace tools {
|
|||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
|
|
|
@ -31,44 +31,41 @@
|
|||
//
|
||||
#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)
|
||||
{
|
||||
//! 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,23 +81,23 @@ 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.
|
||||
/*! \brief Returns the default data directory.
|
||||
*
|
||||
* \details Windows < Vista: C:\\Documents and Settings\\Username\\Application Data\\CRYPTONOTE_NAME
|
||||
*
|
||||
|
@ -110,11 +107,11 @@ namespace tools {
|
|||
*
|
||||
* Unix: ~/.CRYPTONOTE_NAME
|
||||
*/
|
||||
fs::path get_default_data_dir();
|
||||
fs::path get_depreciated_default_data_dir();
|
||||
fs::path get_default_data_dir();
|
||||
fs::path get_depreciated_default_data_dir();
|
||||
|
||||
#ifdef WIN32
|
||||
/**
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param nfolder
|
||||
|
@ -122,17 +119,17 @@ namespace tools {
|
|||
*
|
||||
* @return
|
||||
*/
|
||||
fs::path get_special_folder_path(int nfolder, bool iscreate);
|
||||
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,46 +1,47 @@
|
|||
#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;
|
||||
//
|
||||
template <typename T, typename SFINAE = void>
|
||||
constexpr bool via_to_string = false;
|
||||
// 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;
|
||||
// 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;
|
||||
// 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 {
|
||||
namespace detail {
|
||||
|
||||
template <typename T, typename SFINAE = void>
|
||||
constexpr bool has_to_string_method = false;
|
||||
|
@ -48,19 +49,19 @@ namespace formattable {
|
|||
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;
|
||||
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;
|
||||
inline constexpr bool has_to_hex_string_method<
|
||||
T,
|
||||
std::void_t<decltype(std::declval<const T&>().to_hex_string())>> = true;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
struct to_string_formatter : fmt::formatter<std::string_view> {
|
||||
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>)
|
||||
|
@ -68,10 +69,10 @@ namespace formattable {
|
|||
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 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>)
|
||||
|
@ -79,14 +80,15 @@ namespace formattable {
|
|||
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>> {
|
||||
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>>,
|
||||
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>
|
||||
|
@ -94,24 +96,22 @@ namespace formattable {
|
|||
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 <>
|
||||
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
|
||||
|
||||
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))
|
||||
// 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
|
||||
|
|
|
@ -28,35 +28,54 @@
|
|||
// 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);
|
||||
|
@ -72,26 +91,23 @@ std::string i18n_get_language()
|
|||
language = language.substr(0, language.find("@"));
|
||||
|
||||
// check valid values
|
||||
for (char c: language)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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];
|
||||
|
@ -107,17 +123,14 @@ static std::string utf16(const unsigned char *data, uint32_t len)
|
|||
}
|
||||
if (code <= 0x7f) {
|
||||
s += (char)code;
|
||||
}
|
||||
else if (code <= 0x7ff) {
|
||||
} else if (code <= 0x7ff) {
|
||||
s += 0xc0 | (code >> 6);
|
||||
s += 0x80 | (code & 0x3f);
|
||||
}
|
||||
else if (code <= 0xffff) {
|
||||
} else if (code <= 0xffff) {
|
||||
s += 0xe0 | (code >> 12);
|
||||
s += 0x80 | ((code >> 6) & 0x3f);
|
||||
s += 0x80 | (code & 0x3f);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
s += 0xf0 | (code >> 18);
|
||||
s += 0x80 | ((code >> 12) & 0x3f);
|
||||
s += 0x80 | ((code >> 6) & 0x3f);
|
||||
|
@ -127,14 +140,12 @@ static std::string utf16(const unsigned char *data, uint32_t len)
|
|||
return s;
|
||||
}
|
||||
|
||||
static std::string utf8(const unsigned char *data, uint32_t len)
|
||||
{
|
||||
static std::string utf8(const unsigned char* data, uint32_t len) {
|
||||
/* assume well formedness */
|
||||
return std::string((const char *)data,len);
|
||||
return std::string((const char*)data, len);
|
||||
}
|
||||
|
||||
int i18n_set_language(const char *directory, const char *base, std::string language)
|
||||
{
|
||||
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;
|
||||
|
@ -155,9 +166,10 @@ int i18n_set_language(const char *directory, const char *base, std::string langu
|
|||
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(), '_');
|
||||
const char* underscore = strchr(language.c_str(), '_');
|
||||
if (underscore) {
|
||||
std::string fallback_language = std::string(language, 0, underscore - language.c_str());
|
||||
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);
|
||||
|
@ -179,7 +191,7 @@ int i18n_set_language(const char *directory, const char *base, std::string langu
|
|||
}
|
||||
}
|
||||
|
||||
const unsigned char *data = reinterpret_cast<const unsigned char*>(contents.c_str());
|
||||
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);
|
||||
|
@ -226,7 +238,7 @@ int i18n_set_language(const char *directory, const char *base, std::string langu
|
|||
return -1;
|
||||
}
|
||||
chunk_type = data[idx++];
|
||||
chunk_size = be32(data+idx);
|
||||
chunk_size = be32(data + idx);
|
||||
idx += 4;
|
||||
|
||||
i18n_log("Found " << chunk_type << " of " << chunk_size << " bytes");
|
||||
|
@ -246,9 +258,7 @@ int i18n_set_language(const char *directory, const char *base, std::string langu
|
|||
i18n_log("Found messages at " << idx);
|
||||
messages_idx = idx;
|
||||
break;
|
||||
default:
|
||||
i18n_log("Found unsupported chunk type: " << chunk_type);
|
||||
break;
|
||||
default: i18n_log("Found unsupported chunk type: " << chunk_type); break;
|
||||
}
|
||||
|
||||
idx += chunk_size;
|
||||
|
@ -265,8 +275,8 @@ int i18n_set_language(const char *directory, const char *base, std::string langu
|
|||
|
||||
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);
|
||||
be32(data + offsets_idx + m * 8); // unused
|
||||
idx = be32(data + offsets_idx + m * 8 + 4);
|
||||
idx += messages_idx;
|
||||
|
||||
if (idx > datalen || idx + 1 > datalen) {
|
||||
|
@ -289,7 +299,7 @@ int i18n_set_language(const char *directory, const char *base, std::string langu
|
|||
break;
|
||||
}
|
||||
|
||||
chunk_size = be32(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) {
|
||||
|
@ -298,15 +308,15 @@ int i18n_set_language(const char *directory, const char *base, std::string langu
|
|||
}
|
||||
switch (chunk_type) {
|
||||
case 0x03: // translation, UTF-16
|
||||
translation = utf16(data+idx, chunk_size);
|
||||
translation = utf16(data + idx, chunk_size);
|
||||
i18n_log("Found translation: " << translation);
|
||||
break;
|
||||
case 0x06: // source, UTF-8
|
||||
source = utf8(data+idx, chunk_size);
|
||||
source = utf8(data + idx, chunk_size);
|
||||
i18n_log("Found source: " << source);
|
||||
break;
|
||||
case 0x07: // context, UTF-8
|
||||
context = utf8(data+idx, chunk_size);
|
||||
context = utf8(data + idx, chunk_size);
|
||||
i18n_log("Found context: " << context);
|
||||
break;
|
||||
}
|
||||
|
@ -318,13 +328,10 @@ int i18n_set_language(const char *directory, const char *base, std::string langu
|
|||
}
|
||||
|
||||
/* The entries is constant by that time */
|
||||
const char *i18n_translate(const char *s, const std::string &context)
|
||||
{
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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,10 +1,12 @@
|
|||
#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;
|
||||
|
@ -17,9 +19,11 @@ namespace tools {
|
|||
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)) {
|
||||
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;
|
||||
|
@ -28,15 +32,15 @@ namespace tools {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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 {
|
||||
// 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;
|
||||
|
@ -76,9 +86,12 @@ 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
|
||||
|
@ -115,16 +128,16 @@ namespace tools {
|
|||
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&) {
|
||||
|
@ -133,6 +146,5 @@ namespace nlohmann {
|
|||
static void from_json(const json& j, T& 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,12 +5,12 @@
|
|||
|
||||
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)>{};
|
||||
|
@ -23,11 +23,11 @@ namespace tools {
|
|||
|
||||
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;
|
||||
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
|
||||
|
||||
/// 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
|
||||
|
|
|
@ -26,24 +26,23 @@
|
|||
// 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:
|
||||
- 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)
|
||||
{
|
||||
Notify::Notify(std::string_view spec) {
|
||||
CHECK_AND_ASSERT_THROW_MES(!spec.empty(), "Empty spec");
|
||||
|
||||
auto pieces = tools::split_any(spec, " \t", true);
|
||||
|
@ -56,8 +55,8 @@ Notify::Notify(std::string_view spec)
|
|||
args.emplace_back(piece);
|
||||
}
|
||||
|
||||
void Notify::replace_tag(std::vector<std::string>& margs, std::string_view tag, std::string_view value)
|
||||
{
|
||||
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
|
||||
|
@ -74,4 +73,4 @@ int Notify::spawn(const std::vector<std::string>& margs) const {
|
|||
return tools::spawn(filename, margs, false);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -28,19 +28,19 @@
|
|||
|
||||
#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:
|
||||
class Notify {
|
||||
public:
|
||||
explicit Notify(std::string_view spec);
|
||||
|
||||
template <typename T, typename... MoreTags>
|
||||
|
@ -50,20 +50,25 @@ public:
|
|||
return spawn(margs);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
fs::path filename;
|
||||
std::vector<std::string> args;
|
||||
|
||||
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) {
|
||||
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,9 +40,7 @@ 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)
|
||||
{
|
||||
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
|
||||
|
@ -51,12 +50,12 @@ oxen::exp2(double x)
|
|||
// if (isnand (x)) // unnecessary for us
|
||||
// return x;
|
||||
|
||||
if (x > (double) DBL_MAX_EXP)
|
||||
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))
|
||||
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. */
|
||||
|
@ -94,7 +93,7 @@ oxen::exp2(double x)
|
|||
truncate the series after the z^5 term. */
|
||||
|
||||
{
|
||||
double nm = oxen::round (x * 256.0); /* = 256 * n + m */
|
||||
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). */
|
||||
|
@ -108,16 +107,12 @@ oxen::exp2(double x)
|
|||
#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 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);
|
||||
|
||||
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
|
||||
|
@ -127,8 +122,7 @@ oxen::exp2(double x)
|
|||
(dotimes (i 257)
|
||||
(format t " ~D,~%"
|
||||
(float (exp (* (/ (- i 128) 256) (log 2L0))) a))) */
|
||||
static const double exp_table[257] =
|
||||
{
|
||||
static const double exp_table[257] = {
|
||||
0.707106781186547524400844362104849039284,
|
||||
0.709023942160207598920563322257676190836,
|
||||
0.710946301084582779904674297352120049962,
|
||||
|
@ -385,8 +379,7 @@ oxen::exp2(double x)
|
|||
1.40277269122020470637471352433337881711,
|
||||
1.40657599381901544248361973255451684411,
|
||||
1.410389608217270704414375128268675481145,
|
||||
1.41421356237309504880168872420969807857
|
||||
};
|
||||
1.41421356237309504880168872420969807857};
|
||||
|
||||
double ret = exp_table[128 + m] * exp_y;
|
||||
for (int i = 0; i < n; i++)
|
||||
|
@ -416,36 +409,34 @@ 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)
|
||||
{
|
||||
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)(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
|
||||
|
@ -456,14 +447,12 @@ oxen::round (double x)
|
|||
volatile double y = x;
|
||||
volatile double z = y;
|
||||
|
||||
if (z > 0.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)
|
||||
{
|
||||
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
|
||||
|
@ -474,15 +463,12 @@ oxen::round (double x)
|
|||
if (z > y)
|
||||
z -= 1.0;
|
||||
}
|
||||
}
|
||||
else if (z < 0.0)
|
||||
{
|
||||
} else if (z < 0.0) {
|
||||
/* Avoid rounding error for x = -(0.5 - 2^(-DBL_MANT_DIG-1)). */
|
||||
if (z > - 0.5)
|
||||
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)
|
||||
{
|
||||
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
|
||||
|
@ -496,4 +482,3 @@ oxen::round (double x)
|
|||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
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(); }
|
||||
|
||||
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; }
|
||||
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
|
||||
{
|
||||
struct defer_helper {
|
||||
template <typename lambda_t>
|
||||
deferred<lambda_t> operator+(lambda_t lambda) { return lambda; }
|
||||
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
|
||||
|
|
|
@ -30,10 +30,10 @@
|
|||
|
||||
#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,29 +63,22 @@ 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!r) {
|
||||
break;
|
||||
}
|
||||
else if (ucs2_ch == L'\r')
|
||||
{
|
||||
} else if (ucs2_ch == L'\r') {
|
||||
std::cout << std::endl;
|
||||
break;
|
||||
}
|
||||
else if (ucs2_ch == L'\b')
|
||||
{
|
||||
if (!pass.empty())
|
||||
{
|
||||
} else if (ucs2_ch == L'\b') {
|
||||
if (!pass.empty()) {
|
||||
int len = chlen.back();
|
||||
chlen.pop_back();
|
||||
while(len-- > 0)
|
||||
while (len-- > 0)
|
||||
pass.pop_back();
|
||||
}
|
||||
continue;
|
||||
|
@ -96,10 +86,11 @@ namespace
|
|||
|
||||
char utf8_ch[8] = {0};
|
||||
int len;
|
||||
if((len = WideCharToMultiByte(CP_UTF8, 0, &ucs2_ch, 1, utf8_ch, sizeof(utf8_ch), NULL, NULL)) <= 0)
|
||||
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)
|
||||
if (pass.size() + len >= tools::password_container::max_password_size)
|
||||
break;
|
||||
|
||||
chlen.push_back(len);
|
||||
|
@ -109,17 +100,15 @@ namespace
|
|||
::SetConsoleMode(h_cin, mode_old);
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
while (aPass.size() < tools::password_container::max_password_size) {
|
||||
int ch = getch(hide_input);
|
||||
if (EOF == ch || ch == EOT)
|
||||
{
|
||||
if (EOF == ch || ch == EOT) {
|
||||
return false;
|
||||
}
|
||||
else if (ch == '\n' || ch == '\r')
|
||||
{
|
||||
} else if (ch == '\n' || ch == '\r') {
|
||||
std::cout << std::endl;
|
||||
break;
|
||||
}
|
||||
else if (ch == BACKSPACE)
|
||||
{
|
||||
if (!aPass.empty())
|
||||
{
|
||||
} else if (ch == BACKSPACE) {
|
||||
if (!aPass.empty()) {
|
||||
aPass.pop_back();
|
||||
if (!hide_input)
|
||||
std::cout << "\b\b\b \b\b\b" << std::flush;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
if (!hide_input)
|
||||
std::cout << "\b\b \b\b" << std::flush;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
aPass.push_back(ch);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
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;
|
||||
std::cout << message << ": " << std::flush;
|
||||
if (!read_from_tty(pass1, hide_input))
|
||||
return false;
|
||||
if (verify)
|
||||
{
|
||||
if (verify) {
|
||||
std::cout << "Confirm password: ";
|
||||
if (!read_from_tty(pass2, hide_input))
|
||||
return false;
|
||||
if(pass1!=pass2)
|
||||
{
|
||||
if (pass1 != pass2) {
|
||||
std::cout << "Passwords do not match! Please try again." << std::endl;
|
||||
pass1.clear();
|
||||
pass2.clear();
|
||||
}
|
||||
else //new password matches
|
||||
} else // new password matches
|
||||
return true;
|
||||
}
|
||||
else
|
||||
} else
|
||||
return true;
|
||||
//No need to verify password entered at this point in the code
|
||||
// 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)
|
||||
{
|
||||
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')
|
||||
{
|
||||
if (std::cin.eof() || ch == '\n' || ch == '\r') {
|
||||
break;
|
||||
}
|
||||
else if (std::cin.fail())
|
||||
{
|
||||
} else if (std::cin.fail()) {
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
pass.push_back(ch);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // 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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (loc == std::string::npos) {
|
||||
auto result = prompt(verify);
|
||||
if (!result)
|
||||
return std::nullopt;
|
||||
|
||||
out.password = std::move(*result);
|
||||
}
|
||||
else
|
||||
{
|
||||
} 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
|
||||
|
|
|
@ -30,17 +30,16 @@
|
|||
|
||||
#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,36 +1,36 @@
|
|||
#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,
|
||||
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))}
|
||||
{}
|
||||
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))
|
||||
{
|
||||
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));
|
||||
m_next_delay = std::chrono::microseconds(crypto::rand_range(
|
||||
m_random_delay_interval.first, m_random_delay_interval.second));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,11 +41,11 @@ class periodic_task
|
|||
// Changes the current interval
|
||||
void interval(std::chrono::microseconds us) { m_interval = us; }
|
||||
|
||||
private:
|
||||
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
|
||||
|
|
|
@ -26,25 +26,25 @@
|
|||
// 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");
|
||||
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);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
@ -53,25 +53,26 @@ bool has_unpruned_block(uint64_t block_height, uint64_t blockchain_height, uint3
|
|||
return block_stripe == 0 || block_stripe == stripe;
|
||||
}
|
||||
|
||||
uint32_t get_pruning_stripe(uint64_t block_height, uint64_t blockchain_height, uint32_t log_stripes)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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");
|
||||
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;
|
||||
|
@ -85,15 +86,16 @@ uint64_t get_next_unpruned_block_height(uint64_t block_height, uint64_t blockcha
|
|||
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;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
@ -106,13 +108,12 @@ uint64_t get_next_pruned_block_height(uint64_t block_height, uint64_t blockchain
|
|||
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));
|
||||
return get_next_unpruned_block_height(
|
||||
block_height, blockchain_height, tools::make_pruning_seed(next_stripe, log_stripes));
|
||||
}
|
||||
|
||||
uint32_t get_random_stripe()
|
||||
{
|
||||
uint32_t get_random_stripe() {
|
||||
return 1 + crypto::rand<uint8_t>() % (1ul << PRUNING_LOG_STRIPES);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace tools
|
||||
|
|
|
@ -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 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)
|
||||
{
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
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));
|
||||
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);
|
||||
}
|
||||
|
||||
};
|
||||
}; // 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)
|
||||
{
|
||||
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)
|
||||
if (height - 1 + LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//interpret as time
|
||||
} 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)
|
||||
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,8 +30,7 @@
|
|||
|
||||
#include <cstdint>
|
||||
|
||||
namespace cryptonote::rules
|
||||
{
|
||||
namespace cryptonote::rules {
|
||||
|
||||
bool is_output_unlocked(uint64_t unlock_time, uint64_t height);
|
||||
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
} else
|
||||
fmt::print("{}{}\n", m_prefix, m_content);
|
||||
|
||||
m_content.clear();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
scoped_message_writer::~scoped_message_writer()
|
||||
{
|
||||
scoped_message_writer::~scoped_message_writer() {
|
||||
flush();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -28,40 +28,38 @@
|
|||
|
||||
#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:
|
||||
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:
|
||||
|
||||
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)}
|
||||
{}
|
||||
log::Level log_level = log::Level::info) :
|
||||
m_color{color}, m_log_level{log_level}, m_prefix{std::move(prefix)} {}
|
||||
|
||||
scoped_message_writer(scoped_message_writer&& o)
|
||||
: m_prefix{std::move(o.m_prefix)},
|
||||
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}
|
||||
{
|
||||
m_log_level{o.m_log_level} {
|
||||
o.m_content.clear();
|
||||
}
|
||||
|
||||
|
@ -72,8 +70,7 @@ public:
|
|||
/// 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)
|
||||
{
|
||||
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
|
||||
|
@ -84,10 +81,13 @@ public:
|
|||
/// 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); }
|
||||
|
||||
/// 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); }
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// Flushes the current message to output and resets it. This is normally not called explicitly
|
||||
/// but rather implicitly when the object is destroyed.
|
||||
|
@ -98,8 +98,8 @@ public:
|
|||
};
|
||||
|
||||
template <typename... T>
|
||||
scoped_message_writer msg_writer(std::optional<fmt::terminal_color> color = std::nullopt, T&&... args)
|
||||
{
|
||||
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)...);
|
||||
|
@ -107,12 +107,10 @@ scoped_message_writer msg_writer(std::optional<fmt::terminal_color> color = std:
|
|||
}
|
||||
|
||||
template <typename... T>
|
||||
scoped_message_writer msg_writer(std::string_view msg, 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,23 +120,20 @@ 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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
scoped_message_writer success_msg_writer(std::string_view msg, T&&... args) {
|
||||
return success_msg_writer(true, msg, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
|
@ -147,8 +142,7 @@ 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 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)...);
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
|
||||
#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;
|
||||
fs::ifstream f;
|
||||
|
@ -32,8 +31,7 @@ namespace tools {
|
|||
f.seekg(0, std::ios::beg);
|
||||
|
||||
std::array<unsigned char, 16384> buf;
|
||||
while (size_left)
|
||||
{
|
||||
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())
|
||||
|
@ -44,6 +42,6 @@ namespace tools {
|
|||
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,29 +1,26 @@
|
|||
#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
|
||||
/*! \brief Defines a singleton signal handler for win32 and *nix
|
||||
*/
|
||||
class signal_handler
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (r) {
|
||||
m_handler = t;
|
||||
}
|
||||
return r;
|
||||
|
@ -44,30 +41,28 @@ namespace tools {
|
|||
private:
|
||||
#if defined(WIN32)
|
||||
/*! \brief Handler for win */
|
||||
static BOOL WINAPI win_handler(DWORD type)
|
||||
{
|
||||
if (CTRL_C_EVENT == type || CTRL_BREAK_EVENT == type)
|
||||
{
|
||||
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);
|
||||
} 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)
|
||||
{
|
||||
static void posix_handler(int type) {
|
||||
handle_signal(type);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! \brief calles m_handler */
|
||||
static void handle_signal(int type)
|
||||
{
|
||||
static void handle_signal(int type) {
|
||||
static std::mutex m_mutex;
|
||||
std::unique_lock lock{m_mutex};
|
||||
m_handler(type);
|
||||
|
@ -75,6 +70,6 @@ namespace tools {
|
|||
|
||||
/*! \brief where the installed handler is stored */
|
||||
static inline std::function<void(int)> m_handler;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -27,30 +27,28 @@
|
|||
// 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);
|
||||
#else
|
||||
|
@ -60,8 +58,7 @@ static void closefrom(int fd)
|
|||
#else
|
||||
const int MAX_FDS = 65536;
|
||||
#endif
|
||||
while (fd < MAX_FDS)
|
||||
{
|
||||
while (fd < MAX_FDS) {
|
||||
close(fd);
|
||||
++fd;
|
||||
}
|
||||
|
@ -69,20 +66,27 @@ static void closefrom(int fd)
|
|||
}
|
||||
#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;
|
||||
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))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -92,21 +96,22 @@ int spawn(const fs::path& filename, const std::vector<std::string>& args, bool w
|
|||
CloseHandle(pi.hProcess);
|
||||
};
|
||||
|
||||
if (!wait)
|
||||
{
|
||||
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());
|
||||
if (result != WAIT_OBJECT_0) {
|
||||
log::error(
|
||||
logcat,
|
||||
"WaitForSingleObject failed. Result {}, error code {}",
|
||||
result,
|
||||
GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
DWORD exitCode;
|
||||
if (!GetExitCodeProcess(pi.hProcess, &exitCode))
|
||||
{
|
||||
if (!GetExitCodeProcess(pi.hProcess, &exitCode)) {
|
||||
log::error(logcat, "GetExitCodeProcess failed. Error code {}", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
@ -120,47 +125,40 @@ int spawn(const fs::path& filename, const std::vector<std::string>& args, bool w
|
|||
argv[args.size()] = NULL;
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0)
|
||||
{
|
||||
if (pid < 0) {
|
||||
log::error(logcat, "Error forking: {}", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// child
|
||||
if (pid == 0)
|
||||
{
|
||||
if (pid == 0) {
|
||||
tools::closefrom(3);
|
||||
close(0);
|
||||
char *envp[] = {NULL};
|
||||
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)
|
||||
{
|
||||
if (pid > 0) {
|
||||
if (!wait) {
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
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))
|
||||
{
|
||||
if (WIFEXITED(wstatus)) {
|
||||
log::info(logcat, "Child exited with {}", WEXITSTATUS(wstatus));
|
||||
return WEXITSTATUS(wstatus);
|
||||
}
|
||||
if (WIFSIGNALED(wstatus))
|
||||
{
|
||||
if (WIFSIGNALED(wstatus)) {
|
||||
log::info(logcat, "Child killed by {}", WEXITSTATUS(wstatus));
|
||||
return WEXITSTATUS(wstatus);
|
||||
}
|
||||
|
@ -171,4 +169,4 @@ int spawn(const fs::path& filename, const std::vector<std::string>& args, bool w
|
|||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -27,12 +27,12 @@
|
|||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#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,25 +1,24 @@
|
|||
#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> 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())
|
||||
{
|
||||
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))
|
||||
{
|
||||
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());
|
||||
|
@ -32,16 +31,16 @@ std::vector<std::string_view> split(std::string_view str, const std::string_view
|
|||
return results;
|
||||
}
|
||||
|
||||
std::vector<std::string_view> split_any(std::string_view str, const std::string_view delims, bool trim)
|
||||
{
|
||||
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))
|
||||
{
|
||||
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);
|
||||
size_t until = str.find_first_not_of(delims, pos + 1);
|
||||
if (until == std::string_view::npos)
|
||||
str.remove_prefix(str.size());
|
||||
else
|
||||
|
@ -55,8 +54,7 @@ std::vector<std::string_view> split_any(std::string_view str, const std::string_
|
|||
return results;
|
||||
}
|
||||
|
||||
void trim(std::string_view& s)
|
||||
{
|
||||
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
|
||||
|
@ -69,8 +67,7 @@ void trim(std::string_view& s)
|
|||
s.remove_suffix(s.size() - (pos + 1));
|
||||
}
|
||||
|
||||
std::string lowercase_ascii_string(std::string_view src)
|
||||
{
|
||||
std::string lowercase_ascii_string(std::string_view src) {
|
||||
std::string result;
|
||||
result.reserve(src.size());
|
||||
for (char ch : src)
|
||||
|
@ -114,4 +111,4 @@ std::string friendly_duration(std::chrono::nanoseconds dur) {
|
|||
return friendly;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
#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 <cstring>
|
||||
#include <iterator>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "epee/span.h" // epee
|
||||
|
||||
namespace tools {
|
||||
|
@ -16,7 +18,9 @@ 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::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
|
||||
|
@ -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,7 +62,8 @@ 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.
|
||||
|
@ -86,7 +92,8 @@ std::string join_transform(std::string_view delimiter, It begin, It end, UnaryOp
|
|||
|
||||
/// 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) {
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -133,10 +140,11 @@ 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>,
|
||||
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)};
|
||||
return {reinterpret_cast<const char*>(&val), sizeof(val)};
|
||||
}
|
||||
|
||||
/// Convenience wrapper around the above that also copies the result into a new string
|
||||
|
@ -148,8 +156,9 @@ std::string copy_guts(const T& 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>,
|
||||
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");
|
||||
|
@ -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)
|
||||
{
|
||||
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 {};
|
||||
if (it == end)
|
||||
return {};
|
||||
return std::string_view{*it}.substr(prefix.size());
|
||||
}
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -25,18 +25,17 @@
|
|||
// 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);
|
||||
|
@ -47,22 +46,21 @@ threadpool::~threadpool() {
|
|||
}
|
||||
|
||||
void threadpool::destroy() {
|
||||
try
|
||||
{
|
||||
try {
|
||||
const std::unique_lock lock{mutex};
|
||||
running = false;
|
||||
has_work.notify_all();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
} 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 */ }
|
||||
for (size_t i = 0; i < threads.size(); i++) {
|
||||
try {
|
||||
threads[i].join();
|
||||
} catch (...) { /* ignore */
|
||||
}
|
||||
}
|
||||
threads.clear();
|
||||
}
|
||||
|
@ -81,7 +79,7 @@ void threadpool::create(unsigned int max_threads) {
|
|||
}
|
||||
}
|
||||
|
||||
void threadpool::submit(waiter *obj, std::function<void()> f, bool leaf) {
|
||||
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)) {
|
||||
|
@ -108,30 +106,26 @@ unsigned int threadpool::get_max_concurrency() const {
|
|||
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");
|
||||
log::error(
|
||||
globallogcat, "wait should have been called before waiter dtor - waiting now");
|
||||
} catch (...) { /* ignore */
|
||||
}
|
||||
catch (...) { /* ignore */ }
|
||||
try
|
||||
{
|
||||
try {
|
||||
wait(NULL);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
} catch (const std::exception& e) {
|
||||
/* ignored */
|
||||
}
|
||||
}
|
||||
|
||||
void threadpool::waiter::wait(threadpool *tpool) {
|
||||
void threadpool::waiter::wait(threadpool* tpool) {
|
||||
if (tpool)
|
||||
tpool->run(true);
|
||||
std::unique_lock lock{mt};
|
||||
while(num)
|
||||
while (num)
|
||||
cv.wait(lock);
|
||||
}
|
||||
|
||||
|
@ -151,13 +145,13 @@ void threadpool::run(bool flush) {
|
|||
std::unique_lock lock{mutex};
|
||||
while (running) {
|
||||
entry e;
|
||||
while(queue.empty() && running)
|
||||
{
|
||||
while (queue.empty() && running) {
|
||||
if (flush)
|
||||
return;
|
||||
has_work.wait(lock);
|
||||
}
|
||||
if (!running) break;
|
||||
if (!running)
|
||||
break;
|
||||
|
||||
active++;
|
||||
e = std::move(queue.front());
|
||||
|
@ -175,4 +169,4 @@ void threadpool::run(bool flush) {
|
|||
active--;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -27,27 +27,25 @@
|
|||
// 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:
|
||||
class threadpool {
|
||||
public:
|
||||
static threadpool& getInstance() {
|
||||
static threadpool instance;
|
||||
return instance;
|
||||
}
|
||||
static threadpool *getNewForUnitTests(unsigned max_threads = 0) {
|
||||
static threadpool* getNewForUnitTests(unsigned max_threads = 0) {
|
||||
return new threadpool(max_threads);
|
||||
}
|
||||
|
||||
|
@ -57,18 +55,19 @@ public:
|
|||
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){}
|
||||
void wait(threadpool* tpool); //! Wait for a set of tasks to finish.
|
||||
waiter() : num(0) {}
|
||||
~waiter();
|
||||
};
|
||||
|
||||
// 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);
|
||||
void submit(waiter* waiter, std::function<void()> f, bool leaf = false);
|
||||
|
||||
// destroy and recreate threads
|
||||
void recycle();
|
||||
|
@ -84,7 +83,7 @@ public:
|
|||
void destroy();
|
||||
void create(unsigned int max_threads);
|
||||
typedef struct entry {
|
||||
waiter *wo;
|
||||
waiter* wo;
|
||||
std::function<void()> f;
|
||||
bool leaf;
|
||||
} entry;
|
||||
|
@ -98,4 +97,4 @@ public:
|
|||
void run(bool flush = false);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -34,30 +34,29 @@
|
|||
#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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
for (size_t i = 0; i != s; i++) {
|
||||
h_key k;
|
||||
hval v;
|
||||
a >> k;
|
||||
|
@ -66,27 +65,28 @@ namespace boost
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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_multimap<h_key, hval> &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
for (size_t i = 0; i != s; i++) {
|
||||
h_key k;
|
||||
hval v;
|
||||
a >> k;
|
||||
|
@ -95,49 +95,50 @@ namespace boost
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
template <class Archive, class hval>
|
||||
inline void save(Archive &a, const std::unordered_set<hval> &x, const boost::serialization::version_type 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)
|
||||
{
|
||||
for (auto& v : x) {
|
||||
a << v;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive, class hval>
|
||||
inline void load(Archive &a, std::unordered_set<hval> &x, const boost::serialization::version_type 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++)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
inline void serialize(
|
||||
Archive& a, std::unordered_set<hval>& x, const boost::serialization::version_type ver) {
|
||||
split_free(a, x, ver);
|
||||
}
|
||||
}
|
||||
}
|
||||
}} // namespace boost::serialization
|
||||
|
|
|
@ -29,54 +29,50 @@
|
|||
//
|
||||
// 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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (getrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
|
||||
log::error(logcat, "Failed to determine the lockable memory limit");
|
||||
return -1;
|
||||
}
|
||||
|
@ -84,26 +80,27 @@ namespace tools
|
|||
#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();
|
||||
unsigned hwc = std::thread::hardware_concurrency();
|
||||
|
@ -111,24 +108,23 @@ namespace tools
|
|||
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());
|
||||
|
@ -145,30 +141,32 @@ namespace tools
|
|||
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)
|
||||
{
|
||||
if (r) {
|
||||
return std::make_pair(major, minor);
|
||||
}
|
||||
else
|
||||
{
|
||||
} 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,7 +176,7 @@ 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);
|
||||
|
@ -187,20 +185,18 @@ namespace tools
|
|||
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 "{:%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");
|
||||
|
@ -213,11 +209,11 @@ namespace tools
|
|||
if (ts < 3600 * 24 * 365.25)
|
||||
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) {
|
||||
|
@ -229,12 +225,12 @@ namespace tools
|
|||
}
|
||||
}
|
||||
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
|
||||
|
|
|
@ -31,10 +31,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <oxenc/endian.h>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "crypto/hash.h"
|
||||
#include "cryptonote_config.h"
|
||||
|
@ -45,43 +46,45 @@
|
|||
*
|
||||
*
|
||||
*/
|
||||
namespace tools
|
||||
{
|
||||
bool disable_core_dumps();
|
||||
namespace tools {
|
||||
bool disable_core_dumps();
|
||||
|
||||
bool on_startup();
|
||||
bool on_startup();
|
||||
|
||||
ssize_t get_lockable_memory();
|
||||
ssize_t get_lockable_memory();
|
||||
|
||||
void set_max_concurrency(unsigned n);
|
||||
unsigned get_max_concurrency();
|
||||
void set_max_concurrency(unsigned n);
|
||||
unsigned get_max_concurrency();
|
||||
|
||||
bool is_local_address(const std::string &address);
|
||||
int vercmp(std::string_view v0, std::string_view v1); // returns < 0, 0, > 0, similar to strcmp, but more human friendly than lexical - does not attempt to validate
|
||||
bool is_local_address(const std::string& address);
|
||||
int vercmp(
|
||||
std::string_view v0,
|
||||
std::string_view v1); // returns < 0, 0, > 0, similar to strcmp, but more human friendly
|
||||
// than lexical - does not attempt to validate
|
||||
|
||||
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);
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string input_line_win();
|
||||
std::string input_line_win();
|
||||
#endif
|
||||
|
||||
std::string get_human_readable_timestamp(std::time_t ts);
|
||||
std::string get_human_readable_timespan(std::chrono::seconds seconds);
|
||||
std::string get_human_readable_bytes(uint64_t bytes);
|
||||
std::string get_human_readable_timestamp(std::time_t ts);
|
||||
std::string get_human_readable_timespan(std::chrono::seconds seconds);
|
||||
std::string get_human_readable_bytes(uint64_t bytes);
|
||||
|
||||
template <typename Duration, std::enable_if_t<!std::is_same_v<Duration, std::chrono::seconds>, int> = 0>
|
||||
std::string get_human_readable_timespan(Duration d)
|
||||
{
|
||||
template <
|
||||
typename Duration,
|
||||
std::enable_if_t<!std::is_same_v<Duration, std::chrono::seconds>, int> = 0>
|
||||
std::string get_human_readable_timespan(Duration d) {
|
||||
return get_human_readable_timespan(std::chrono::duration_cast<std::chrono::seconds>(d));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Duration>
|
||||
constexpr uint64_t to_seconds(Duration d)
|
||||
{
|
||||
template <typename Duration>
|
||||
constexpr uint64_t to_seconds(Duration d) {
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(d).count();
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
namespace detail {
|
||||
// Copy an integer type, swapping to little-endian if needed
|
||||
template <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
|
||||
void memcpy_one(char*& dest, T t) {
|
||||
|
@ -96,7 +99,8 @@ namespace tools
|
|||
static std::false_type check(...);
|
||||
};
|
||||
template <typename T>
|
||||
constexpr bool is_crypto_bytes_derived = decltype(crypto_bytes_base_helper::check((T*) nullptr))::value;
|
||||
constexpr bool is_crypto_bytes_derived =
|
||||
decltype(crypto_bytes_base_helper::check((T*)nullptr))::value;
|
||||
|
||||
// Copy the data out of a crypto::bytes<N, true>-derived type.
|
||||
template <typename T, std::enable_if_t<is_crypto_bytes_derived<T>, int> = 0>
|
||||
|
@ -106,11 +110,15 @@ namespace tools
|
|||
}
|
||||
|
||||
// Copy a class byte-for-byte (but only if it is standard layout and has byte alignment)
|
||||
template <typename T, std::enable_if_t<std::is_class<T>::value && !is_crypto_bytes_derived<T>, int> = 0>
|
||||
template <
|
||||
typename T,
|
||||
std::enable_if_t<std::is_class<T>::value && !is_crypto_bytes_derived<T>, int> = 0>
|
||||
void memcpy_one(char*& dest, const T& t) {
|
||||
// We don't *actually* require byte alignment here but it's quite possibly an error (i.e.
|
||||
// passing in a type containing integer members) so disallow it.
|
||||
static_assert(std::is_trivially_copyable<T>::value && alignof(T) == 1, "memcpy_le() may only be used on simple (1-byte alignment) struct types");
|
||||
static_assert(
|
||||
std::is_trivially_copyable<T>::value && alignof(T) == 1,
|
||||
"memcpy_le() may only be used on simple (1-byte alignment) struct types");
|
||||
std::memcpy(dest, &t, sizeof(T));
|
||||
dest += sizeof(T);
|
||||
}
|
||||
|
@ -118,7 +126,7 @@ namespace tools
|
|||
// Copy a string literal
|
||||
template <typename T, size_t N>
|
||||
void memcpy_one(char*& dest, const T (&arr)[N]) {
|
||||
for (const T &t : arr)
|
||||
for (const T& t : arr)
|
||||
memcpy_one(dest, t);
|
||||
}
|
||||
|
||||
|
@ -126,37 +134,38 @@ namespace tools
|
|||
constexpr size_t memcpy_size = sizeof(T);
|
||||
|
||||
template <typename T>
|
||||
inline constexpr size_t memcpy_size<T, std::enable_if_t<is_crypto_bytes_derived<T>>>
|
||||
= T::size();
|
||||
}
|
||||
inline constexpr size_t memcpy_size<T, std::enable_if_t<is_crypto_bytes_derived<T>>> =
|
||||
T::size();
|
||||
} // namespace detail
|
||||
|
||||
// Does a memcpy of one or more values into a char array; for any given values that are basic
|
||||
// integer types the value is first converted from native to little-endian representation (if
|
||||
// necessary). Non-integer types with alignment of 1 (typically meaning structs containing only
|
||||
// char, bools, and arrays of those) and fixed-size arrays of the above (including string
|
||||
// literals) are also permitted; more complex types are not.
|
||||
//
|
||||
// The 1-byte alignment is here to protect you: if you have a larger alignment that usually means
|
||||
// you have a contained type with a larger alignment, which is probably an integer.
|
||||
template <typename... T>
|
||||
auto memcpy_le(const T &...t) {
|
||||
// Does a memcpy of one or more values into a char array; for any given values that are basic
|
||||
// integer types the value is first converted from native to little-endian representation (if
|
||||
// necessary). Non-integer types with alignment of 1 (typically meaning structs containing only
|
||||
// char, bools, and arrays of those) and fixed-size arrays of the above (including string
|
||||
// literals) are also permitted; more complex types are not.
|
||||
//
|
||||
// The 1-byte alignment is here to protect you: if you have a larger alignment that usually means
|
||||
// you have a contained type with a larger alignment, which is probably an integer.
|
||||
template <typename... T>
|
||||
auto memcpy_le(const T&... t) {
|
||||
std::array<char, (0 + ... + detail::memcpy_size<T>)> r;
|
||||
char* dest = r.data();
|
||||
(..., detail::memcpy_one(dest, t));
|
||||
return r;
|
||||
}
|
||||
|
||||
// Returns the `_count` element of a scoped enum, cast to the enum's underlying type
|
||||
template <typename Enum>
|
||||
constexpr auto enum_count = static_cast<std::underlying_type_t<Enum>>(Enum::_count);
|
||||
|
||||
template <typename Enum>
|
||||
constexpr Enum enum_top = static_cast<Enum>(enum_count<Enum> - 1);
|
||||
|
||||
uint64_t cumulative_block_sync_weight(cryptonote::network_type nettype, uint64_t start_block, uint64_t num_blocks);
|
||||
|
||||
template <typename T, typename... Any>
|
||||
constexpr bool equals_any(const T& v, const Any&... any) {
|
||||
return (... || (v == any));
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the `_count` element of a scoped enum, cast to the enum's underlying type
|
||||
template <typename Enum>
|
||||
constexpr auto enum_count = static_cast<std::underlying_type_t<Enum>>(Enum::_count);
|
||||
|
||||
template <typename Enum>
|
||||
constexpr Enum enum_top = static_cast<Enum>(enum_count<Enum> - 1);
|
||||
|
||||
uint64_t cumulative_block_sync_weight(
|
||||
cryptonote::network_type nettype, uint64_t start_block, uint64_t num_blocks);
|
||||
|
||||
template <typename T, typename... Any>
|
||||
constexpr bool equals_any(const T& v, const Any&... any) {
|
||||
return (... || (v == any));
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -31,10 +31,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
|
||||
/*! \file varint.h
|
||||
* \brief provides the implementation of varint's
|
||||
|
@ -71,23 +71,26 @@
|
|||
|
||||
namespace tools {
|
||||
|
||||
/// Returned by read_varint in case of overflow
|
||||
constexpr int EVARINT_OVERFLOW = -1;
|
||||
/// Returned by read_varint in case of malformed varint representation
|
||||
constexpr int EVARINT_REPRESENT = -2;
|
||||
/// Returns by read_varint if the input range ends before we finish reading a value
|
||||
constexpr int EVARINT_TRUNCATED = -3;
|
||||
/// Returned by read_varint in case of overflow
|
||||
constexpr int EVARINT_OVERFLOW = -1;
|
||||
/// Returned by read_varint in case of malformed varint representation
|
||||
constexpr int EVARINT_REPRESENT = -2;
|
||||
/// Returns by read_varint if the input range ends before we finish reading a value
|
||||
constexpr int EVARINT_TRUNCATED = -3;
|
||||
|
||||
// Maximum number of bytes of a varint-encoded integer of the given type.
|
||||
template <typename T>
|
||||
constexpr size_t VARINT_MAX_LENGTH = (sizeof(T) * 8 + 6) / 7; // i.e. integer ceil(bits / 7)
|
||||
// Maximum number of bytes of a varint-encoded integer of the given type.
|
||||
template <typename T>
|
||||
constexpr size_t VARINT_MAX_LENGTH = (sizeof(T) * 8 + 6) / 7; // i.e. integer ceil(bits / 7)
|
||||
|
||||
/*! \brief writes a varint to an output iterator. Only supports unsigned integer types. You
|
||||
/*! \brief writes a varint to an output iterator. Only supports unsigned integer types. You
|
||||
* could static cast a signed type to an unsigned type, but note that any actual negative values
|
||||
* (which have a high bit set) will always end up full size since the high bit is set.
|
||||
*/
|
||||
template <typename OutputIt, typename T, std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
||||
void write_varint(OutputIt&& it, T i) {
|
||||
template <
|
||||
typename OutputIt,
|
||||
typename T,
|
||||
std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
||||
void write_varint(OutputIt&& it, T i) {
|
||||
#ifndef NDEBUG
|
||||
size_t count = 0;
|
||||
#endif
|
||||
|
@ -100,31 +103,33 @@ namespace tools {
|
|||
// Last byte (7 bits or less, no continuation bit)
|
||||
assert(++count <= VARINT_MAX_LENGTH<T>);
|
||||
*it++ = static_cast<char>(i);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Returns the string that represents the varint
|
||||
/*! \brief Returns the string that represents the varint
|
||||
*/
|
||||
template<typename T>
|
||||
std::string get_varint_data(const T& v)
|
||||
{
|
||||
template <typename T>
|
||||
std::string get_varint_data(const T& v) {
|
||||
std::string result;
|
||||
write_varint(std::back_insert_iterator{result}, v);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief reads in the varint that is pointed to by InputIt into write. `it` will modified to
|
||||
/*! \brief reads in the varint that is pointed to by InputIt into write. `it` will modified to
|
||||
* the position after the varint bytes that we read. Returns the number of bytes read on success,
|
||||
* or one of the negative EVARIANT_* constants on failure.
|
||||
*/
|
||||
template <typename It, typename EndIt, typename T, std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
||||
int read_varint(It&& it, const EndIt& end, T& write) {
|
||||
template <
|
||||
typename It,
|
||||
typename EndIt,
|
||||
typename T,
|
||||
std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
||||
int read_varint(It&& it, const EndIt& end, T& write) {
|
||||
constexpr size_t bits = sizeof(T) * 8;
|
||||
|
||||
bool more = true;
|
||||
int read = 0;
|
||||
write = 0;
|
||||
for (size_t shift = 0; more && it != end; shift += 7)
|
||||
{
|
||||
for (size_t shift = 0; more && it != end; shift += 7) {
|
||||
auto byte = static_cast<unsigned char>(*it++);
|
||||
++read;
|
||||
|
||||
|
@ -133,7 +138,8 @@ namespace tools {
|
|||
if (byte == 0 && shift)
|
||||
return EVARINT_REPRESENT;
|
||||
|
||||
// If we have <= 7 bits of space remaining then the value must fit and must not have a continuation bit
|
||||
// If we have <= 7 bits of space remaining then the value must fit and must not have a
|
||||
// continuation bit
|
||||
if (size_t bits_avail = bits - shift; bits_avail <= 7 && byte >= 1 << bits_avail)
|
||||
return EVARINT_OVERFLOW;
|
||||
|
||||
|
@ -143,21 +149,30 @@ namespace tools {
|
|||
}
|
||||
|
||||
return more ? EVARINT_TRUNCATED : read;
|
||||
}
|
||||
}
|
||||
|
||||
// Overloads to allow {read,write}_varint to be called with const iterators
|
||||
template <typename OutputIt, typename T, std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
||||
auto write_varint(const OutputIt& it_, T i) { return write_varint(OutputIt{it_}, i); }
|
||||
// Overloads to allow {read,write}_varint to be called with const iterators
|
||||
template <
|
||||
typename OutputIt,
|
||||
typename T,
|
||||
std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
||||
auto write_varint(const OutputIt& it_, T i) {
|
||||
return write_varint(OutputIt{it_}, i);
|
||||
}
|
||||
|
||||
template <typename It, typename T, std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
||||
auto read_varint(const It& it_, const It& end, T& i) { return read_varint(It{it_}, end, i); }
|
||||
template <
|
||||
typename It,
|
||||
typename T,
|
||||
std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
||||
auto read_varint(const It& it_, const It& end, T& i) {
|
||||
return read_varint(It{it_}, end, i);
|
||||
}
|
||||
|
||||
|
||||
/*! \brief reads the varint from an encoded string into `write`. Returns the number of bytes
|
||||
/*! \brief reads the varint from an encoded string into `write`. Returns the number of bytes
|
||||
* consumed, or an error value (as above).
|
||||
*/
|
||||
template <typename T, std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
||||
int read_varint(std::string_view s, T& write) {
|
||||
template <typename T, std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, int> = 0>
|
||||
int read_varint(std::string_view s, T& write) {
|
||||
return read_varint(s.begin(), s.end(), write);
|
||||
}
|
||||
}
|
||||
} // namespace tools
|
||||
|
|
|
@ -19,11 +19,11 @@ Issue Date: 20/12/2007
|
|||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "epee/int-util.h"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define TABLE_ALIGN 32
|
||||
|
@ -35,72 +35,95 @@ extern "C"
|
|||
#if defined(_MSC_VER)
|
||||
#define LOCAL_ALIGN __declspec(align(TABLE_ALIGN))
|
||||
#elif defined(__GNUC__)
|
||||
#define LOCAL_ALIGN __attribute__ ((aligned(16)))
|
||||
#define LOCAL_ALIGN __attribute__((aligned(16)))
|
||||
#else
|
||||
#define LOCAL_ALIGN
|
||||
#endif
|
||||
|
||||
#define rf1(r,c) (r)
|
||||
#define word_in(x,c) (*((uint32_t*)(x)+(c)))
|
||||
#define word_out(x,c,v) (*((uint32_t*)(x)+(c)) = (v))
|
||||
#define rf1(r, c) (r)
|
||||
#define word_in(x, c) (*((uint32_t*)(x) + (c)))
|
||||
#define word_out(x, c, v) (*((uint32_t*)(x) + (c)) = (v))
|
||||
|
||||
#define s(x,c) x[c]
|
||||
#define si(y,x,c) (s(y,c) = word_in(x, c))
|
||||
#define so(y,x,c) word_out(y, c, s(x,c))
|
||||
#define state_in(y,x) si(y,x,0); si(y,x,1); si(y,x,2); si(y,x,3)
|
||||
#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3)
|
||||
#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3)
|
||||
#define to_byte(x) ((x) & 0xff)
|
||||
#define bval(x,n) to_byte(SWAP32LE(x) >> (8 * (n)))
|
||||
#define s(x, c) x[c]
|
||||
#define si(y, x, c) (s(y, c) = word_in(x, c))
|
||||
#define so(y, x, c) word_out(y, c, s(x, c))
|
||||
#define state_in(y, x) \
|
||||
si(y, x, 0); \
|
||||
si(y, x, 1); \
|
||||
si(y, x, 2); \
|
||||
si(y, x, 3)
|
||||
#define state_out(y, x) \
|
||||
so(y, x, 0); \
|
||||
so(y, x, 1); \
|
||||
so(y, x, 2); \
|
||||
so(y, x, 3)
|
||||
#define round(rm, y, x, k) \
|
||||
rm(y, x, k, 0); \
|
||||
rm(y, x, k, 1); \
|
||||
rm(y, x, k, 2); \
|
||||
rm(y, x, k, 3)
|
||||
#define to_byte(x) ((x)&0xff)
|
||||
#define bval(x, n) to_byte(SWAP32LE(x) >> (8 * (n)))
|
||||
|
||||
#define fwd_var(x,r,c)\
|
||||
( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\
|
||||
: r == 1 ? ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0))\
|
||||
: r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\
|
||||
: ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2)))
|
||||
#define fwd_var(x, r, c) \
|
||||
(r == 0 ? (c == 0 ? s(x, 0) \
|
||||
: c == 1 ? s(x, 1) \
|
||||
: c == 2 ? s(x, 2) \
|
||||
: s(x, 3)) \
|
||||
: r == 1 ? (c == 0 ? s(x, 1) \
|
||||
: c == 1 ? s(x, 2) \
|
||||
: c == 2 ? s(x, 3) \
|
||||
: s(x, 0)) \
|
||||
: r == 2 ? (c == 0 ? s(x, 2) \
|
||||
: c == 1 ? s(x, 3) \
|
||||
: c == 2 ? s(x, 0) \
|
||||
: s(x, 1)) \
|
||||
: (c == 0 ? s(x, 3) \
|
||||
: c == 1 ? s(x, 0) \
|
||||
: c == 2 ? s(x, 1) \
|
||||
: s(x, 2)))
|
||||
|
||||
#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ SWAP32LE(four_tables(x,t_use(f,n),fwd_var,rf1,c)))
|
||||
#define fwd_rnd(y, x, k, c) \
|
||||
(s(y, c) = (k)[c] ^ SWAP32LE(four_tables(x, t_use(f, n), fwd_var, rf1, c)))
|
||||
|
||||
#define sb_data(w) {\
|
||||
w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\
|
||||
w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\
|
||||
w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\
|
||||
w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\
|
||||
w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\
|
||||
w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\
|
||||
w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\
|
||||
w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\
|
||||
w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\
|
||||
w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\
|
||||
w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\
|
||||
w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\
|
||||
w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\
|
||||
w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\
|
||||
w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\
|
||||
w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\
|
||||
w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\
|
||||
w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\
|
||||
w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\
|
||||
w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\
|
||||
w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\
|
||||
w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\
|
||||
w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\
|
||||
w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\
|
||||
w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\
|
||||
w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\
|
||||
w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\
|
||||
w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\
|
||||
w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\
|
||||
w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\
|
||||
w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\
|
||||
w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) }
|
||||
#define sb_data(w) \
|
||||
{ \
|
||||
w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5), w(0x30), w(0x01), \
|
||||
w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76), w(0xca), w(0x82), w(0xc9), \
|
||||
w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0), w(0xad), w(0xd4), w(0xa2), w(0xaf), \
|
||||
w(0x9c), w(0xa4), w(0x72), w(0xc0), w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), \
|
||||
w(0x3f), w(0xf7), w(0xcc), w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), \
|
||||
w(0x31), w(0x15), w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), \
|
||||
w(0x9a), w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75), \
|
||||
w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0), w(0x52), \
|
||||
w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84), w(0x53), w(0xd1), \
|
||||
w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b), w(0x6a), w(0xcb), w(0xbe), \
|
||||
w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf), w(0xd0), w(0xef), w(0xaa), w(0xfb), \
|
||||
w(0x43), w(0x4d), w(0x33), w(0x85), w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), \
|
||||
w(0x3c), w(0x9f), w(0xa8), w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), \
|
||||
w(0x38), w(0xf5), w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), \
|
||||
w(0xd2), w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17), \
|
||||
w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73), w(0x60), \
|
||||
w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88), w(0x46), w(0xee), \
|
||||
w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb), w(0xe0), w(0x32), w(0x3a), \
|
||||
w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c), w(0xc2), w(0xd3), w(0xac), w(0x62), \
|
||||
w(0x91), w(0x95), w(0xe4), w(0x79), w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), \
|
||||
w(0xd5), w(0x4e), w(0xa9), w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), \
|
||||
w(0xae), w(0x08), w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), \
|
||||
w(0xc6), w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a), \
|
||||
w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e), w(0x61), \
|
||||
w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e), w(0xe1), w(0xf8), \
|
||||
w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94), w(0x9b), w(0x1e), w(0x87), \
|
||||
w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf), w(0x8c), w(0xa1), w(0x89), w(0x0d), \
|
||||
w(0xbf), w(0xe6), w(0x42), w(0x68), w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), \
|
||||
w(0x54), w(0xbb), w(0x16) \
|
||||
}
|
||||
|
||||
#define rc_data(w) {\
|
||||
w(0x01), w(0x02), w(0x04), w(0x08), w(0x10),w(0x20), w(0x40), w(0x80),\
|
||||
w(0x1b), w(0x36) }
|
||||
#define rc_data(w) \
|
||||
{ w(0x01), w(0x02), w(0x04), w(0x08), w(0x10), w(0x20), w(0x40), w(0x80), w(0x1b), w(0x36) }
|
||||
|
||||
#define bytes2word(b0, b1, b2, b3) (((uint32_t)(b3) << 24) | \
|
||||
((uint32_t)(b2) << 16) | ((uint32_t)(b1) << 8) | (b0))
|
||||
#define bytes2word(b0, b1, b2, b3) \
|
||||
(((uint32_t)(b3) << 24) | ((uint32_t)(b2) << 16) | ((uint32_t)(b1) << 8) | (b0))
|
||||
|
||||
#define h0(x) (x)
|
||||
#define w0(p) bytes2word(p, 0, 0, 0)
|
||||
|
@ -118,28 +141,27 @@ extern "C"
|
|||
#define v2(p) bytes2word(fd(p), fb(p), fe(p), f9(p))
|
||||
#define v3(p) bytes2word(f9(p), fd(p), fb(p), fe(p))
|
||||
|
||||
#define f2(x) ((x<<1) ^ (((x>>7) & 1) * WPOLY))
|
||||
#define f4(x) ((x<<2) ^ (((x>>6) & 1) * WPOLY) ^ (((x>>6) & 2) * WPOLY))
|
||||
#define f8(x) ((x<<3) ^ (((x>>5) & 1) * WPOLY) ^ (((x>>5) & 2) * WPOLY) ^ (((x>>5) & 4) * WPOLY))
|
||||
#define f2(x) ((x << 1) ^ (((x >> 7) & 1) * WPOLY))
|
||||
#define f4(x) ((x << 2) ^ (((x >> 6) & 1) * WPOLY) ^ (((x >> 6) & 2) * WPOLY))
|
||||
#define f8(x) \
|
||||
((x << 3) ^ (((x >> 5) & 1) * WPOLY) ^ (((x >> 5) & 2) * WPOLY) ^ (((x >> 5) & 4) * WPOLY))
|
||||
#define f3(x) (f2(x) ^ x)
|
||||
#define f9(x) (f8(x) ^ x)
|
||||
#define fb(x) (f8(x) ^ f2(x) ^ x)
|
||||
#define fd(x) (f8(x) ^ f4(x) ^ x)
|
||||
#define fe(x) (f8(x) ^ f4(x) ^ f2(x))
|
||||
|
||||
#define t_dec(m,n) t_##m##n
|
||||
#define t_set(m,n) t_##m##n
|
||||
#define t_use(m,n) t_##m##n
|
||||
#define t_dec(m, n) t_##m##n
|
||||
#define t_set(m, n) t_##m##n
|
||||
#define t_use(m, n) t_##m##n
|
||||
|
||||
#define d_4(t,n,b,e,f,g,h) LOCAL_ALIGN const t n[4][256] = { b(e), b(f), b(g), b(h) }
|
||||
#define d_4(t, n, b, e, f, g, h) LOCAL_ALIGN const t n[4][256] = {b(e), b(f), b(g), b(h)}
|
||||
|
||||
#define four_tables(x,tab,vf,rf,c) \
|
||||
(tab[0][bval(vf(x,0,c),rf(0,c))] \
|
||||
^ tab[1][bval(vf(x,1,c),rf(1,c))] \
|
||||
^ tab[2][bval(vf(x,2,c),rf(2,c))] \
|
||||
^ tab[3][bval(vf(x,3,c),rf(3,c))])
|
||||
#define four_tables(x, tab, vf, rf, c) \
|
||||
(tab[0][bval(vf(x, 0, c), rf(0, c))] ^ tab[1][bval(vf(x, 1, c), rf(1, c))] ^ \
|
||||
tab[2][bval(vf(x, 2, c), rf(2, c))] ^ tab[3][bval(vf(x, 3, c), rf(3, c))])
|
||||
|
||||
d_4(uint32_t, t_dec(f,n), sb_data, u0, u1, u2, u3);
|
||||
d_4(uint32_t, t_dec(f, n), sb_data, u0, u1, u2, u3);
|
||||
|
||||
#if !defined(STATIC)
|
||||
#define STATIC
|
||||
|
@ -149,10 +171,9 @@ d_4(uint32_t, t_dec(f,n), sb_data, u0, u1, u2, u3);
|
|||
#define INLINE
|
||||
#endif
|
||||
|
||||
STATIC INLINE void aesb_single_round(const uint8_t *in, uint8_t *out, uint8_t *expandedKey)
|
||||
{
|
||||
STATIC INLINE void aesb_single_round(const uint8_t* in, uint8_t* out, uint8_t* expandedKey) {
|
||||
uint32_t b0[4], b1[4];
|
||||
const uint32_t *kp = (uint32_t *) expandedKey;
|
||||
const uint32_t* kp = (uint32_t*)expandedKey;
|
||||
state_in(b0, in);
|
||||
|
||||
round(fwd_rnd, b1, b0, kp);
|
||||
|
@ -160,10 +181,9 @@ STATIC INLINE void aesb_single_round(const uint8_t *in, uint8_t *out, uint8_t *e
|
|||
state_out(out, b1);
|
||||
}
|
||||
|
||||
STATIC INLINE void aesb_pseudo_round(const uint8_t *in, uint8_t *out, uint8_t *expandedKey)
|
||||
{
|
||||
STATIC INLINE void aesb_pseudo_round(const uint8_t* in, uint8_t* out, uint8_t* expandedKey) {
|
||||
uint32_t b0[4], b1[4];
|
||||
const uint32_t *kp = (uint32_t *) expandedKey;
|
||||
const uint32_t* kp = (uint32_t*)expandedKey;
|
||||
state_in(b0, in);
|
||||
|
||||
round(fwd_rnd, b1, b0, kp);
|
||||
|
@ -180,7 +200,6 @@ STATIC INLINE void aesb_pseudo_round(const uint8_t *in, uint8_t *out, uint8_t *e
|
|||
state_out(out, b0);
|
||||
}
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/format.h"
|
||||
#include "common/formattable.h"
|
||||
#include "common/hex.h"
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
namespace crypto {
|
||||
|
||||
/// constexpr null (all-0) value for various crypto types; use as `crypto::null<crypto::whatever>`.
|
||||
template <typename T, typename = std::enable_if_t<std::is_standard_layout_v<T> && std::is_default_constructible_v<T>>>
|
||||
constexpr T null{};
|
||||
/// constexpr null (all-0) value for various crypto types; use as `crypto::null<crypto::whatever>`.
|
||||
template <
|
||||
typename T,
|
||||
typename = std::enable_if_t<
|
||||
std::is_standard_layout_v<T> && std::is_default_constructible_v<T>>>
|
||||
constexpr T null{};
|
||||
|
||||
// Base type for fixed-byte quantities (points, scalars, signatures, hashes). The bool controls
|
||||
// whether the type should have ==, !=, std::hash, and to_hex_string.
|
||||
template <size_t Bytes, bool MemcmpHashHex = false>
|
||||
struct alignas(size_t) bytes {
|
||||
// Base type for fixed-byte quantities (points, scalars, signatures, hashes). The bool controls
|
||||
// whether the type should have ==, !=, std::hash, and to_hex_string.
|
||||
template <size_t Bytes, bool MemcmpHashHex = false>
|
||||
struct alignas(size_t) bytes {
|
||||
std::array<unsigned char, Bytes> data_;
|
||||
|
||||
unsigned char* data() { return data_.data(); }
|
||||
|
@ -36,47 +39,49 @@ namespace crypto {
|
|||
const unsigned char& operator[](size_t i) const { return data_[i]; }
|
||||
|
||||
static constexpr bool compare_hash_hex = MemcmpHashHex;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T, typename = void>
|
||||
constexpr bool has_compare_hash_hex = false;
|
||||
template <typename T>
|
||||
inline constexpr bool has_compare_hash_hex<T, std::enable_if_t<T::compare_hash_hex>> = true;
|
||||
template <typename T, typename = void>
|
||||
constexpr bool has_compare_hash_hex = false;
|
||||
template <typename T>
|
||||
inline constexpr bool has_compare_hash_hex<T, std::enable_if_t<T::compare_hash_hex>> = true;
|
||||
|
||||
template <typename Left, typename Right, typename = void>
|
||||
constexpr bool are_comparable_v = false;
|
||||
template <typename L, typename R>
|
||||
inline constexpr bool are_comparable_v<L, R, std::enable_if_t<std::is_same_v<L, R> && has_compare_hash_hex<L>>> = true;
|
||||
template <typename Left, typename Right, typename = void>
|
||||
constexpr bool are_comparable_v = false;
|
||||
template <typename L, typename R>
|
||||
inline constexpr bool
|
||||
are_comparable_v<L, R, std::enable_if_t<std::is_same_v<L, R> && has_compare_hash_hex<L>>> =
|
||||
true;
|
||||
|
||||
template <typename L, typename R, std::enable_if_t<are_comparable_v<L, R>, int> = 0>
|
||||
bool operator==(const L& left, const R& right) {
|
||||
template <typename L, typename R, std::enable_if_t<are_comparable_v<L, R>, int> = 0>
|
||||
bool operator==(const L& left, const R& right) {
|
||||
return left.data_ == right.data_;
|
||||
}
|
||||
template <typename L, typename R, std::enable_if_t<are_comparable_v<L, R>, int> = 0>
|
||||
bool operator!=(const L& left, const R& right) {
|
||||
}
|
||||
template <typename L, typename R, std::enable_if_t<are_comparable_v<L, R>, int> = 0>
|
||||
bool operator!=(const L& left, const R& right) {
|
||||
return left.data_ != right.data_;
|
||||
}
|
||||
template <typename L, typename R, std::enable_if_t<are_comparable_v<L, R>, int> = 0>
|
||||
bool operator<(const L& left, const R& right) {
|
||||
}
|
||||
template <typename L, typename R, std::enable_if_t<are_comparable_v<L, R>, int> = 0>
|
||||
bool operator<(const L& left, const R& right) {
|
||||
return left.data_ < right.data_;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename = std::enable_if_t<has_compare_hash_hex<T>>>
|
||||
std::string to_hex_string(const T& val) { return "<{}>"_format(tools::type_to_hex(val)); }
|
||||
template <typename T, typename = std::enable_if_t<has_compare_hash_hex<T>>>
|
||||
std::string to_hex_string(const T& val) {
|
||||
return "<{}>"_format(tools::type_to_hex(val));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct raw_hasher {
|
||||
template <typename T>
|
||||
struct raw_hasher {
|
||||
static_assert(T::compare_hash_hex);
|
||||
static_assert(std::is_standard_layout_v<T>);
|
||||
static_assert(sizeof(T) >= sizeof(size_t));
|
||||
static_assert(alignof(T) >= sizeof(size_t));
|
||||
|
||||
size_t operator()(const T& val) const {
|
||||
return *reinterpret_cast<const size_t*>(val.data());
|
||||
}
|
||||
};
|
||||
}
|
||||
size_t operator()(const T& val) const { return *reinterpret_cast<const size_t*>(val.data()); }
|
||||
};
|
||||
} // namespace crypto
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool formattable::via_to_hex_string<T, std::enable_if_t<crypto::has_compare_hash_hex<T>>>
|
||||
= true;
|
||||
inline constexpr bool
|
||||
formattable::via_to_hex_string<T, std::enable_if_t<crypto::has_compare_hash_hex<T>>> = true;
|
||||
|
|
|
@ -37,66 +37,81 @@
|
|||
* HMAC is specified by RFC 2104.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include "epee/memwipe.h"
|
||||
#include "blake256.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "epee/memwipe.h"
|
||||
|
||||
#define U8TO32(p) \
|
||||
(((uint32_t)((p)[0]) << 24) | ((uint32_t)((p)[1]) << 16) | \
|
||||
((uint32_t)((p)[2]) << 8) | ((uint32_t)((p)[3]) ))
|
||||
(((uint32_t)((p)[0]) << 24) | ((uint32_t)((p)[1]) << 16) | ((uint32_t)((p)[2]) << 8) | \
|
||||
((uint32_t)((p)[3])))
|
||||
#define U32TO8(p, v) \
|
||||
(p)[0] = (uint8_t)((v) >> 24); (p)[1] = (uint8_t)((v) >> 16); \
|
||||
(p)[2] = (uint8_t)((v) >> 8); (p)[3] = (uint8_t)((v) );
|
||||
(p)[0] = (uint8_t)((v) >> 24); \
|
||||
(p)[1] = (uint8_t)((v) >> 16); \
|
||||
(p)[2] = (uint8_t)((v) >> 8); \
|
||||
(p)[3] = (uint8_t)((v));
|
||||
|
||||
const uint8_t sigma[][16] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15},
|
||||
{14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3},
|
||||
{11, 8,12, 0, 5, 2,15,13,10,14, 3, 6, 7, 1, 9, 4},
|
||||
{ 7, 9, 3, 1,13,12,11,14, 2, 6, 5,10, 4, 0,15, 8},
|
||||
{ 9, 0, 5, 7, 2, 4,10,15,14, 1,11,12, 6, 8, 3,13},
|
||||
{ 2,12, 6,10, 0,11, 8, 3, 4,13, 7, 5,15,14, 1, 9},
|
||||
{12, 5, 1,15,14,13, 4,10, 0, 7, 6, 3, 9, 2, 8,11},
|
||||
{13,11, 7,14,12, 1, 3, 9, 5, 0,15, 4, 8, 6, 2,10},
|
||||
{ 6,15,14, 9,11, 3, 0, 8,12, 2,13, 7, 1, 4,10, 5},
|
||||
{10, 2, 8, 4, 7, 6, 1, 5,15,11, 9,14, 3,12,13, 0},
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15},
|
||||
{14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3},
|
||||
{11, 8,12, 0, 5, 2,15,13,10,14, 3, 6, 7, 1, 9, 4},
|
||||
{ 7, 9, 3, 1,13,12,11,14, 2, 6, 5,10, 4, 0,15, 8}
|
||||
};
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
|
||||
{11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
|
||||
{7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
|
||||
{9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
|
||||
{2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
|
||||
{12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
|
||||
{13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
|
||||
{6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
|
||||
{10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0},
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
|
||||
{11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
|
||||
{7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}};
|
||||
|
||||
const uint32_t cst[16] = {
|
||||
0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344,
|
||||
0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89,
|
||||
0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,
|
||||
0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917
|
||||
};
|
||||
0x243F6A88,
|
||||
0x85A308D3,
|
||||
0x13198A2E,
|
||||
0x03707344,
|
||||
0xA4093822,
|
||||
0x299F31D0,
|
||||
0x082EFA98,
|
||||
0xEC4E6C89,
|
||||
0x452821E6,
|
||||
0x38D01377,
|
||||
0xBE5466CF,
|
||||
0x34E90C6C,
|
||||
0xC0AC29B7,
|
||||
0xC97C50DD,
|
||||
0x3F84D5B5,
|
||||
0xB5470917};
|
||||
|
||||
static const uint8_t padding[] = {
|
||||
0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
};
|
||||
static const uint8_t padding[] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
|
||||
void blake256_compress(state *S, const uint8_t *block) {
|
||||
void blake256_compress(state* S, const uint8_t* block) {
|
||||
uint32_t v[16], m[16], i;
|
||||
|
||||
#define ROT(x,n) (((x)<<(32-n))|((x)>>(n)))
|
||||
#define G(a,b,c,d,e) \
|
||||
v[a] += (m[sigma[i][e]] ^ cst[sigma[i][e+1]]) + v[b]; \
|
||||
v[d] = ROT(v[d] ^ v[a],16); \
|
||||
#define ROT(x, n) (((x) << (32 - n)) | ((x) >> (n)))
|
||||
#define G(a, b, c, d, e) \
|
||||
v[a] += (m[sigma[i][e]] ^ cst[sigma[i][e + 1]]) + v[b]; \
|
||||
v[d] = ROT(v[d] ^ v[a], 16); \
|
||||
v[c] += v[d]; \
|
||||
v[b] = ROT(v[b] ^ v[c],12); \
|
||||
v[a] += (m[sigma[i][e+1]] ^ cst[sigma[i][e]])+v[b]; \
|
||||
v[b] = ROT(v[b] ^ v[c], 12); \
|
||||
v[a] += (m[sigma[i][e + 1]] ^ cst[sigma[i][e]]) + v[b]; \
|
||||
v[d] = ROT(v[d] ^ v[a], 8); \
|
||||
v[c] += v[d]; \
|
||||
v[b] = ROT(v[b] ^ v[c], 7);
|
||||
|
||||
for (i = 0; i < 16; ++i) m[i] = U8TO32(block + i * 4);
|
||||
for (i = 0; i < 8; ++i) v[i] = S->h[i];
|
||||
v[ 8] = S->s[0] ^ 0x243F6A88;
|
||||
v[ 9] = S->s[1] ^ 0x85A308D3;
|
||||
for (i = 0; i < 16; ++i)
|
||||
m[i] = U8TO32(block + i * 4);
|
||||
for (i = 0; i < 8; ++i)
|
||||
v[i] = S->h[i];
|
||||
v[8] = S->s[0] ^ 0x243F6A88;
|
||||
v[9] = S->s[1] ^ 0x85A308D3;
|
||||
v[10] = S->s[2] ^ 0x13198A2E;
|
||||
v[11] = S->s[3] ^ 0x03707344;
|
||||
v[12] = 0xA4093822;
|
||||
|
@ -122,11 +137,13 @@ void blake256_compress(state *S, const uint8_t *block) {
|
|||
G(1, 6, 11, 12, 10);
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; ++i) S->h[i % 8] ^= v[i];
|
||||
for (i = 0; i < 8; ++i) S->h[i] ^= S->s[i % 4];
|
||||
for (i = 0; i < 16; ++i)
|
||||
S->h[i % 8] ^= v[i];
|
||||
for (i = 0; i < 8; ++i)
|
||||
S->h[i] ^= S->s[i % 4];
|
||||
}
|
||||
|
||||
void blake256_init(state *S) {
|
||||
void blake256_init(state* S) {
|
||||
S->h[0] = 0x6A09E667;
|
||||
S->h[1] = 0xBB67AE85;
|
||||
S->h[2] = 0x3C6EF372;
|
||||
|
@ -139,7 +156,7 @@ void blake256_init(state *S) {
|
|||
S->s[0] = S->s[1] = S->s[2] = S->s[3] = 0;
|
||||
}
|
||||
|
||||
void blake224_init(state *S) {
|
||||
void blake224_init(state* S) {
|
||||
S->h[0] = 0xC1059ED8;
|
||||
S->h[1] = 0x367CD507;
|
||||
S->h[2] = 0x3070DD17;
|
||||
|
@ -153,14 +170,15 @@ void blake224_init(state *S) {
|
|||
}
|
||||
|
||||
// datalen = number of bits
|
||||
void blake256_update(state *S, const uint8_t *data, uint64_t datalen) {
|
||||
void blake256_update(state* S, const uint8_t* data, uint64_t datalen) {
|
||||
int left = S->buflen >> 3;
|
||||
int fill = 64 - left;
|
||||
|
||||
if (left && (((datalen >> 3)) >= (unsigned) fill)) {
|
||||
memcpy((void *) (S->buf + left), (void *) data, fill);
|
||||
if (left && (((datalen >> 3)) >= (unsigned)fill)) {
|
||||
memcpy((void*)(S->buf + left), (void*)data, fill);
|
||||
S->t[0] += 512;
|
||||
if (S->t[0] == 0) S->t[1]++;
|
||||
if (S->t[0] == 0)
|
||||
S->t[1]++;
|
||||
blake256_compress(S, S->buf);
|
||||
data += fill;
|
||||
datalen -= (fill << 3);
|
||||
|
@ -169,14 +187,15 @@ void blake256_update(state *S, const uint8_t *data, uint64_t datalen) {
|
|||
|
||||
while (datalen >= 512) {
|
||||
S->t[0] += 512;
|
||||
if (S->t[0] == 0) S->t[1]++;
|
||||
if (S->t[0] == 0)
|
||||
S->t[1]++;
|
||||
blake256_compress(S, data);
|
||||
data += 64;
|
||||
datalen -= 512;
|
||||
}
|
||||
|
||||
if (datalen > 0) {
|
||||
memcpy((void *) (S->buf + left), (void *) data, datalen >> 3);
|
||||
memcpy((void*)(S->buf + left), (void*)data, datalen >> 3);
|
||||
S->buflen = (left << 3) + datalen;
|
||||
} else {
|
||||
S->buflen = 0;
|
||||
|
@ -184,14 +203,15 @@ void blake256_update(state *S, const uint8_t *data, uint64_t datalen) {
|
|||
}
|
||||
|
||||
// datalen = number of bits
|
||||
void blake224_update(state *S, const uint8_t *data, uint64_t datalen) {
|
||||
void blake224_update(state* S, const uint8_t* data, uint64_t datalen) {
|
||||
blake256_update(S, data, datalen);
|
||||
}
|
||||
|
||||
void blake256_final_h(state *S, uint8_t *digest, uint8_t pa, uint8_t pb) {
|
||||
void blake256_final_h(state* S, uint8_t* digest, uint8_t pa, uint8_t pb) {
|
||||
uint8_t msglen[8];
|
||||
uint32_t lo = S->t[0] + S->buflen, hi = S->t[1];
|
||||
if (lo < (unsigned) S->buflen) hi++;
|
||||
if (lo < (unsigned)S->buflen)
|
||||
hi++;
|
||||
U32TO8(msglen + 0, hi);
|
||||
U32TO8(msglen + 4, lo);
|
||||
|
||||
|
@ -200,7 +220,8 @@ void blake256_final_h(state *S, uint8_t *digest, uint8_t pa, uint8_t pb) {
|
|||
blake256_update(S, &pa, 8);
|
||||
} else {
|
||||
if (S->buflen < 440) { /* enough space to fill the block */
|
||||
if (S->buflen == 0) S->nullt = 1;
|
||||
if (S->buflen == 0)
|
||||
S->nullt = 1;
|
||||
S->t[0] -= 440 - S->buflen;
|
||||
blake256_update(S, padding, 440 - S->buflen);
|
||||
} else { /* need 2 compressions */
|
||||
|
@ -226,16 +247,16 @@ void blake256_final_h(state *S, uint8_t *digest, uint8_t pa, uint8_t pb) {
|
|||
U32TO8(digest + 28, S->h[7]);
|
||||
}
|
||||
|
||||
void blake256_final(state *S, uint8_t *digest) {
|
||||
void blake256_final(state* S, uint8_t* digest) {
|
||||
blake256_final_h(S, digest, 0x81, 0x01);
|
||||
}
|
||||
|
||||
void blake224_final(state *S, uint8_t *digest) {
|
||||
void blake224_final(state* S, uint8_t* digest) {
|
||||
blake256_final_h(S, digest, 0x80, 0x00);
|
||||
}
|
||||
|
||||
// inlen = number of bytes
|
||||
void blake256_hash(uint8_t *out, const uint8_t *in, uint64_t inlen) {
|
||||
void blake256_hash(uint8_t* out, const uint8_t* in, uint64_t inlen) {
|
||||
state S;
|
||||
blake256_init(&S);
|
||||
blake256_update(&S, in, inlen * 8);
|
||||
|
@ -243,7 +264,7 @@ void blake256_hash(uint8_t *out, const uint8_t *in, uint64_t inlen) {
|
|||
}
|
||||
|
||||
// inlen = number of bytes
|
||||
void blake224_hash(uint8_t *out, const uint8_t *in, uint64_t inlen) {
|
||||
void blake224_hash(uint8_t* out, const uint8_t* in, uint64_t inlen) {
|
||||
state S;
|
||||
blake224_init(&S);
|
||||
blake224_update(&S, in, inlen * 8);
|
||||
|
@ -251,8 +272,8 @@ void blake224_hash(uint8_t *out, const uint8_t *in, uint64_t inlen) {
|
|||
}
|
||||
|
||||
// keylen = number of bytes
|
||||
void hmac_blake256_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) {
|
||||
const uint8_t *key = _key;
|
||||
void hmac_blake256_init(hmac_state* S, const uint8_t* _key, uint64_t keylen) {
|
||||
const uint8_t* key = _key;
|
||||
uint8_t keyhash[32];
|
||||
uint8_t pad[64];
|
||||
uint64_t i;
|
||||
|
@ -281,8 +302,8 @@ void hmac_blake256_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) {
|
|||
}
|
||||
|
||||
// keylen = number of bytes
|
||||
void hmac_blake224_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) {
|
||||
const uint8_t *key = _key;
|
||||
void hmac_blake224_init(hmac_state* S, const uint8_t* _key, uint64_t keylen) {
|
||||
const uint8_t* key = _key;
|
||||
uint8_t keyhash[32];
|
||||
uint8_t pad[64];
|
||||
uint64_t i;
|
||||
|
@ -311,18 +332,18 @@ void hmac_blake224_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) {
|
|||
}
|
||||
|
||||
// datalen = number of bits
|
||||
void hmac_blake256_update(hmac_state *S, const uint8_t *data, uint64_t datalen) {
|
||||
void hmac_blake256_update(hmac_state* S, const uint8_t* data, uint64_t datalen) {
|
||||
// update the inner state
|
||||
blake256_update(&S->inner, data, datalen);
|
||||
}
|
||||
|
||||
// datalen = number of bits
|
||||
void hmac_blake224_update(hmac_state *S, const uint8_t *data, uint64_t datalen) {
|
||||
void hmac_blake224_update(hmac_state* S, const uint8_t* data, uint64_t datalen) {
|
||||
// update the inner state
|
||||
blake224_update(&S->inner, data, datalen);
|
||||
}
|
||||
|
||||
void hmac_blake256_final(hmac_state *S, uint8_t *digest) {
|
||||
void hmac_blake256_final(hmac_state* S, uint8_t* digest) {
|
||||
uint8_t ihash[32];
|
||||
blake256_final(&S->inner, ihash);
|
||||
blake256_update(&S->outer, ihash, 256);
|
||||
|
@ -330,7 +351,7 @@ void hmac_blake256_final(hmac_state *S, uint8_t *digest) {
|
|||
memwipe(ihash, sizeof(ihash));
|
||||
}
|
||||
|
||||
void hmac_blake224_final(hmac_state *S, uint8_t *digest) {
|
||||
void hmac_blake224_final(hmac_state* S, uint8_t* digest) {
|
||||
uint8_t ihash[32];
|
||||
blake224_final(&S->inner, ihash);
|
||||
blake224_update(&S->outer, ihash, 224);
|
||||
|
@ -339,7 +360,8 @@ void hmac_blake224_final(hmac_state *S, uint8_t *digest) {
|
|||
}
|
||||
|
||||
// keylen = number of bytes; inlen = number of bytes
|
||||
void hmac_blake256_hash(uint8_t *out, const uint8_t *key, uint64_t keylen, const uint8_t *in, uint64_t inlen) {
|
||||
void hmac_blake256_hash(
|
||||
uint8_t* out, const uint8_t* key, uint64_t keylen, const uint8_t* in, uint64_t inlen) {
|
||||
hmac_state S;
|
||||
hmac_blake256_init(&S, key, keylen);
|
||||
hmac_blake256_update(&S, in, inlen * 8);
|
||||
|
@ -347,7 +369,8 @@ void hmac_blake256_hash(uint8_t *out, const uint8_t *key, uint64_t keylen, const
|
|||
}
|
||||
|
||||
// keylen = number of bytes; inlen = number of bytes
|
||||
void hmac_blake224_hash(uint8_t *out, const uint8_t *key, uint64_t keylen, const uint8_t *in, uint64_t inlen) {
|
||||
void hmac_blake224_hash(
|
||||
uint8_t* out, const uint8_t* key, uint64_t keylen, const uint8_t* in, uint64_t inlen) {
|
||||
hmac_state S;
|
||||
hmac_blake224_init(&S, key, keylen);
|
||||
hmac_blake224_update(&S, in, inlen * 8);
|
||||
|
|
|
@ -44,30 +44,30 @@ typedef struct {
|
|||
state outer;
|
||||
} hmac_state;
|
||||
|
||||
void blake256_init(state *);
|
||||
void blake224_init(state *);
|
||||
void blake256_init(state*);
|
||||
void blake224_init(state*);
|
||||
|
||||
void blake256_update(state *, const uint8_t *, uint64_t);
|
||||
void blake224_update(state *, const uint8_t *, uint64_t);
|
||||
void blake256_update(state*, const uint8_t*, uint64_t);
|
||||
void blake224_update(state*, const uint8_t*, uint64_t);
|
||||
|
||||
void blake256_final(state *, uint8_t *);
|
||||
void blake224_final(state *, uint8_t *);
|
||||
void blake256_final(state*, uint8_t*);
|
||||
void blake224_final(state*, uint8_t*);
|
||||
|
||||
void blake256_hash(uint8_t *, const uint8_t *, uint64_t);
|
||||
void blake224_hash(uint8_t *, const uint8_t *, uint64_t);
|
||||
void blake256_hash(uint8_t*, const uint8_t*, uint64_t);
|
||||
void blake224_hash(uint8_t*, const uint8_t*, uint64_t);
|
||||
|
||||
/* HMAC functions: */
|
||||
|
||||
void hmac_blake256_init(hmac_state *, const uint8_t *, uint64_t);
|
||||
void hmac_blake224_init(hmac_state *, const uint8_t *, uint64_t);
|
||||
void hmac_blake256_init(hmac_state*, const uint8_t*, uint64_t);
|
||||
void hmac_blake224_init(hmac_state*, const uint8_t*, uint64_t);
|
||||
|
||||
void hmac_blake256_update(hmac_state *, const uint8_t *, uint64_t);
|
||||
void hmac_blake224_update(hmac_state *, const uint8_t *, uint64_t);
|
||||
void hmac_blake256_update(hmac_state*, const uint8_t*, uint64_t);
|
||||
void hmac_blake224_update(hmac_state*, const uint8_t*, uint64_t);
|
||||
|
||||
void hmac_blake256_final(hmac_state *, uint8_t *);
|
||||
void hmac_blake224_final(hmac_state *, uint8_t *);
|
||||
void hmac_blake256_final(hmac_state*, uint8_t*);
|
||||
void hmac_blake224_final(hmac_state*, uint8_t*);
|
||||
|
||||
void hmac_blake256_hash(uint8_t *, const uint8_t *, uint64_t, const uint8_t *, uint64_t);
|
||||
void hmac_blake224_hash(uint8_t *, const uint8_t *, uint64_t, const uint8_t *, uint64_t);
|
||||
void hmac_blake256_hash(uint8_t*, const uint8_t*, uint64_t, const uint8_t*, uint64_t);
|
||||
void hmac_blake224_hash(uint8_t*, const uint8_t*, uint64_t, const uint8_t*, uint64_t);
|
||||
|
||||
#endif /* _BLAKE256_H_ */
|
||||
|
|
|
@ -33,11 +33,15 @@
|
|||
#include <windows.h>
|
||||
#define CTHR_MUTEX_TYPE HANDLE
|
||||
#define CTHR_MUTEX_INIT NULL
|
||||
#define CTHR_MUTEX_LOCK(x) do { if (x == NULL) { \
|
||||
#define CTHR_MUTEX_LOCK(x) \
|
||||
do { \
|
||||
if (x == NULL) { \
|
||||
HANDLE p = CreateMutex(NULL, FALSE, NULL); \
|
||||
if (InterlockedCompareExchangePointer((PVOID*)&x, (PVOID)p, NULL) != NULL) \
|
||||
CloseHandle(p); \
|
||||
} WaitForSingleObject(x, INFINITE); } while(0)
|
||||
} \
|
||||
WaitForSingleObject(x, INFINITE); \
|
||||
} while (0)
|
||||
#define CTHR_MUTEX_UNLOCK(x) ReleaseMutex(x)
|
||||
#define CTHR_THREAD_TYPE HANDLE
|
||||
#define CTHR_THREAD_RTYPE void
|
||||
|
@ -51,7 +55,7 @@
|
|||
#define CTHR_MUTEX_LOCK(x) pthread_mutex_lock(&x)
|
||||
#define CTHR_MUTEX_UNLOCK(x) pthread_mutex_unlock(&x)
|
||||
#define CTHR_THREAD_TYPE pthread_t
|
||||
#define CTHR_THREAD_RTYPE void *
|
||||
#define CTHR_THREAD_RTYPE void*
|
||||
#define CTHR_THREAD_RETURN return NULL
|
||||
#define CTHR_THREAD_CREATE(thr, func, arg) pthread_create(&thr, NULL, func, arg)
|
||||
#define CTHR_THREAD_JOIN(thr) pthread_join(thr, NULL)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue