oxen-core/src/cryptonote_basic/cryptonote_format_utils.cpp

1535 lines
62 KiB
C++
Raw Normal View History

// Copyright (c) 2014-2019, The Monero Project
2018-04-10 06:49:20 +02:00
// Copyright (c) 2018, The Loki Project
2014-07-23 15:03:52 +02:00
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
2014-03-03 23:07:58 +01:00
#include <atomic>
#include <boost/algorithm/string.hpp>
2020-07-02 17:50:37 +02:00
#include <limits>
#include <oxenc/hex.h>
2020-06-22 03:42:32 +02:00
#include <variant>
2020-10-23 22:32:28 +02:00
#include "common/hex.h"
Atomic staking amounts This adds a new tx registration interpretation for HF19+ by repurposing the fields of the registration: - `expiration` becomes `hf_or_expiration`; for "new" registrations it contains the hardfork number (e.g. 19 for HF19), but the data layout on chain doesn't change: essentially we determine whether it's a new registration based on whether the field is <= 255 (interpret as HF) or not (interpret as expiration). Changes in "new" registrations: - stake amounts are atomic OXEN rather than portions. This lets us skip a whole bunch of fiddling around with amounts that was necessary to deal with integer truncation when converting between amounts and portions. - the fee in the registration data is now a value out of 10000 instead of a portion (i.e. value out of 2^64-4). This limits fee precision to a percentage with two decimal places instead of ~17 decimal places. Internally we still convert this to a portion when processing the registration for service_node_states, but this makes the registration itself much simpler and easier to work with (as a human). - HF19+ registrations no longer have an expiry timestamp (though they do depend on the hardfork, so they "expire" whenever the next hard fork). The expiry timestamp was really only there to avoid a registration amount decreasing too much from the dropping staking requirement. - Both types are registration are still permitted for HF19, but because registrations with more than 4 contributors expose bugs in the portion transfer code (that results in registrations become invalid), old-style registrations are still limited to 4 contributors. - HF19 will allow both old and new registrations, so that registrations generated before the HF will still work, and so that we don't break testnet which has various "old" registrations on it.
2022-05-19 02:44:57 +02:00
#include "cryptonote_core/service_node_list.h"
#include "epee/wipeable_string.h"
#include "epee/string_tools.h"
#include "common/i18n.h"
2020-06-02 05:08:48 +02:00
#include "common/meta.h"
2017-12-22 20:47:12 +01:00
#include "serialization/string.h"
2020-06-02 05:08:48 +02:00
#include "serialization/binary_utils.h"
2014-03-03 23:07:58 +01:00
#include "cryptonote_format_utils.h"
#include "cryptonote_config.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "ringct/rctSigs.h"
#include "cryptonote_basic/verification_context.h"
#include "cryptonote_core/service_node_voting.h"
2021-01-04 01:09:45 +01:00
#include "cryptonote_core/oxen_name_system.h"
Change logging to easylogging++ This replaces the epee and data_loggers logging systems with a single one, and also adds filename:line and explicit severity levels. Categories may be defined, and logging severity set by category (or set of categories). epee style 0-4 log level maps to a sensible severity configuration. Log files now also rotate when reaching 100 MB. To select which logs to output, use the MONERO_LOGS environment variable, with a comma separated list of categories (globs are supported), with their requested severity level after a colon. If a log matches more than one such setting, the last one in the configuration string applies. A few examples: This one is (mostly) silent, only outputting fatal errors: MONERO_LOGS=*:FATAL This one is very verbose: MONERO_LOGS=*:TRACE This one is totally silent (logwise): MONERO_LOGS="" This one outputs all errors and warnings, except for the "verify" category, which prints just fatal errors (the verify category is used for logs about incoming transactions and blocks, and it is expected that some/many will fail to verify, hence we don't want the spam): MONERO_LOGS=*:WARNING,verify:FATAL Log levels are, in decreasing order of priority: FATAL, ERROR, WARNING, INFO, DEBUG, TRACE Subcategories may be added using prefixes and globs. This example will output net.p2p logs at the TRACE level, but all other net* logs only at INFO: MONERO_LOGS=*:ERROR,net*:INFO,net.p2p:TRACE Logs which are intended for the user (which Monero was using a lot through epee, but really isn't a nice way to go things) should use the "global" category. There are a few helper macros for using this category, eg: MGINFO("this shows up by default") or MGINFO_RED("this is red"), to try to keep a similar look and feel for now. Existing epee log macros still exist, and map to the new log levels, but since they're used as a "user facing" UI element as much as a logging system, they often don't map well to log severities (ie, a log level 0 log may be an error, or may be something we want the user to see, such as an important info). In those cases, I tried to use the new macros. In other cases, I left the existing macros in. When modifying logs, it is probably best to switch to the new macros with explicit levels. The --log-level options and set_log commands now also accept category settings, in addition to the epee style log levels.
2017-01-01 17:34:23 +01:00
using namespace crypto;
#define CHECK_AND_ASSERT_THROW_MES_L1(expr, message) {if(!(expr)) {log::warning(logcat, message); throw std::runtime_error(message);}}
namespace cryptonote
{
static auto logcat = log::Cat("cn");
static inline unsigned char *operator &(ec_point &point) {
return &reinterpret_cast<unsigned char &>(point);
}
static inline const unsigned char *operator &(const ec_point &point) {
return &reinterpret_cast<const unsigned char &>(point);
}
// a copy of rct::addKeys, since we can't link to libringct to avoid circular dependencies
static void add_public_key(crypto::public_key &AB, const crypto::public_key &A, const crypto::public_key &B) {
ge_p3 B2, A2;
CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, &B) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A2, &A) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
ge_cached tmp2;
ge_p3_to_cached(&tmp2, &B2);
ge_p1p1 tmp3;
ge_add(&tmp3, &A2, &tmp2);
ge_p1p1_to_p3(&A2, &tmp3);
ge_p3_tobytes(&AB, &A2);
}
uint64_t get_transaction_weight_clawback(const transaction &tx, size_t n_padded_outputs)
{
const rct::rctSig &rv = tx.rct_signatures;
const uint64_t bp_base = 368;
const size_t n_outputs = tx.vout.size();
if (n_padded_outputs <= 2)
return 0;
size_t nlr = 0;
while ((1u << nlr) < n_padded_outputs)
++nlr;
nlr += 6;
const size_t bp_size = 32 * (9 + 2 * nlr);
CHECK_AND_ASSERT_THROW_MES_L1(n_outputs <= TX_BULLETPROOF_MAX_OUTPUTS, "maximum number of outputs is " + std::to_string(TX_BULLETPROOF_MAX_OUTPUTS) + " per transaction");
CHECK_AND_ASSERT_THROW_MES_L1(bp_base * n_padded_outputs >= bp_size, "Invalid bulletproof clawback: bp_base " + std::to_string(bp_base) + ", n_padded_outputs "
+ std::to_string(n_padded_outputs) + ", bp_size " + std::to_string(bp_size));
const uint64_t bp_clawback = (bp_base * n_padded_outputs - bp_size) * 4 / 5;
return bp_clawback;
}
//---------------------------------------------------------------
}
2014-03-03 23:07:58 +01:00
namespace cryptonote
{
//---------------------------------------------------------------
2020-04-03 22:56:55 +02:00
void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h, hw::device &hwdev)
{
hwdev.get_transaction_prefix_hash(tx,h);
}
//---------------------------------------------------------------
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx, hw::device &hwdev)
{
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
crypto::hash h{};
2020-04-03 22:56:55 +02:00
get_transaction_prefix_hash(tx, h, hwdev);
return h;
}
2014-03-03 23:07:58 +01:00
//---------------------------------------------------------------
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx)
{
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
crypto::hash h{};
2014-03-03 23:07:58 +01:00
get_transaction_prefix_hash(tx, h);
return h;
}
//---------------------------------------------------------------
bool expand_transaction_1(transaction &tx, bool base_only)
{
Make tx type and version scoped enums This converts the transaction type and version to scoped enum, giving type safety and making the tx type assignment less error prone because there is no implicit conversion or comparison with raw integers that has to be worried about. This ends up converting any use of `cryptonote::transaction::type_xyz` to `cryptonote::transaction::txtype::xyz`. For version, names like `transaction::version_v4` become `cryptonote::txversion::v4_tx_types`. This also allows/includes various other simplifications related to or enabled by this change: - handle `is_deregister` dynamically in serialization code (setting `type::standard` or `type::deregister` rather than using a version-determined union) - `get_type()` is no longer needed with the above change: it is now much simpler to directly access `type` which will always have the correct value (even for v2 or v3 transaction types). And though there was an assertion on the enum value, `get_type()` was being used only sporadically: many places accessed `.type` directly. - the old unscoped enum didn't have a type but was assumed castable to/from `uint16_t`, which technically meant there was potential undefined behaviour when deserializing any type values >= 8. - tx type range checks weren't being done in all serialization paths; they are now. Because `get_type()` was not used everywhere (lots of places simply accessed `.type` directory) these might not have been caught. - `set_type()` is not needed; it was only being used in a single place (wallet2.cpp) and only for v4 txes, so the version protection code was never doing anything. - added a std::ostream << operator for the enum types so that they can be output with `<< tx_type <<` rather than needing to wrap it in `type_to_string(tx_type)` everywhere. For the versions, you get the annotated version string (e.g. 4_tx_types) rather than just the number 4.
2019-06-11 20:53:46 +02:00
if (tx.version >= txversion::v2_ringct && !is_coinbase(tx))
{
rct::rctSig &rv = tx.rct_signatures;
if (rv.type == rct::RCTType::Null)
return true;
if (rv.outPk.size() != tx.vout.size())
{
log::info(logcat, "Failed to parse transaction from blob, bad outPk size in tx {}", get_transaction_hash(tx));
return false;
}
for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n)
{
if (!std::holds_alternative<txout_to_key>(tx.vout[n].target))
{
log::info(logcat, "Unsupported output type in tx {}", get_transaction_hash(tx));
return false;
}
rv.outPk[n].dest = rct::pk2rct(var::get<txout_to_key>(tx.vout[n].target).key);
}
if (!base_only)
{
const bool bulletproof = rct::is_rct_bulletproof(rv.type);
if (bulletproof)
{
if (rv.p.bulletproofs.size() != 1)
{
log::info(logcat, "Failed to parse transaction from blob, bad bulletproofs size in tx {}", get_transaction_hash(tx));
return false;
}
if (rv.p.bulletproofs[0].L.size() < 6)
{
log::info(logcat, "Failed to parse transaction from blob, bad bulletproofs L size in tx {}", get_transaction_hash(tx));
return false;
}
const size_t max_outputs = 1 << (rv.p.bulletproofs[0].L.size() - 6);
if (max_outputs < tx.vout.size())
{
log::info(logcat, "Failed to parse transaction from blob, bad bulletproofs max outputs in tx {}", get_transaction_hash(tx));
return false;
}
const size_t n_amounts = tx.vout.size();
CHECK_AND_ASSERT_MES(n_amounts == rv.outPk.size(), false, "Internal error filling out V");
rv.p.bulletproofs[0].V.resize(n_amounts);
for (size_t i = 0; i < n_amounts; ++i)
rv.p.bulletproofs[0].V[i] = rct::scalarmultKey(rv.outPk[i].mask, rct::INV_EIGHT);
}
}
}
return true;
}
#if defined(_LIBCPP_VERSION)
#define BINARY_ARCHIVE_STREAM(stream_name, blob) \
std::stringstream stream_name; \
stream_name.write(reinterpret_cast<const char *>(blob.data()), blob.size())
#else
#define BINARY_ARCHIVE_STREAM(stream_name, blob) \
auto buf = tools::one_shot_read_buffer{reinterpret_cast<const char *>(blob.data()), blob.size()}; \
std::istream stream_name{&buf}
#endif
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
bool parse_and_validate_tx_from_blob(const std::string_view tx_blob, transaction& tx)
2014-03-03 23:07:58 +01:00
{
2020-06-02 05:08:48 +02:00
serialization::binary_string_unarchiver ba{tx_blob};
try {
serialization::serialize(ba, tx);
} catch (const std::exception& e) {
log::error(logcat, "Failed to parse and validate transaction from blob: {}", e.what());
2020-06-02 05:08:48 +02:00
return false;
}
CHECK_AND_ASSERT_MES(expand_transaction_1(tx, false), false, "Failed to expand transaction data");
tx.invalidate_hashes();
tx.set_blob_size(tx_blob.size());
2014-03-03 23:07:58 +01:00
return true;
}
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
bool parse_and_validate_tx_base_from_blob(const std::string_view tx_blob, transaction& tx)
{
2020-06-02 05:08:48 +02:00
serialization::binary_string_unarchiver ba{tx_blob};
try {
tx.serialize_base(ba);
} catch (const std::exception& e) {
log::error(logcat, "Failed to parse transaction base from blob: {}", e.what());
2020-06-02 05:08:48 +02:00
return false;
}
CHECK_AND_ASSERT_MES(expand_transaction_1(tx, true), false, "Failed to expand transaction data");
tx.invalidate_hashes();
return true;
}
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
bool parse_and_validate_tx_prefix_from_blob(const std::string_view tx_blob, transaction_prefix& tx)
{
2020-06-02 05:08:48 +02:00
serialization::binary_string_unarchiver ba{tx_blob};
try {
serialization::value(ba, tx);
} catch (const std::exception& e) {
log::error(logcat, "Failed to parse transaction prefix from blob: {}", e.what());
2020-06-02 05:08:48 +02:00
return false;
}
return true;
}
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
bool parse_and_validate_tx_from_blob(const std::string_view tx_blob, transaction& tx, crypto::hash& tx_hash)
2014-03-03 23:07:58 +01:00
{
2020-06-02 05:08:48 +02:00
serialization::binary_string_unarchiver ba{tx_blob};
try {
serialization::serialize(ba, tx);
} catch (const std::exception& e) {
log::error(logcat, "Failed to parse and validate transaction from blob + hash: {}", e.what());
2020-06-02 05:08:48 +02:00
return false;
}
CHECK_AND_ASSERT_MES(expand_transaction_1(tx, false), false, "Failed to expand transaction data");
tx.invalidate_hashes();
2014-03-03 23:07:58 +01:00
//TODO: validate tx
return get_transaction_hash(tx, tx_hash);
}
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
bool parse_and_validate_tx_from_blob(const std::string_view tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash)
{
if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash))
return false;
2014-03-03 23:07:58 +01:00
get_transaction_prefix_hash(tx, tx_prefix_hash);
return true;
}
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
bool is_v1_tx(const std::string_view tx_blob)
{
uint64_t version;
2020-06-02 05:08:48 +02:00
if (tools::read_varint(tx_blob, version) <= 0)
throw std::runtime_error("Internal error getting transaction version");
return version <= 1;
}
//---------------------------------------------------------------
bool generate_key_image_helper(const account_keys& ack, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev)
2014-03-03 23:07:58 +01:00
{
crypto::key_derivation recv_derivation{};
bool r = hwdev.generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation);
if (!r)
{
log::warning(logcat, "key image helper: failed to generate_key_derivation({}, <{}>)", tx_public_key, tools::type_to_hex(ack.m_view_secret_key));
memcpy(&recv_derivation, rct::identity().bytes, sizeof(recv_derivation));
}
2014-03-03 23:07:58 +01:00
2017-02-19 03:42:10 +01:00
std::vector<crypto::key_derivation> additional_recv_derivations;
for (size_t i = 0; i < additional_tx_public_keys.size(); ++i)
{
crypto::key_derivation additional_recv_derivation{};
r = hwdev.generate_key_derivation(additional_tx_public_keys[i], ack.m_view_secret_key, additional_recv_derivation);
if (!r)
{
log::warning(logcat, "key image helper: failed to generate_key_derivation({}, {})", additional_tx_public_keys[i], tools::type_to_hex(ack.m_view_secret_key));
}
else
{
additional_recv_derivations.push_back(additional_recv_derivation);
}
2017-02-19 03:42:10 +01:00
}
2020-06-02 00:30:19 +02:00
std::optional<subaddress_receive_info> subaddr_recv_info = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index,hwdev);
2017-02-19 03:42:10 +01:00
CHECK_AND_ASSERT_MES(subaddr_recv_info, false, "key image helper: given output pubkey doesn't seem to belong to this address");
return generate_key_image_helper_precomp(ack, out_key, subaddr_recv_info->derivation, real_output_index, subaddr_recv_info->index, in_ephemeral, ki, hwdev);
2017-02-19 03:42:10 +01:00
}
//---------------------------------------------------------------
bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev)
2017-02-19 03:42:10 +01:00
{
2023-01-16 21:52:30 +01:00
// This tries to compute the key image using a hardware device, if this succeeds return immediately, otherwise continue
if (hwdev.compute_key_image(ack, out_key, recv_derivation, real_output_index, received_index, in_ephemeral, ki))
{
return true;
}
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
if (!ack.m_spend_secret_key)
2017-02-19 03:42:10 +01:00
{
// for watch-only wallet, simply copy the known output pubkey
in_ephemeral.pub = out_key;
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
in_ephemeral.sec.zero();
2017-02-19 03:42:10 +01:00
}
else
{
// derive secret key with subaddress - step 1: original CN derivation
crypto::secret_key scalar_step1;
hwdev.derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, scalar_step1); // computes Hs(a*R || idx) + b
2017-02-19 03:42:10 +01:00
// step 2: add Hs(a || index_major || index_minor)
crypto::secret_key subaddr_sk;
2017-02-19 03:42:10 +01:00
crypto::secret_key scalar_step2;
if (received_index.is_zero())
{
scalar_step2 = scalar_step1; // treat index=(0,0) as a special case representing the main address
}
else
{
subaddr_sk = hwdev.get_subaddress_secret_key(ack.m_view_secret_key, received_index);
hwdev.sc_secret_add(scalar_step2, scalar_step1,subaddr_sk);
2017-02-19 03:42:10 +01:00
}
2014-03-03 23:07:58 +01:00
2017-02-19 03:42:10 +01:00
in_ephemeral.sec = scalar_step2;
if (ack.m_multisig_keys.empty())
{
// when not in multisig, we know the full spend secret key, so the output pubkey can be obtained by scalarmultBase
CHECK_AND_ASSERT_MES(hwdev.secret_key_to_public_key(in_ephemeral.sec, in_ephemeral.pub), false, "Failed to derive public key");
}
else
{
// when in multisig, we only know the partial spend secret key. but we do know the full spend public key, so the output pubkey can be obtained by using the standard CN key derivation
CHECK_AND_ASSERT_MES(hwdev.derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub), false, "Failed to derive public key");
// and don't forget to add the contribution from the subaddress part
if (!received_index.is_zero())
{
crypto::public_key subaddr_pk;
CHECK_AND_ASSERT_MES(hwdev.secret_key_to_public_key(subaddr_sk, subaddr_pk), false, "Failed to derive public key");
add_public_key(in_ephemeral.pub, in_ephemeral.pub, subaddr_pk);
}
}
CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_key,
false, "key image helper precomp: given output pubkey doesn't match the derived one");
2017-02-19 03:42:10 +01:00
}
2014-03-03 23:07:58 +01:00
hwdev.generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki);
2014-03-03 23:07:58 +01:00
return true;
}
//---------------------------------------------------------------
std::optional<uint64_t> parse_amount(std::string_view str_amount)
2014-03-03 23:07:58 +01:00
{
uint64_t amount;
tools::trim(str_amount);
2014-03-03 23:07:58 +01:00
auto parts = tools::split(str_amount, "."sv);
if (parts.size() > 2)
return std::nullopt; // 123.456.789 no thanks.
if (parts.size() == 2 && parts[1].empty())
parts.pop_back(); // allow "123." (treat it as as "123")
if (parts[0].find_first_not_of("0123456789"sv) != std::string::npos)
return std::nullopt; // whole part contains non-digit
if (parts[0].empty()) {
// Only allow an empty whole number part if there is a fractional part.
if (parts.size() == 1)
return std::nullopt;
amount = 0;
2014-03-03 23:07:58 +01:00
}
2014-04-02 18:00:17 +02:00
else
{
if (!tools::parse_int(parts[0], amount))
return std::nullopt;
// Scale up the number (e.g. 12 from "12.45") to atomic units.
if (amount > std::numeric_limits<uint64_t>::max() / oxen::COIN)
return std::nullopt; // would overflow
amount *= oxen::COIN;
2014-04-02 18:00:17 +02:00
}
if (parts.size() == 1)
return amount;
if (parts[1].find_first_not_of("0123456789"sv) != std::string::npos)
return std::nullopt; // fractional part contains non-digit
// If too long, but with insignificant 0's, trim them off
while (parts[1].size() > oxen::DISPLAY_DECIMAL_POINT && parts[1].back() == '0')
parts[1].remove_suffix(1);
if (parts[1].size() > oxen::DISPLAY_DECIMAL_POINT)
return std::nullopt; // fractional part has too many significant digits
uint64_t fractional;
if (!tools::parse_int(parts[1], fractional))
return std::nullopt;
2014-04-02 18:00:17 +02:00
// Scale up the value if it wasn't a full fractional value, e.g. if we have "10.45" then we
// need to convert the 45 we just parsed to 450'000'000.
for (size_t i = parts[1].size(); i < oxen::DISPLAY_DECIMAL_POINT; i++)
fractional *= 10;
2014-04-02 18:00:17 +02:00
if (fractional > std::numeric_limits<uint64_t>::max() - amount)
return std::nullopt; // would overflow
amount += fractional;
return amount;
2014-03-03 23:07:58 +01:00
}
//---------------------------------------------------------------
uint64_t get_transaction_weight(const transaction &tx, size_t blob_size)
{
CHECK_AND_ASSERT_MES(!tx.pruned, std::numeric_limits<uint64_t>::max(), "get_transaction_weight does not support pruned txes");
Make tx type and version scoped enums This converts the transaction type and version to scoped enum, giving type safety and making the tx type assignment less error prone because there is no implicit conversion or comparison with raw integers that has to be worried about. This ends up converting any use of `cryptonote::transaction::type_xyz` to `cryptonote::transaction::txtype::xyz`. For version, names like `transaction::version_v4` become `cryptonote::txversion::v4_tx_types`. This also allows/includes various other simplifications related to or enabled by this change: - handle `is_deregister` dynamically in serialization code (setting `type::standard` or `type::deregister` rather than using a version-determined union) - `get_type()` is no longer needed with the above change: it is now much simpler to directly access `type` which will always have the correct value (even for v2 or v3 transaction types). And though there was an assertion on the enum value, `get_type()` was being used only sporadically: many places accessed `.type` directly. - the old unscoped enum didn't have a type but was assumed castable to/from `uint16_t`, which technically meant there was potential undefined behaviour when deserializing any type values >= 8. - tx type range checks weren't being done in all serialization paths; they are now. Because `get_type()` was not used everywhere (lots of places simply accessed `.type` directory) these might not have been caught. - `set_type()` is not needed; it was only being used in a single place (wallet2.cpp) and only for v4 txes, so the version protection code was never doing anything. - added a std::ostream << operator for the enum types so that they can be output with `<< tx_type <<` rather than needing to wrap it in `type_to_string(tx_type)` everywhere. For the versions, you get the annotated version string (e.g. 4_tx_types) rather than just the number 4.
2019-06-11 20:53:46 +02:00
if (tx.version < txversion::v2_ringct)
return blob_size;
const rct::rctSig &rv = tx.rct_signatures;
if (!rct::is_rct_bulletproof(rv.type))
return blob_size;
const size_t n_padded_outputs = rct::n_bulletproof_max_amounts(rv.p.bulletproofs);
uint64_t bp_clawback = get_transaction_weight_clawback(tx, n_padded_outputs);
CHECK_AND_ASSERT_THROW_MES_L1(bp_clawback <= std::numeric_limits<uint64_t>::max() - blob_size, "Weight overflow");
return blob_size + bp_clawback;
}
//---------------------------------------------------------------
uint64_t get_pruned_transaction_weight(const transaction &tx)
{
CHECK_AND_ASSERT_MES(tx.pruned, std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support non pruned txes");
CHECK_AND_ASSERT_MES(tx.version >= txversion::v2_ringct, std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support v1 txes");
CHECK_AND_ASSERT_MES(tx.rct_signatures.type >= rct::RCTType::Bulletproof2,
std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support older range proof types");
CHECK_AND_ASSERT_MES(!tx.vin.empty(), std::numeric_limits<uint64_t>::max(), "empty vin");
2020-06-22 03:42:32 +02:00
CHECK_AND_ASSERT_MES(std::holds_alternative<cryptonote::txin_to_key>(tx.vin[0]), std::numeric_limits<uint64_t>::max(), "empty vin");
// get pruned data size
uint64_t weight = serialization::dump_binary(const_cast<transaction&>(tx)).size();
// nbps (technically varint)
weight += 1;
// calculate deterministic bulletproofs size (assumes canonical BP format)
size_t nrl = 0, n_padded_outputs;
while ((n_padded_outputs = (1u << nrl)) < tx.vout.size())
++nrl;
nrl += 6;
uint64_t extra = 32 * (9 + 2 * nrl) + 2;
weight += extra;
// calculate deterministic CLSAG/MLSAG data size
const size_t ring_size = var::get<cryptonote::txin_to_key>(tx.vin[0]).key_offsets.size();
if (tx.rct_signatures.type == rct::RCTType::CLSAG)
extra = tx.vin.size() * (ring_size + 2) * 32;
else
extra = tx.vin.size() * (ring_size * (1 + 1) * 32 + 32 /* cc */);
weight += extra;
// calculate deterministic pseudoOuts size
extra = 32 * (tx.vin.size());
weight += extra;
// clawback
uint64_t bp_clawback = get_transaction_weight_clawback(tx, n_padded_outputs);
CHECK_AND_ASSERT_THROW_MES_L1(bp_clawback <= std::numeric_limits<uint64_t>::max() - weight, "Weight overflow");
weight += bp_clawback;
return weight;
}
//---------------------------------------------------------------
uint64_t get_transaction_weight(const transaction &tx)
{
2020-06-02 05:08:48 +02:00
size_t blob_size =
tx.is_blob_size_valid()
? tx.blob_size
: serialization::dump_binary(const_cast<transaction&>(tx)).size();
return get_transaction_weight(tx, blob_size);
}
//---------------------------------------------------------------
Generic burn fee checking + blink burn fee checking This adds the ability for check_fee() to also check the burn amount. This requires passing extra info through `add_tx()` (and the various things that call it), so I took the: bool keeped_by_block, bool relayed, bool do_not_relay argument triplet, moved it into a struct in tx_pool.h, then added the other fee options there (along with some static factory functions for generating the typical sets of option). The majority of this commit is chasing that change through the codebase and test suite. This is used by blink but should also help LNS and other future burn transactions to verify a burn amount simply when adding the transation to the mempool. It supports a fixed burn amount, a burn amount as a multiple of the minimum tx fee, and also allows you to increase the minimum tx fee (so that, for example, we could require blink txes to pay miners 250% of the usual minimum (unimportant) priority tx fee. - Removed a useless core::add_new_tx() overload that wasn't used anywhere. Blink-specific changes: (I'd normally separate these into a separate commit, but they got interwoven fairly heavily with the above change). - changed the way blink burning is specified so that we have three knobs for fee adjustment (fixed burn fee; base fee multiple; and required miner tx fee). The fixed amount is currently 0, base fee is 400%, and require miner tx fee is simply 100% (i.e. no different than a normal transaction). This is the same as before this commit, but is changing how they are being specified in cryptonote_config.h. - blink tx fee, burn amount, and miner tx fee (if > 100%) now get checked before signing a blink tx. (These fee checks don't apply to anyone else -- when propagating over the network only the miner tx fee is checked). - Added a couple of checks for blink quorums: 1) make sure they have reached the blink hf; 2) make sure the submitted tx version conforms to the current hf min/max tx version. - print blink fee information in simplewallet's `fee` output - add "typical" fee calculations in the `fee` output: [wallet T6SCwL (has locked stakes)]: fee Current fee is 0.000000850 loki per byte + 0.020000000 loki per output No backlog at priority 1 No backlog at priority 2 No backlog at priority 3 No backlog at priority 4 Current blink fee is 0.000004250 loki per byte + 0.100000000 loki per output Estimated typical small transaction fees: 0.042125000 (unimportant), 0.210625000 (normal), 1.053125000 (elevated), 5.265625000 (priority), 0.210625000 (blink) where "small" here is the same tx size (2500 bytes + 2 outputs) used to estimate backlogs.
2019-11-09 04:14:15 +01:00
bool get_tx_miner_fee(const transaction& tx, uint64_t & fee, bool burning_enabled, uint64_t *burned)
2014-03-03 23:07:58 +01:00
{
Generic burn fee checking + blink burn fee checking This adds the ability for check_fee() to also check the burn amount. This requires passing extra info through `add_tx()` (and the various things that call it), so I took the: bool keeped_by_block, bool relayed, bool do_not_relay argument triplet, moved it into a struct in tx_pool.h, then added the other fee options there (along with some static factory functions for generating the typical sets of option). The majority of this commit is chasing that change through the codebase and test suite. This is used by blink but should also help LNS and other future burn transactions to verify a burn amount simply when adding the transation to the mempool. It supports a fixed burn amount, a burn amount as a multiple of the minimum tx fee, and also allows you to increase the minimum tx fee (so that, for example, we could require blink txes to pay miners 250% of the usual minimum (unimportant) priority tx fee. - Removed a useless core::add_new_tx() overload that wasn't used anywhere. Blink-specific changes: (I'd normally separate these into a separate commit, but they got interwoven fairly heavily with the above change). - changed the way blink burning is specified so that we have three knobs for fee adjustment (fixed burn fee; base fee multiple; and required miner tx fee). The fixed amount is currently 0, base fee is 400%, and require miner tx fee is simply 100% (i.e. no different than a normal transaction). This is the same as before this commit, but is changing how they are being specified in cryptonote_config.h. - blink tx fee, burn amount, and miner tx fee (if > 100%) now get checked before signing a blink tx. (These fee checks don't apply to anyone else -- when propagating over the network only the miner tx fee is checked). - Added a couple of checks for blink quorums: 1) make sure they have reached the blink hf; 2) make sure the submitted tx version conforms to the current hf min/max tx version. - print blink fee information in simplewallet's `fee` output - add "typical" fee calculations in the `fee` output: [wallet T6SCwL (has locked stakes)]: fee Current fee is 0.000000850 loki per byte + 0.020000000 loki per output No backlog at priority 1 No backlog at priority 2 No backlog at priority 3 No backlog at priority 4 Current blink fee is 0.000004250 loki per byte + 0.100000000 loki per output Estimated typical small transaction fees: 0.042125000 (unimportant), 0.210625000 (normal), 1.053125000 (elevated), 5.265625000 (priority), 0.210625000 (blink) where "small" here is the same tx size (2500 bytes + 2 outputs) used to estimate backlogs.
2019-11-09 04:14:15 +01:00
if (burned)
*burned = 0;
Make tx type and version scoped enums This converts the transaction type and version to scoped enum, giving type safety and making the tx type assignment less error prone because there is no implicit conversion or comparison with raw integers that has to be worried about. This ends up converting any use of `cryptonote::transaction::type_xyz` to `cryptonote::transaction::txtype::xyz`. For version, names like `transaction::version_v4` become `cryptonote::txversion::v4_tx_types`. This also allows/includes various other simplifications related to or enabled by this change: - handle `is_deregister` dynamically in serialization code (setting `type::standard` or `type::deregister` rather than using a version-determined union) - `get_type()` is no longer needed with the above change: it is now much simpler to directly access `type` which will always have the correct value (even for v2 or v3 transaction types). And though there was an assertion on the enum value, `get_type()` was being used only sporadically: many places accessed `.type` directly. - the old unscoped enum didn't have a type but was assumed castable to/from `uint16_t`, which technically meant there was potential undefined behaviour when deserializing any type values >= 8. - tx type range checks weren't being done in all serialization paths; they are now. Because `get_type()` was not used everywhere (lots of places simply accessed `.type` directory) these might not have been caught. - `set_type()` is not needed; it was only being used in a single place (wallet2.cpp) and only for v4 txes, so the version protection code was never doing anything. - added a std::ostream << operator for the enum types so that they can be output with `<< tx_type <<` rather than needing to wrap it in `type_to_string(tx_type)` everywhere. For the versions, you get the annotated version string (e.g. 4_tx_types) rather than just the number 4.
2019-06-11 20:53:46 +02:00
if (tx.version >= txversion::v2_ringct)
{
fee = tx.rct_signatures.txnFee;
if (burning_enabled)
Generic burn fee checking + blink burn fee checking This adds the ability for check_fee() to also check the burn amount. This requires passing extra info through `add_tx()` (and the various things that call it), so I took the: bool keeped_by_block, bool relayed, bool do_not_relay argument triplet, moved it into a struct in tx_pool.h, then added the other fee options there (along with some static factory functions for generating the typical sets of option). The majority of this commit is chasing that change through the codebase and test suite. This is used by blink but should also help LNS and other future burn transactions to verify a burn amount simply when adding the transation to the mempool. It supports a fixed burn amount, a burn amount as a multiple of the minimum tx fee, and also allows you to increase the minimum tx fee (so that, for example, we could require blink txes to pay miners 250% of the usual minimum (unimportant) priority tx fee. - Removed a useless core::add_new_tx() overload that wasn't used anywhere. Blink-specific changes: (I'd normally separate these into a separate commit, but they got interwoven fairly heavily with the above change). - changed the way blink burning is specified so that we have three knobs for fee adjustment (fixed burn fee; base fee multiple; and required miner tx fee). The fixed amount is currently 0, base fee is 400%, and require miner tx fee is simply 100% (i.e. no different than a normal transaction). This is the same as before this commit, but is changing how they are being specified in cryptonote_config.h. - blink tx fee, burn amount, and miner tx fee (if > 100%) now get checked before signing a blink tx. (These fee checks don't apply to anyone else -- when propagating over the network only the miner tx fee is checked). - Added a couple of checks for blink quorums: 1) make sure they have reached the blink hf; 2) make sure the submitted tx version conforms to the current hf min/max tx version. - print blink fee information in simplewallet's `fee` output - add "typical" fee calculations in the `fee` output: [wallet T6SCwL (has locked stakes)]: fee Current fee is 0.000000850 loki per byte + 0.020000000 loki per output No backlog at priority 1 No backlog at priority 2 No backlog at priority 3 No backlog at priority 4 Current blink fee is 0.000004250 loki per byte + 0.100000000 loki per output Estimated typical small transaction fees: 0.042125000 (unimportant), 0.210625000 (normal), 1.053125000 (elevated), 5.265625000 (priority), 0.210625000 (blink) where "small" here is the same tx size (2500 bytes + 2 outputs) used to estimate backlogs.
2019-11-09 04:14:15 +01:00
{
uint64_t fee_burned = get_burned_amount_from_tx_extra(tx.extra);
fee -= std::min(fee, fee_burned);
if (burned)
*burned = fee_burned;
}
return true;
}
uint64_t amount_in;
if (!get_inputs_money_amount(tx, amount_in)) return false;
uint64_t amount_out = get_outs_money_amount(tx);
2014-03-03 23:07:58 +01:00
CHECK_AND_ASSERT_MES(amount_in >= amount_out, false, "transaction spend (" <<amount_in << ") more than it has (" << amount_out << ")");
fee = amount_in - amount_out;
return true;
}
//---------------------------------------------------------------
uint64_t get_tx_miner_fee(const transaction& tx, bool burning_enabled)
2014-03-03 23:07:58 +01:00
{
uint64_t r = 0;
if(!get_tx_miner_fee(tx, r, burning_enabled))
2014-03-03 23:07:58 +01:00
return 0;
return r;
}
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
[[nodiscard]] bool parse_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<tx_extra_field>& tx_extra_fields)
2014-03-03 23:07:58 +01:00
{
2014-05-03 18:19:43 +02:00
tx_extra_fields.clear();
if(tx_extra.empty())
return true;
2020-06-02 05:08:48 +02:00
serialization::binary_string_unarchiver ar{tx_extra};
2014-05-03 18:19:43 +02:00
2020-06-02 05:08:48 +02:00
try {
serialization::deserialize_all(ar, tx_extra_fields);
} catch (const std::exception& e) {
log::warning(logcat, "{}: failed to deserialize extra field: {}; extra = {}", __func__, e.what(), oxenc::to_hex(tx_extra.begin(), tx_extra.end()));
2020-06-02 05:08:48 +02:00
return false;
2014-03-03 23:07:58 +01:00
}
2014-05-03 18:19:43 +02:00
2014-03-03 23:07:58 +01:00
return true;
}
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
[[nodiscard]] bool sort_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<uint8_t> &sorted_tx_extra)
{
std::vector<tx_extra_field> tx_extra_fields;
2020-06-02 05:08:48 +02:00
if (!parse_tx_extra(tx_extra, tx_extra_fields))
return false;
2020-06-02 05:08:48 +02:00
// Sort according to the order of variant alternatives in the variant itself
std::stable_sort(tx_extra_fields.begin(), tx_extra_fields.end(), [](auto& a, auto& b) { return a.index() < b.index(); });
2020-06-02 05:08:48 +02:00
serialization::binary_string_archiver ar;
try {
for (auto& f : tx_extra_fields)
serialization::value(ar, f);
} catch (const std::exception& e) {
log::info(logcat, "failed to serialize tx extra field: {}", e.what());
return false;
}
2020-06-02 05:08:48 +02:00
std::string extrastr = ar.str();
sorted_tx_extra = std::vector<uint8_t>(extrastr.begin(), extrastr.end());
return true;
}
//---------------------------------------------------------------
crypto::public_key get_tx_pub_key_from_extra(const std::vector<uint8_t>& tx_extra, size_t pk_index)
2014-05-03 18:19:43 +02:00
{
tx_extra_pub_key pub_key_field;
if (get_field_from_tx_extra(tx_extra, pub_key_field, pk_index))
return pub_key_field.pub_key;
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
return null<public_key>;
2014-05-03 18:19:43 +02:00
}
//---------------------------------------------------------------
crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx_prefix, size_t pk_index)
{
return get_tx_pub_key_from_extra(tx_prefix.extra, pk_index);
}
//---------------------------------------------------------------
void add_tagged_data_to_tx_extra(std::vector<uint8_t>& tx_extra, uint8_t tag, std::string_view data)
{
tx_extra.reserve(tx_extra.size() + 1 + data.size());
tx_extra.push_back(tag);
tx_extra.insert(tx_extra.end(), data.begin(), data.end());
2014-03-03 23:07:58 +01:00
}
//---------------------------------------------------------------
2017-02-19 03:42:10 +01:00
std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const std::vector<uint8_t>& tx_extra)
{
tx_extra_additional_pub_keys additional_pub_keys;
if (get_field_from_tx_extra(tx_extra, additional_pub_keys))
return additional_pub_keys.data;
return {};
2017-02-19 03:42:10 +01:00
}
//---------------------------------------------------------------
std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const transaction_prefix& tx)
{
return get_additional_tx_pub_keys_from_extra(tx.extra);
}
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
static bool add_tx_extra_field_to_tx_extra(std::vector<uint8_t>& tx_extra, tx_extra_field& field)
2017-02-19 03:42:10 +01:00
{
2020-06-02 05:08:48 +02:00
std::string tx_extra_str;
try {
tx_extra_str = serialization::dump_binary(field);
} catch (...) {
Infinite Staking Part 1 (#387) * Remove dead branches in hot-path check_tx_inputs Also renames #define for mixins to better match naming convention * Shuffle around some more code into common branches * Fix min/max tx version rules, since there 1 tx v2 on v9 fork * First draft infinite staking implementation * Actually generate the right key image and expire appropriately * Add framework to lock key images after expiry * Return locked key images for nodes, add request unlock option * Introduce transaction types for key image unlock * Update validation steps to accept tx types, key_image_unlock * Add mapping for lockable key images to amounts * Change inconsistent naming scheme of contributors * Create key image unlock transaction type and process it * Update tx params to allow v4 types and as a result construct_tx* * Fix some serialisation issues not sending all the information * Fix dupe tx extra tag causing incorrect deserialisation * Add warning comments * Fix key image unlocks parsing error * Simplify key image proof checks * Fix rebase errors * Correctly calculate the key image unlock times * Blacklist key image on deregistration * Serialise key image blacklist * Rollback blacklisted key images * Fix expiry logic error * Disallow requesting stake unlock if already unlocked client side * Add double spend checks for key image unlocks * Rename get_staking_requirement_lock_blocks To staking_initial_num_lock_blocks * Begin modifying output selection to not use locked outputs * Modify output selection to avoid locked/blacklisted key images * Cleanup and undoing some protocol breakages * Simplify expiration of nodes * Request unlock schedules entire node for expiration * Fix off by one in expiring nodes * Undo expiring code for pre v10 nodes * Fix RPC returning register as unlock height and not checking 0 * Rename key image unlock height const * Undo testnet hardfork debug changes * Remove is_type for get_type, fix missing var rename * Move serialisable data into public namespace * Serialise tx types properly * Fix typo in no service node known msg * Code review * Fix == to >= on serialising tx type * Code review 2 * Fix tests and key image unlock * Add additional test, fix assert * Remove debug code in wallet * Fix merge dev problem
2019-01-25 04:15:52 +01:00
return false;
2020-06-02 05:08:48 +02:00
}
Infinite Staking Part 1 (#387) * Remove dead branches in hot-path check_tx_inputs Also renames #define for mixins to better match naming convention * Shuffle around some more code into common branches * Fix min/max tx version rules, since there 1 tx v2 on v9 fork * First draft infinite staking implementation * Actually generate the right key image and expire appropriately * Add framework to lock key images after expiry * Return locked key images for nodes, add request unlock option * Introduce transaction types for key image unlock * Update validation steps to accept tx types, key_image_unlock * Add mapping for lockable key images to amounts * Change inconsistent naming scheme of contributors * Create key image unlock transaction type and process it * Update tx params to allow v4 types and as a result construct_tx* * Fix some serialisation issues not sending all the information * Fix dupe tx extra tag causing incorrect deserialisation * Add warning comments * Fix key image unlocks parsing error * Simplify key image proof checks * Fix rebase errors * Correctly calculate the key image unlock times * Blacklist key image on deregistration * Serialise key image blacklist * Rollback blacklisted key images * Fix expiry logic error * Disallow requesting stake unlock if already unlocked client side * Add double spend checks for key image unlocks * Rename get_staking_requirement_lock_blocks To staking_initial_num_lock_blocks * Begin modifying output selection to not use locked outputs * Modify output selection to avoid locked/blacklisted key images * Cleanup and undoing some protocol breakages * Simplify expiration of nodes * Request unlock schedules entire node for expiration * Fix off by one in expiring nodes * Undo expiring code for pre v10 nodes * Fix RPC returning register as unlock height and not checking 0 * Rename key image unlock height const * Undo testnet hardfork debug changes * Remove is_type for get_type, fix missing var rename * Move serialisable data into public namespace * Serialise tx types properly * Fix typo in no service node known msg * Code review * Fix == to >= on serialising tx type * Code review 2 * Fix tests and key image unlock * Add additional test, fix assert * Remove debug code in wallet * Fix merge dev problem
2019-01-25 04:15:52 +01:00
2020-06-02 05:08:48 +02:00
tx_extra.reserve(tx_extra.size() + tx_extra_str.size());
tx_extra.insert(tx_extra.end(), tx_extra_str.begin(), tx_extra_str.end());
Infinite Staking Part 1 (#387) * Remove dead branches in hot-path check_tx_inputs Also renames #define for mixins to better match naming convention * Shuffle around some more code into common branches * Fix min/max tx version rules, since there 1 tx v2 on v9 fork * First draft infinite staking implementation * Actually generate the right key image and expire appropriately * Add framework to lock key images after expiry * Return locked key images for nodes, add request unlock option * Introduce transaction types for key image unlock * Update validation steps to accept tx types, key_image_unlock * Add mapping for lockable key images to amounts * Change inconsistent naming scheme of contributors * Create key image unlock transaction type and process it * Update tx params to allow v4 types and as a result construct_tx* * Fix some serialisation issues not sending all the information * Fix dupe tx extra tag causing incorrect deserialisation * Add warning comments * Fix key image unlocks parsing error * Simplify key image proof checks * Fix rebase errors * Correctly calculate the key image unlock times * Blacklist key image on deregistration * Serialise key image blacklist * Rollback blacklisted key images * Fix expiry logic error * Disallow requesting stake unlock if already unlocked client side * Add double spend checks for key image unlocks * Rename get_staking_requirement_lock_blocks To staking_initial_num_lock_blocks * Begin modifying output selection to not use locked outputs * Modify output selection to avoid locked/blacklisted key images * Cleanup and undoing some protocol breakages * Simplify expiration of nodes * Request unlock schedules entire node for expiration * Fix off by one in expiring nodes * Undo expiring code for pre v10 nodes * Fix RPC returning register as unlock height and not checking 0 * Rename key image unlock height const * Undo testnet hardfork debug changes * Remove is_type for get_type, fix missing var rename * Move serialisable data into public namespace * Serialise tx types properly * Fix typo in no service node known msg * Code review * Fix == to >= on serialising tx type * Code review 2 * Fix tests and key image unlock * Add additional test, fix assert * Remove debug code in wallet * Fix merge dev problem
2019-01-25 04:15:52 +01:00
return true;
}
//---------------------------------------------------------------
bool add_additional_tx_pub_keys_to_extra(std::vector<uint8_t>& tx_extra, const std::vector<crypto::public_key>& additional_pub_keys)
{
tx_extra_field field = tx_extra_additional_pub_keys{ additional_pub_keys };
if (!add_tx_extra_field_to_tx_extra(tx_extra, field))
{
log::info(logcat, "failed to serialize tx extra additional tx pub keys");
return false;
}
2017-02-19 03:42:10 +01:00
return true;
}
//---------------------------------------------------------------
2022-05-20 23:29:48 +02:00
bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const std::string& extra_nonce)
2014-03-03 23:07:58 +01:00
{
2014-05-03 18:19:43 +02:00
CHECK_AND_ASSERT_MES(extra_nonce.size() <= TX_EXTRA_NONCE_MAX_COUNT, false, "extra nonce could be 255 bytes max");
2020-06-02 05:08:48 +02:00
tx_extra.reserve(tx_extra.size() + 2 + extra_nonce.size());
tx_extra.push_back(TX_EXTRA_NONCE); // write tag
tx_extra.push_back(static_cast<uint8_t>(extra_nonce.size())); // write len
std::copy(extra_nonce.begin(), extra_nonce.end(), std::back_inserter(tx_extra));
2014-05-03 18:19:43 +02:00
return true;
}
Service Node Deregister Part 5 (#89) * Retrieve quorum list from height, reviewed * Setup data structures for de/register TX * Submit and validate partial/full deregisters * Add P2P relaying of partial deregistration votes * Code review adjustments for deregistration part 1 - Fix check_tx_semantic - Remove signature_pod as votes are now stored as blobs. Serialization overrides don't intefere with crypto::signature anymore. * deregistration_vote_pool - changed sign/verify interface and removed repeated code * Misc review, fix sign/verify api, vote threshold * Deregister/tx edge case handling for combinatoric votes * core, service_node_list: separated address from service node pubkey * Retrieve quorum list from height, reviewed * Setup data structures for de/register TX * Submit and validate partial/full deregisters * Add P2P relaying of partial deregistration votes * Code review adjustments for deregistration part 1 - Fix check_tx_semantic - Remove signature_pod as votes are now stored as blobs. Serialization overrides don't intefere with crypto::signature anymore. * deregistration_vote_pool - changed sign/verify interface and removed repeated code * Misc review, fix sign/verify api, vote threshold * Deregister/tx edge case handling for combinatoric votes * Store service node lists for the duration of deregister lifetimes * Quorum min/max bug, sort node list, fix node to test list * Change quorum to store acc pub address, fix oob bug * Code review for expiring votes, acc keys to pub_key, improve err msgs * Add early out for is_deregistration_tx and protect against quorum changes * Remove debug code, fix segfault * Remove irrelevant check for tx v3 in blockchain, fix >= height for pruning quorum states Incorrect assumption that a transaction can be kept in the chain if it could eventually become invalid, because if it were the chain would be split and eventually these transaction would be dropped. But also that we should not override the pre-existing logic which handles this case anyway.
2018-07-18 04:42:47 +02:00
bool add_service_node_state_change_to_tx_extra(std::vector<uint8_t>& tx_extra, const tx_extra_service_node_state_change& state_change, const hf hf_version)
Relax deregistration rules The replaces the deregistration mechanism with a new state change mechanism (beginning at the v12 fork) which can change a service node's network status via three potential values (and is extensible in the future to handle more): - deregistered -- this is the same as the existing deregistration; the SN is instantly removed from the SN list. - decommissioned -- this is a sort of temporary deregistration: your SN remains in the service node list, but is removed from the rewards list and from any network duties. - recommissioned -- this tx is sent by a quorum if they observe a decommissioned SN sending uptime proofs again. Upon reception, the SN is reactivated and put on the end of the reward list. Since this is broadening the quorum use, this also renames the relevant quorum to a "obligations" quorum (since it validates SN obligations), while the transactions are "state_change" transactions (since they change the state of a registered SN). The new parameters added to service_node_rules.h control how this works: // Service node decommissioning: as service nodes stay up they earn "credits" (measured in blocks) // towards a future outage. A new service node starts out with INITIAL_CREDIT, and then builds up // CREDIT_PER_DAY for each day the service node remains active up to a maximum of // DECOMMISSION_MAX_CREDIT. // // If a service node stops sending uptime proofs, a quorum will consider whether the service node // has built up enough credits (at least MINIMUM): if so, instead of submitting a deregistration, // it instead submits a decommission. This removes the service node from the list of active // service nodes both for rewards and for any active network duties. If the service node comes // back online (i.e. starts sending the required performance proofs again) before the credits run // out then a quorum will reinstate the service node using a recommission transaction, which adds // the service node back to the bottom of the service node reward list, and resets its accumulated // credits to 0. If it does not come back online within the required number of blocks (i.e. the // accumulated credit at the point of decommissioning) then a quorum will send a permanent // deregistration transaction to the network, starting a 30-day deregistration count down. This commit currently includes values (which are not necessarily finalized): - 8 hours (240 blocks) of credit required for activation of a decommission (rather than a deregister) - 0 initial credits at registration - a maximum of 24 hours (720 blocks) of credits - credits accumulate at a rate that you hit 24 hours of credits after 30 days of operation. Miscellaneous other details of this PR: - a new TX extra tag is used for the state change (including deregistrations). The old extra tag has no version or type tag, so couldn't be reused. The data in the new tag is slightly more efficiently packed than the old deregistration transaction, so it gets used for deregistrations (starting at the v12 fork) as well. - Correct validator/worker selection required generalizing the shuffle function to be able to shuffle just part of a vector. This lets us stick any down service nodes at the end of the potential list, then select validators by only shuffling the part of the index vector that contains active service indices. Once the validators are selected, the remainder of the list (this time including decommissioned SN indices) is shuffled to select quorum workers to check, thus allowing decommisioned nodes to be randomly included in the nodes to check without being selected as a validator. - Swarm recalculation was not quite right: swarms were recalculated on SN registrations, even if those registrations were include shared node registrations, but *not* recalculated on stakes. Starting with the upgrade this behaviour is fixed (swarms aren't actually used currently and aren't consensus-relevant so recalculating early won't hurt anything). - Details on decomm/dereg are added to RPC info and print_sn/print_sn_status - Slightly improves the % of reward output in the print_sn output by rounding it to two digits, and reserves space in the output string to avoid excessive reallocations. - Adds various debugging at higher debug levels to quorum voting (into all of voting itself, vote transmission, and vote reception). - Reset service node list internal data structure version to 0. The SN list has to be rescanned anyway at upgrade (its size has changed), so we might as well reset the version and remove the version-dependent serialization code. (Note that the affected code here is for SN states in lmdb storage, not for SN-to-SN communication serialization).
2019-06-18 23:57:02 +02:00
{
tx_extra_field field;
if (hf_version < hf::hf12_checkpointing)
Relax deregistration rules The replaces the deregistration mechanism with a new state change mechanism (beginning at the v12 fork) which can change a service node's network status via three potential values (and is extensible in the future to handle more): - deregistered -- this is the same as the existing deregistration; the SN is instantly removed from the SN list. - decommissioned -- this is a sort of temporary deregistration: your SN remains in the service node list, but is removed from the rewards list and from any network duties. - recommissioned -- this tx is sent by a quorum if they observe a decommissioned SN sending uptime proofs again. Upon reception, the SN is reactivated and put on the end of the reward list. Since this is broadening the quorum use, this also renames the relevant quorum to a "obligations" quorum (since it validates SN obligations), while the transactions are "state_change" transactions (since they change the state of a registered SN). The new parameters added to service_node_rules.h control how this works: // Service node decommissioning: as service nodes stay up they earn "credits" (measured in blocks) // towards a future outage. A new service node starts out with INITIAL_CREDIT, and then builds up // CREDIT_PER_DAY for each day the service node remains active up to a maximum of // DECOMMISSION_MAX_CREDIT. // // If a service node stops sending uptime proofs, a quorum will consider whether the service node // has built up enough credits (at least MINIMUM): if so, instead of submitting a deregistration, // it instead submits a decommission. This removes the service node from the list of active // service nodes both for rewards and for any active network duties. If the service node comes // back online (i.e. starts sending the required performance proofs again) before the credits run // out then a quorum will reinstate the service node using a recommission transaction, which adds // the service node back to the bottom of the service node reward list, and resets its accumulated // credits to 0. If it does not come back online within the required number of blocks (i.e. the // accumulated credit at the point of decommissioning) then a quorum will send a permanent // deregistration transaction to the network, starting a 30-day deregistration count down. This commit currently includes values (which are not necessarily finalized): - 8 hours (240 blocks) of credit required for activation of a decommission (rather than a deregister) - 0 initial credits at registration - a maximum of 24 hours (720 blocks) of credits - credits accumulate at a rate that you hit 24 hours of credits after 30 days of operation. Miscellaneous other details of this PR: - a new TX extra tag is used for the state change (including deregistrations). The old extra tag has no version or type tag, so couldn't be reused. The data in the new tag is slightly more efficiently packed than the old deregistration transaction, so it gets used for deregistrations (starting at the v12 fork) as well. - Correct validator/worker selection required generalizing the shuffle function to be able to shuffle just part of a vector. This lets us stick any down service nodes at the end of the potential list, then select validators by only shuffling the part of the index vector that contains active service indices. Once the validators are selected, the remainder of the list (this time including decommissioned SN indices) is shuffled to select quorum workers to check, thus allowing decommisioned nodes to be randomly included in the nodes to check without being selected as a validator. - Swarm recalculation was not quite right: swarms were recalculated on SN registrations, even if those registrations were include shared node registrations, but *not* recalculated on stakes. Starting with the upgrade this behaviour is fixed (swarms aren't actually used currently and aren't consensus-relevant so recalculating early won't hurt anything). - Details on decomm/dereg are added to RPC info and print_sn/print_sn_status - Slightly improves the % of reward output in the print_sn output by rounding it to two digits, and reserves space in the output string to avoid excessive reallocations. - Adds various debugging at higher debug levels to quorum voting (into all of voting itself, vote transmission, and vote reception). - Reset service node list internal data structure version to 0. The SN list has to be rescanned anyway at upgrade (its size has changed), so we might as well reset the version and remove the version-dependent serialization code. (Note that the affected code here is for SN states in lmdb storage, not for SN-to-SN communication serialization).
2019-06-18 23:57:02 +02:00
{
CHECK_AND_ASSERT_MES(state_change.state == service_nodes::new_state::deregister, false, "internal error: cannot construct an old deregistration for a non-deregistration state change (before hardfork v12)");
field = tx_extra_service_node_deregister_old{state_change};
}
else
{
field = state_change;
}
Service Node Deregister Part 5 (#89) * Retrieve quorum list from height, reviewed * Setup data structures for de/register TX * Submit and validate partial/full deregisters * Add P2P relaying of partial deregistration votes * Code review adjustments for deregistration part 1 - Fix check_tx_semantic - Remove signature_pod as votes are now stored as blobs. Serialization overrides don't intefere with crypto::signature anymore. * deregistration_vote_pool - changed sign/verify interface and removed repeated code * Misc review, fix sign/verify api, vote threshold * Deregister/tx edge case handling for combinatoric votes * core, service_node_list: separated address from service node pubkey * Retrieve quorum list from height, reviewed * Setup data structures for de/register TX * Submit and validate partial/full deregisters * Add P2P relaying of partial deregistration votes * Code review adjustments for deregistration part 1 - Fix check_tx_semantic - Remove signature_pod as votes are now stored as blobs. Serialization overrides don't intefere with crypto::signature anymore. * deregistration_vote_pool - changed sign/verify interface and removed repeated code * Misc review, fix sign/verify api, vote threshold * Deregister/tx edge case handling for combinatoric votes * Store service node lists for the duration of deregister lifetimes * Quorum min/max bug, sort node list, fix node to test list * Change quorum to store acc pub address, fix oob bug * Code review for expiring votes, acc keys to pub_key, improve err msgs * Add early out for is_deregistration_tx and protect against quorum changes * Remove debug code, fix segfault * Remove irrelevant check for tx v3 in blockchain, fix >= height for pruning quorum states Incorrect assumption that a transaction can be kept in the chain if it could eventually become invalid, because if it were the chain would be split and eventually these transaction would be dropped. But also that we should not override the pre-existing logic which handles this case anyway.
2018-07-18 04:42:47 +02:00
Relax deregistration rules The replaces the deregistration mechanism with a new state change mechanism (beginning at the v12 fork) which can change a service node's network status via three potential values (and is extensible in the future to handle more): - deregistered -- this is the same as the existing deregistration; the SN is instantly removed from the SN list. - decommissioned -- this is a sort of temporary deregistration: your SN remains in the service node list, but is removed from the rewards list and from any network duties. - recommissioned -- this tx is sent by a quorum if they observe a decommissioned SN sending uptime proofs again. Upon reception, the SN is reactivated and put on the end of the reward list. Since this is broadening the quorum use, this also renames the relevant quorum to a "obligations" quorum (since it validates SN obligations), while the transactions are "state_change" transactions (since they change the state of a registered SN). The new parameters added to service_node_rules.h control how this works: // Service node decommissioning: as service nodes stay up they earn "credits" (measured in blocks) // towards a future outage. A new service node starts out with INITIAL_CREDIT, and then builds up // CREDIT_PER_DAY for each day the service node remains active up to a maximum of // DECOMMISSION_MAX_CREDIT. // // If a service node stops sending uptime proofs, a quorum will consider whether the service node // has built up enough credits (at least MINIMUM): if so, instead of submitting a deregistration, // it instead submits a decommission. This removes the service node from the list of active // service nodes both for rewards and for any active network duties. If the service node comes // back online (i.e. starts sending the required performance proofs again) before the credits run // out then a quorum will reinstate the service node using a recommission transaction, which adds // the service node back to the bottom of the service node reward list, and resets its accumulated // credits to 0. If it does not come back online within the required number of blocks (i.e. the // accumulated credit at the point of decommissioning) then a quorum will send a permanent // deregistration transaction to the network, starting a 30-day deregistration count down. This commit currently includes values (which are not necessarily finalized): - 8 hours (240 blocks) of credit required for activation of a decommission (rather than a deregister) - 0 initial credits at registration - a maximum of 24 hours (720 blocks) of credits - credits accumulate at a rate that you hit 24 hours of credits after 30 days of operation. Miscellaneous other details of this PR: - a new TX extra tag is used for the state change (including deregistrations). The old extra tag has no version or type tag, so couldn't be reused. The data in the new tag is slightly more efficiently packed than the old deregistration transaction, so it gets used for deregistrations (starting at the v12 fork) as well. - Correct validator/worker selection required generalizing the shuffle function to be able to shuffle just part of a vector. This lets us stick any down service nodes at the end of the potential list, then select validators by only shuffling the part of the index vector that contains active service indices. Once the validators are selected, the remainder of the list (this time including decommissioned SN indices) is shuffled to select quorum workers to check, thus allowing decommisioned nodes to be randomly included in the nodes to check without being selected as a validator. - Swarm recalculation was not quite right: swarms were recalculated on SN registrations, even if those registrations were include shared node registrations, but *not* recalculated on stakes. Starting with the upgrade this behaviour is fixed (swarms aren't actually used currently and aren't consensus-relevant so recalculating early won't hurt anything). - Details on decomm/dereg are added to RPC info and print_sn/print_sn_status - Slightly improves the % of reward output in the print_sn output by rounding it to two digits, and reserves space in the output string to avoid excessive reallocations. - Adds various debugging at higher debug levels to quorum voting (into all of voting itself, vote transmission, and vote reception). - Reset service node list internal data structure version to 0. The SN list has to be rescanned anyway at upgrade (its size has changed), so we might as well reset the version and remove the version-dependent serialization code. (Note that the affected code here is for SN states in lmdb storage, not for SN-to-SN communication serialization).
2019-06-18 23:57:02 +02:00
bool r = add_tx_extra_field_to_tx_extra(tx_extra, field);
CHECK_AND_ASSERT_MES(r, false, "failed to serialize tx extra service node state change");
2018-06-29 06:47:00 +02:00
return true;
}
Relax deregistration rules The replaces the deregistration mechanism with a new state change mechanism (beginning at the v12 fork) which can change a service node's network status via three potential values (and is extensible in the future to handle more): - deregistered -- this is the same as the existing deregistration; the SN is instantly removed from the SN list. - decommissioned -- this is a sort of temporary deregistration: your SN remains in the service node list, but is removed from the rewards list and from any network duties. - recommissioned -- this tx is sent by a quorum if they observe a decommissioned SN sending uptime proofs again. Upon reception, the SN is reactivated and put on the end of the reward list. Since this is broadening the quorum use, this also renames the relevant quorum to a "obligations" quorum (since it validates SN obligations), while the transactions are "state_change" transactions (since they change the state of a registered SN). The new parameters added to service_node_rules.h control how this works: // Service node decommissioning: as service nodes stay up they earn "credits" (measured in blocks) // towards a future outage. A new service node starts out with INITIAL_CREDIT, and then builds up // CREDIT_PER_DAY for each day the service node remains active up to a maximum of // DECOMMISSION_MAX_CREDIT. // // If a service node stops sending uptime proofs, a quorum will consider whether the service node // has built up enough credits (at least MINIMUM): if so, instead of submitting a deregistration, // it instead submits a decommission. This removes the service node from the list of active // service nodes both for rewards and for any active network duties. If the service node comes // back online (i.e. starts sending the required performance proofs again) before the credits run // out then a quorum will reinstate the service node using a recommission transaction, which adds // the service node back to the bottom of the service node reward list, and resets its accumulated // credits to 0. If it does not come back online within the required number of blocks (i.e. the // accumulated credit at the point of decommissioning) then a quorum will send a permanent // deregistration transaction to the network, starting a 30-day deregistration count down. This commit currently includes values (which are not necessarily finalized): - 8 hours (240 blocks) of credit required for activation of a decommission (rather than a deregister) - 0 initial credits at registration - a maximum of 24 hours (720 blocks) of credits - credits accumulate at a rate that you hit 24 hours of credits after 30 days of operation. Miscellaneous other details of this PR: - a new TX extra tag is used for the state change (including deregistrations). The old extra tag has no version or type tag, so couldn't be reused. The data in the new tag is slightly more efficiently packed than the old deregistration transaction, so it gets used for deregistrations (starting at the v12 fork) as well. - Correct validator/worker selection required generalizing the shuffle function to be able to shuffle just part of a vector. This lets us stick any down service nodes at the end of the potential list, then select validators by only shuffling the part of the index vector that contains active service indices. Once the validators are selected, the remainder of the list (this time including decommissioned SN indices) is shuffled to select quorum workers to check, thus allowing decommisioned nodes to be randomly included in the nodes to check without being selected as a validator. - Swarm recalculation was not quite right: swarms were recalculated on SN registrations, even if those registrations were include shared node registrations, but *not* recalculated on stakes. Starting with the upgrade this behaviour is fixed (swarms aren't actually used currently and aren't consensus-relevant so recalculating early won't hurt anything). - Details on decomm/dereg are added to RPC info and print_sn/print_sn_status - Slightly improves the % of reward output in the print_sn output by rounding it to two digits, and reserves space in the output string to avoid excessive reallocations. - Adds various debugging at higher debug levels to quorum voting (into all of voting itself, vote transmission, and vote reception). - Reset service node list internal data structure version to 0. The SN list has to be rescanned anyway at upgrade (its size has changed), so we might as well reset the version and remove the version-dependent serialization code. (Note that the affected code here is for SN states in lmdb storage, not for SN-to-SN communication serialization).
2019-06-18 23:57:02 +02:00
2018-06-29 06:47:00 +02:00
//---------------------------------------------------------------
void add_service_node_pubkey_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::public_key& pubkey)
{
add_tx_extra<tx_extra_service_node_pubkey>(tx_extra, pubkey);
}
//---------------------------------------------------------------
bool get_service_node_pubkey_from_tx_extra(const std::vector<uint8_t>& tx_extra, crypto::public_key& pubkey)
{
tx_extra_service_node_pubkey pk;
if (!get_field_from_tx_extra(tx_extra, pk))
return false;
pubkey = pk.m_service_node_key;
return true;
}
//---------------------------------------------------------------
void add_service_node_contributor_to_tx_extra(std::vector<uint8_t>& tx_extra, const cryptonote::account_public_address& address)
{
add_tx_extra<tx_extra_service_node_contributor>(tx_extra, address);
}
//---------------------------------------------------------------
2018-08-03 10:34:49 +02:00
bool get_tx_secret_key_from_tx_extra(const std::vector<uint8_t>& tx_extra, crypto::secret_key& key)
{
tx_extra_tx_secret_key seckey;
if (!get_field_from_tx_extra(tx_extra, seckey))
2018-08-03 10:34:49 +02:00
return false;
key = seckey.key;
return true;
}
//---------------------------------------------------------------
void add_tx_secret_key_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::secret_key& key)
{
add_tx_extra<tx_extra_tx_secret_key>(tx_extra, key);
Infinite Staking Part 1 (#387) * Remove dead branches in hot-path check_tx_inputs Also renames #define for mixins to better match naming convention * Shuffle around some more code into common branches * Fix min/max tx version rules, since there 1 tx v2 on v9 fork * First draft infinite staking implementation * Actually generate the right key image and expire appropriately * Add framework to lock key images after expiry * Return locked key images for nodes, add request unlock option * Introduce transaction types for key image unlock * Update validation steps to accept tx types, key_image_unlock * Add mapping for lockable key images to amounts * Change inconsistent naming scheme of contributors * Create key image unlock transaction type and process it * Update tx params to allow v4 types and as a result construct_tx* * Fix some serialisation issues not sending all the information * Fix dupe tx extra tag causing incorrect deserialisation * Add warning comments * Fix key image unlocks parsing error * Simplify key image proof checks * Fix rebase errors * Correctly calculate the key image unlock times * Blacklist key image on deregistration * Serialise key image blacklist * Rollback blacklisted key images * Fix expiry logic error * Disallow requesting stake unlock if already unlocked client side * Add double spend checks for key image unlocks * Rename get_staking_requirement_lock_blocks To staking_initial_num_lock_blocks * Begin modifying output selection to not use locked outputs * Modify output selection to avoid locked/blacklisted key images * Cleanup and undoing some protocol breakages * Simplify expiration of nodes * Request unlock schedules entire node for expiration * Fix off by one in expiring nodes * Undo expiring code for pre v10 nodes * Fix RPC returning register as unlock height and not checking 0 * Rename key image unlock height const * Undo testnet hardfork debug changes * Remove is_type for get_type, fix missing var rename * Move serialisable data into public namespace * Serialise tx types properly * Fix typo in no service node known msg * Code review * Fix == to >= on serialising tx type * Code review 2 * Fix tests and key image unlock * Add additional test, fix assert * Remove debug code in wallet * Fix merge dev problem
2019-01-25 04:15:52 +01:00
}
//---------------------------------------------------------------
bool add_tx_key_image_proofs_to_tx_extra(std::vector<uint8_t>& tx_extra, const tx_extra_tx_key_image_proofs& proofs)
{
tx_extra_field field = proofs;
if (!add_tx_extra_field_to_tx_extra(tx_extra, field))
{
log::info(logcat, "failed to serialize tx extra tx key image proof");
return false;
}
return true;
Infinite Staking Part 1 (#387) * Remove dead branches in hot-path check_tx_inputs Also renames #define for mixins to better match naming convention * Shuffle around some more code into common branches * Fix min/max tx version rules, since there 1 tx v2 on v9 fork * First draft infinite staking implementation * Actually generate the right key image and expire appropriately * Add framework to lock key images after expiry * Return locked key images for nodes, add request unlock option * Introduce transaction types for key image unlock * Update validation steps to accept tx types, key_image_unlock * Add mapping for lockable key images to amounts * Change inconsistent naming scheme of contributors * Create key image unlock transaction type and process it * Update tx params to allow v4 types and as a result construct_tx* * Fix some serialisation issues not sending all the information * Fix dupe tx extra tag causing incorrect deserialisation * Add warning comments * Fix key image unlocks parsing error * Simplify key image proof checks * Fix rebase errors * Correctly calculate the key image unlock times * Blacklist key image on deregistration * Serialise key image blacklist * Rollback blacklisted key images * Fix expiry logic error * Disallow requesting stake unlock if already unlocked client side * Add double spend checks for key image unlocks * Rename get_staking_requirement_lock_blocks To staking_initial_num_lock_blocks * Begin modifying output selection to not use locked outputs * Modify output selection to avoid locked/blacklisted key images * Cleanup and undoing some protocol breakages * Simplify expiration of nodes * Request unlock schedules entire node for expiration * Fix off by one in expiring nodes * Undo expiring code for pre v10 nodes * Fix RPC returning register as unlock height and not checking 0 * Rename key image unlock height const * Undo testnet hardfork debug changes * Remove is_type for get_type, fix missing var rename * Move serialisable data into public namespace * Serialise tx types properly * Fix typo in no service node known msg * Code review * Fix == to >= on serialising tx type * Code review 2 * Fix tests and key image unlock * Add additional test, fix assert * Remove debug code in wallet * Fix merge dev problem
2019-01-25 04:15:52 +01:00
}
//---------------------------------------------------------------
bool add_tx_key_image_unlock_to_tx_extra(std::vector<uint8_t>& tx_extra, const tx_extra_tx_key_image_unlock& unlock)
{
tx_extra_field field = unlock;
if (!add_tx_extra_field_to_tx_extra(tx_extra, field))
{
log::info(logcat, "failed to serialize tx extra tx key image unlock");
return false;
}
return true;
Infinite Staking Part 1 (#387) * Remove dead branches in hot-path check_tx_inputs Also renames #define for mixins to better match naming convention * Shuffle around some more code into common branches * Fix min/max tx version rules, since there 1 tx v2 on v9 fork * First draft infinite staking implementation * Actually generate the right key image and expire appropriately * Add framework to lock key images after expiry * Return locked key images for nodes, add request unlock option * Introduce transaction types for key image unlock * Update validation steps to accept tx types, key_image_unlock * Add mapping for lockable key images to amounts * Change inconsistent naming scheme of contributors * Create key image unlock transaction type and process it * Update tx params to allow v4 types and as a result construct_tx* * Fix some serialisation issues not sending all the information * Fix dupe tx extra tag causing incorrect deserialisation * Add warning comments * Fix key image unlocks parsing error * Simplify key image proof checks * Fix rebase errors * Correctly calculate the key image unlock times * Blacklist key image on deregistration * Serialise key image blacklist * Rollback blacklisted key images * Fix expiry logic error * Disallow requesting stake unlock if already unlocked client side * Add double spend checks for key image unlocks * Rename get_staking_requirement_lock_blocks To staking_initial_num_lock_blocks * Begin modifying output selection to not use locked outputs * Modify output selection to avoid locked/blacklisted key images * Cleanup and undoing some protocol breakages * Simplify expiration of nodes * Request unlock schedules entire node for expiration * Fix off by one in expiring nodes * Undo expiring code for pre v10 nodes * Fix RPC returning register as unlock height and not checking 0 * Rename key image unlock height const * Undo testnet hardfork debug changes * Remove is_type for get_type, fix missing var rename * Move serialisable data into public namespace * Serialise tx types properly * Fix typo in no service node known msg * Code review * Fix == to >= on serialising tx type * Code review 2 * Fix tests and key image unlock * Add additional test, fix assert * Remove debug code in wallet * Fix merge dev problem
2019-01-25 04:15:52 +01:00
}
//---------------------------------------------------------------
bool get_service_node_contributor_from_tx_extra(const std::vector<uint8_t>& tx_extra, cryptonote::account_public_address& address)
{
tx_extra_service_node_contributor contributor;
if (!get_field_from_tx_extra(tx_extra, contributor))
return false;
address.m_spend_public_key = contributor.m_spend_public_key;
address.m_view_public_key = contributor.m_view_public_key;
return true;
}
//---------------------------------------------------------------
Atomic staking amounts This adds a new tx registration interpretation for HF19+ by repurposing the fields of the registration: - `expiration` becomes `hf_or_expiration`; for "new" registrations it contains the hardfork number (e.g. 19 for HF19), but the data layout on chain doesn't change: essentially we determine whether it's a new registration based on whether the field is <= 255 (interpret as HF) or not (interpret as expiration). Changes in "new" registrations: - stake amounts are atomic OXEN rather than portions. This lets us skip a whole bunch of fiddling around with amounts that was necessary to deal with integer truncation when converting between amounts and portions. - the fee in the registration data is now a value out of 10000 instead of a portion (i.e. value out of 2^64-4). This limits fee precision to a percentage with two decimal places instead of ~17 decimal places. Internally we still convert this to a portion when processing the registration for service_node_states, but this makes the registration itself much simpler and easier to work with (as a human). - HF19+ registrations no longer have an expiry timestamp (though they do depend on the hardfork, so they "expire" whenever the next hard fork). The expiry timestamp was really only there to avoid a registration amount decreasing too much from the dropping staking requirement. - Both types are registration are still permitted for HF19, but because registrations with more than 4 contributors expose bugs in the portion transfer code (that results in registrations become invalid), old-style registrations are still limited to 4 contributors. - HF19 will allow both old and new registrations, so that registrations generated before the HF will still work, and so that we don't break testnet which has various "old" registrations on it.
2022-05-19 02:44:57 +02:00
bool add_service_node_registration_to_tx_extra(std::vector<uint8_t>& tx_extra, const service_nodes::registration_details& reg)
{
Atomic staking amounts This adds a new tx registration interpretation for HF19+ by repurposing the fields of the registration: - `expiration` becomes `hf_or_expiration`; for "new" registrations it contains the hardfork number (e.g. 19 for HF19), but the data layout on chain doesn't change: essentially we determine whether it's a new registration based on whether the field is <= 255 (interpret as HF) or not (interpret as expiration). Changes in "new" registrations: - stake amounts are atomic OXEN rather than portions. This lets us skip a whole bunch of fiddling around with amounts that was necessary to deal with integer truncation when converting between amounts and portions. - the fee in the registration data is now a value out of 10000 instead of a portion (i.e. value out of 2^64-4). This limits fee precision to a percentage with two decimal places instead of ~17 decimal places. Internally we still convert this to a portion when processing the registration for service_node_states, but this makes the registration itself much simpler and easier to work with (as a human). - HF19+ registrations no longer have an expiry timestamp (though they do depend on the hardfork, so they "expire" whenever the next hard fork). The expiry timestamp was really only there to avoid a registration amount decreasing too much from the dropping staking requirement. - Both types are registration are still permitted for HF19, but because registrations with more than 4 contributors expose bugs in the portion transfer code (that results in registrations become invalid), old-style registrations are still limited to 4 contributors. - HF19 will allow both old and new registrations, so that registrations generated before the HF will still work, and so that we don't break testnet which has various "old" registrations on it.
2022-05-19 02:44:57 +02:00
tx_extra_field field;
auto& txreg = field.emplace<tx_extra_service_node_register>();
txreg.amounts.reserve(reg.reserved.size());
txreg.public_spend_keys.reserve(reg.reserved.size());
txreg.public_view_keys.reserve(reg.reserved.size());
for (const auto& [addr, amount] : reg.reserved)
{
Atomic staking amounts This adds a new tx registration interpretation for HF19+ by repurposing the fields of the registration: - `expiration` becomes `hf_or_expiration`; for "new" registrations it contains the hardfork number (e.g. 19 for HF19), but the data layout on chain doesn't change: essentially we determine whether it's a new registration based on whether the field is <= 255 (interpret as HF) or not (interpret as expiration). Changes in "new" registrations: - stake amounts are atomic OXEN rather than portions. This lets us skip a whole bunch of fiddling around with amounts that was necessary to deal with integer truncation when converting between amounts and portions. - the fee in the registration data is now a value out of 10000 instead of a portion (i.e. value out of 2^64-4). This limits fee precision to a percentage with two decimal places instead of ~17 decimal places. Internally we still convert this to a portion when processing the registration for service_node_states, but this makes the registration itself much simpler and easier to work with (as a human). - HF19+ registrations no longer have an expiry timestamp (though they do depend on the hardfork, so they "expire" whenever the next hard fork). The expiry timestamp was really only there to avoid a registration amount decreasing too much from the dropping staking requirement. - Both types are registration are still permitted for HF19, but because registrations with more than 4 contributors expose bugs in the portion transfer code (that results in registrations become invalid), old-style registrations are still limited to 4 contributors. - HF19 will allow both old and new registrations, so that registrations generated before the HF will still work, and so that we don't break testnet which has various "old" registrations on it.
2022-05-19 02:44:57 +02:00
txreg.public_spend_keys.push_back(addr.m_spend_public_key);
txreg.public_view_keys.push_back(addr.m_view_public_key);
txreg.amounts.push_back(amount);
}
Atomic staking amounts This adds a new tx registration interpretation for HF19+ by repurposing the fields of the registration: - `expiration` becomes `hf_or_expiration`; for "new" registrations it contains the hardfork number (e.g. 19 for HF19), but the data layout on chain doesn't change: essentially we determine whether it's a new registration based on whether the field is <= 255 (interpret as HF) or not (interpret as expiration). Changes in "new" registrations: - stake amounts are atomic OXEN rather than portions. This lets us skip a whole bunch of fiddling around with amounts that was necessary to deal with integer truncation when converting between amounts and portions. - the fee in the registration data is now a value out of 10000 instead of a portion (i.e. value out of 2^64-4). This limits fee precision to a percentage with two decimal places instead of ~17 decimal places. Internally we still convert this to a portion when processing the registration for service_node_states, but this makes the registration itself much simpler and easier to work with (as a human). - HF19+ registrations no longer have an expiry timestamp (though they do depend on the hardfork, so they "expire" whenever the next hard fork). The expiry timestamp was really only there to avoid a registration amount decreasing too much from the dropping staking requirement. - Both types are registration are still permitted for HF19, but because registrations with more than 4 contributors expose bugs in the portion transfer code (that results in registrations become invalid), old-style registrations are still limited to 4 contributors. - HF19 will allow both old and new registrations, so that registrations generated before the HF will still work, and so that we don't break testnet which has various "old" registrations on it.
2022-05-19 02:44:57 +02:00
txreg.fee = reg.fee;
txreg.hf_or_expiration = reg.hf;
txreg.signature = reg.signature;
Infinite Staking Part 1 (#387) * Remove dead branches in hot-path check_tx_inputs Also renames #define for mixins to better match naming convention * Shuffle around some more code into common branches * Fix min/max tx version rules, since there 1 tx v2 on v9 fork * First draft infinite staking implementation * Actually generate the right key image and expire appropriately * Add framework to lock key images after expiry * Return locked key images for nodes, add request unlock option * Introduce transaction types for key image unlock * Update validation steps to accept tx types, key_image_unlock * Add mapping for lockable key images to amounts * Change inconsistent naming scheme of contributors * Create key image unlock transaction type and process it * Update tx params to allow v4 types and as a result construct_tx* * Fix some serialisation issues not sending all the information * Fix dupe tx extra tag causing incorrect deserialisation * Add warning comments * Fix key image unlocks parsing error * Simplify key image proof checks * Fix rebase errors * Correctly calculate the key image unlock times * Blacklist key image on deregistration * Serialise key image blacklist * Rollback blacklisted key images * Fix expiry logic error * Disallow requesting stake unlock if already unlocked client side * Add double spend checks for key image unlocks * Rename get_staking_requirement_lock_blocks To staking_initial_num_lock_blocks * Begin modifying output selection to not use locked outputs * Modify output selection to avoid locked/blacklisted key images * Cleanup and undoing some protocol breakages * Simplify expiration of nodes * Request unlock schedules entire node for expiration * Fix off by one in expiring nodes * Undo expiring code for pre v10 nodes * Fix RPC returning register as unlock height and not checking 0 * Rename key image unlock height const * Undo testnet hardfork debug changes * Remove is_type for get_type, fix missing var rename * Move serialisable data into public namespace * Serialise tx types properly * Fix typo in no service node known msg * Code review * Fix == to >= on serialising tx type * Code review 2 * Fix tests and key image unlock * Add additional test, fix assert * Remove debug code in wallet * Fix merge dev problem
2019-01-25 04:15:52 +01:00
if (!add_tx_extra_field_to_tx_extra(tx_extra, field))
{
log::info(logcat, "failed to serialize tx extra registration tx");
return false;
}
return true;
}
//---------------------------------------------------------------
void add_service_node_winner_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::public_key& winner)
{
add_tx_extra<tx_extra_service_node_winner>(tx_extra, winner);
2018-06-29 06:47:00 +02:00
}
//---------------------------------------------------------------
bool get_service_node_state_change_from_tx_extra(const std::vector<uint8_t>& tx_extra, tx_extra_service_node_state_change &state_change, const hf hf_version)
{
if (hf_version >= hf::hf12_checkpointing) {
Relax deregistration rules The replaces the deregistration mechanism with a new state change mechanism (beginning at the v12 fork) which can change a service node's network status via three potential values (and is extensible in the future to handle more): - deregistered -- this is the same as the existing deregistration; the SN is instantly removed from the SN list. - decommissioned -- this is a sort of temporary deregistration: your SN remains in the service node list, but is removed from the rewards list and from any network duties. - recommissioned -- this tx is sent by a quorum if they observe a decommissioned SN sending uptime proofs again. Upon reception, the SN is reactivated and put on the end of the reward list. Since this is broadening the quorum use, this also renames the relevant quorum to a "obligations" quorum (since it validates SN obligations), while the transactions are "state_change" transactions (since they change the state of a registered SN). The new parameters added to service_node_rules.h control how this works: // Service node decommissioning: as service nodes stay up they earn "credits" (measured in blocks) // towards a future outage. A new service node starts out with INITIAL_CREDIT, and then builds up // CREDIT_PER_DAY for each day the service node remains active up to a maximum of // DECOMMISSION_MAX_CREDIT. // // If a service node stops sending uptime proofs, a quorum will consider whether the service node // has built up enough credits (at least MINIMUM): if so, instead of submitting a deregistration, // it instead submits a decommission. This removes the service node from the list of active // service nodes both for rewards and for any active network duties. If the service node comes // back online (i.e. starts sending the required performance proofs again) before the credits run // out then a quorum will reinstate the service node using a recommission transaction, which adds // the service node back to the bottom of the service node reward list, and resets its accumulated // credits to 0. If it does not come back online within the required number of blocks (i.e. the // accumulated credit at the point of decommissioning) then a quorum will send a permanent // deregistration transaction to the network, starting a 30-day deregistration count down. This commit currently includes values (which are not necessarily finalized): - 8 hours (240 blocks) of credit required for activation of a decommission (rather than a deregister) - 0 initial credits at registration - a maximum of 24 hours (720 blocks) of credits - credits accumulate at a rate that you hit 24 hours of credits after 30 days of operation. Miscellaneous other details of this PR: - a new TX extra tag is used for the state change (including deregistrations). The old extra tag has no version or type tag, so couldn't be reused. The data in the new tag is slightly more efficiently packed than the old deregistration transaction, so it gets used for deregistrations (starting at the v12 fork) as well. - Correct validator/worker selection required generalizing the shuffle function to be able to shuffle just part of a vector. This lets us stick any down service nodes at the end of the potential list, then select validators by only shuffling the part of the index vector that contains active service indices. Once the validators are selected, the remainder of the list (this time including decommissioned SN indices) is shuffled to select quorum workers to check, thus allowing decommisioned nodes to be randomly included in the nodes to check without being selected as a validator. - Swarm recalculation was not quite right: swarms were recalculated on SN registrations, even if those registrations were include shared node registrations, but *not* recalculated on stakes. Starting with the upgrade this behaviour is fixed (swarms aren't actually used currently and aren't consensus-relevant so recalculating early won't hurt anything). - Details on decomm/dereg are added to RPC info and print_sn/print_sn_status - Slightly improves the % of reward output in the print_sn output by rounding it to two digits, and reserves space in the output string to avoid excessive reallocations. - Adds various debugging at higher debug levels to quorum voting (into all of voting itself, vote transmission, and vote reception). - Reset service node list internal data structure version to 0. The SN list has to be rescanned anyway at upgrade (its size has changed), so we might as well reset the version and remove the version-dependent serialization code. (Note that the affected code here is for SN states in lmdb storage, not for SN-to-SN communication serialization).
2019-06-18 23:57:02 +02:00
// Look for a new-style state change field:
return get_field_from_tx_extra(tx_extra, state_change);
Relax deregistration rules The replaces the deregistration mechanism with a new state change mechanism (beginning at the v12 fork) which can change a service node's network status via three potential values (and is extensible in the future to handle more): - deregistered -- this is the same as the existing deregistration; the SN is instantly removed from the SN list. - decommissioned -- this is a sort of temporary deregistration: your SN remains in the service node list, but is removed from the rewards list and from any network duties. - recommissioned -- this tx is sent by a quorum if they observe a decommissioned SN sending uptime proofs again. Upon reception, the SN is reactivated and put on the end of the reward list. Since this is broadening the quorum use, this also renames the relevant quorum to a "obligations" quorum (since it validates SN obligations), while the transactions are "state_change" transactions (since they change the state of a registered SN). The new parameters added to service_node_rules.h control how this works: // Service node decommissioning: as service nodes stay up they earn "credits" (measured in blocks) // towards a future outage. A new service node starts out with INITIAL_CREDIT, and then builds up // CREDIT_PER_DAY for each day the service node remains active up to a maximum of // DECOMMISSION_MAX_CREDIT. // // If a service node stops sending uptime proofs, a quorum will consider whether the service node // has built up enough credits (at least MINIMUM): if so, instead of submitting a deregistration, // it instead submits a decommission. This removes the service node from the list of active // service nodes both for rewards and for any active network duties. If the service node comes // back online (i.e. starts sending the required performance proofs again) before the credits run // out then a quorum will reinstate the service node using a recommission transaction, which adds // the service node back to the bottom of the service node reward list, and resets its accumulated // credits to 0. If it does not come back online within the required number of blocks (i.e. the // accumulated credit at the point of decommissioning) then a quorum will send a permanent // deregistration transaction to the network, starting a 30-day deregistration count down. This commit currently includes values (which are not necessarily finalized): - 8 hours (240 blocks) of credit required for activation of a decommission (rather than a deregister) - 0 initial credits at registration - a maximum of 24 hours (720 blocks) of credits - credits accumulate at a rate that you hit 24 hours of credits after 30 days of operation. Miscellaneous other details of this PR: - a new TX extra tag is used for the state change (including deregistrations). The old extra tag has no version or type tag, so couldn't be reused. The data in the new tag is slightly more efficiently packed than the old deregistration transaction, so it gets used for deregistrations (starting at the v12 fork) as well. - Correct validator/worker selection required generalizing the shuffle function to be able to shuffle just part of a vector. This lets us stick any down service nodes at the end of the potential list, then select validators by only shuffling the part of the index vector that contains active service indices. Once the validators are selected, the remainder of the list (this time including decommissioned SN indices) is shuffled to select quorum workers to check, thus allowing decommisioned nodes to be randomly included in the nodes to check without being selected as a validator. - Swarm recalculation was not quite right: swarms were recalculated on SN registrations, even if those registrations were include shared node registrations, but *not* recalculated on stakes. Starting with the upgrade this behaviour is fixed (swarms aren't actually used currently and aren't consensus-relevant so recalculating early won't hurt anything). - Details on decomm/dereg are added to RPC info and print_sn/print_sn_status - Slightly improves the % of reward output in the print_sn output by rounding it to two digits, and reserves space in the output string to avoid excessive reallocations. - Adds various debugging at higher debug levels to quorum voting (into all of voting itself, vote transmission, and vote reception). - Reset service node list internal data structure version to 0. The SN list has to be rescanned anyway at upgrade (its size has changed), so we might as well reset the version and remove the version-dependent serialization code. (Note that the affected code here is for SN states in lmdb storage, not for SN-to-SN communication serialization).
2019-06-18 23:57:02 +02:00
}
// v11 or earlier; parse the old style and copy into a new style
tx_extra_service_node_deregister_old dereg;
if (!get_field_from_tx_extra(tx_extra, dereg))
return false;
state_change = tx_extra_service_node_state_change{
tx_extra_service_node_state_change::version_t::v0,
service_nodes::new_state::deregister, dereg.block_height, dereg.service_node_index, 0, 0, {dereg.votes.begin(), dereg.votes.end()}};
return true;
}
//---------------------------------------------------------------
crypto::public_key get_service_node_winner_from_tx_extra(const std::vector<uint8_t>& tx_extra)
{
// find corresponding field
tx_extra_service_node_winner winner;
if (get_field_from_tx_extra(tx_extra, winner))
return winner.m_service_node_key;
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
return null<public_key>;
}
//---------------------------------------------------------------
2021-01-04 01:09:45 +01:00
void add_oxen_name_system_to_tx_extra(std::vector<uint8_t> &tx_extra, tx_extra_oxen_name_system const &entry)
{
tx_extra_field field = entry;
add_tx_extra_field_to_tx_extra(tx_extra, field);
}
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
bool remove_field_from_tx_extra(std::vector<uint8_t>& tx_extra, const size_t variant_index)
{
if (tx_extra.empty())
return true;
2020-06-02 05:08:48 +02:00
serialization::binary_string_unarchiver ar{tx_extra};
serialization::binary_string_archiver newar;
try {
do
{
tx_extra_field field;
value(ar, field);
if (field.index() != variant_index)
value(newar, field);
} while (ar.remaining_bytes() > 0);
} catch (const std::exception& e) {
log::info(logcat, "{}: failed to deserialize extra field: {}; extra = {}", __func__, e.what(), oxenc::to_hex(tx_extra.begin(), tx_extra.end()));
2020-06-02 05:08:48 +02:00
return false;
}
2020-06-02 05:08:48 +02:00
std::string s = newar.str();
tx_extra.clear();
tx_extra.reserve(s.size());
std::copy(s.begin(), s.end(), std::back_inserter(tx_extra));
return true;
}
//---------------------------------------------------------------
2022-05-20 23:29:48 +02:00
void set_payment_id_to_tx_extra_nonce(std::string& extra_nonce, const crypto::hash& payment_id)
2014-05-03 18:19:43 +02:00
{
extra_nonce.clear();
extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID);
2014-05-03 18:19:43 +02:00
const uint8_t* payment_id_ptr = reinterpret_cast<const uint8_t*>(&payment_id);
std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce));
}
//---------------------------------------------------------------
2022-05-20 23:29:48 +02:00
void set_encrypted_payment_id_to_tx_extra_nonce(std::string& extra_nonce, const crypto::hash8& payment_id)
{
extra_nonce.clear();
extra_nonce.push_back(TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID);
const uint8_t* payment_id_ptr = reinterpret_cast<const uint8_t*>(&payment_id);
std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce));
}
//---------------------------------------------------------------
2022-05-20 23:29:48 +02:00
bool get_payment_id_from_tx_extra_nonce(const std::string& extra_nonce, crypto::hash& payment_id)
2014-05-03 18:19:43 +02:00
{
if(sizeof(crypto::hash) + 1 != extra_nonce.size())
return false;
if(TX_EXTRA_NONCE_PAYMENT_ID != extra_nonce[0])
2014-05-03 18:19:43 +02:00
return false;
payment_id = *reinterpret_cast<const crypto::hash*>(extra_nonce.data() + 1);
return true;
}
//---------------------------------------------------------------
2022-05-20 23:29:48 +02:00
bool get_encrypted_payment_id_from_tx_extra_nonce(const std::string& extra_nonce, crypto::hash8& payment_id)
{
if(sizeof(crypto::hash8) + 1 != extra_nonce.size())
return false;
if (TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID != extra_nonce[0])
return false;
payment_id = *reinterpret_cast<const crypto::hash8*>(extra_nonce.data() + 1);
return true;
}
//---------------------------------------------------------------
uint64_t get_burned_amount_from_tx_extra(const std::vector<uint8_t>& tx_extra)
{
tx_extra_burn burn;
if (get_field_from_tx_extra(tx_extra, burn))
return burn.amount;
return 0;
}
//---------------------------------------------------------------
bool add_burned_amount_to_tx_extra(std::vector<uint8_t>& tx_extra, uint64_t burn)
{
tx_extra_field field = tx_extra_burn{burn};
if (!add_tx_extra_field_to_tx_extra(tx_extra, field))
{
log::info(logcat, "failed to serialize tx extra burn amount");
return false;
}
return true;
}
//---------------------------------------------------------------
2014-03-03 23:07:58 +01:00
bool get_inputs_money_amount(const transaction& tx, uint64_t& money)
{
money = 0;
for(const auto& in: tx.vin)
2014-03-03 23:07:58 +01:00
{
CHECKED_GET_SPECIFIC_VARIANT(in, txin_to_key, tokey_in, false);
2014-03-03 23:07:58 +01:00
money += tokey_in.amount;
}
return true;
}
//---------------------------------------------------------------
uint64_t get_block_height(const block& b)
{
cryptonote::block bl = b;
if (b.miner_tx.vout.size() > 0)
{
CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, 0, "wrong miner tx in block: " << get_block_hash(b) << ", b.miner_tx.vin.size() != 1 (size is: " << b.miner_tx.vin.size() << ")");
CHECKED_GET_SPECIFIC_VARIANT(b.miner_tx.vin[0], txin_gen, coinbase_in, 0);
2022-05-20 22:55:07 +02:00
if (b.major_version >= hf::hf19_reward_batching)
{
CHECK_AND_ASSERT_MES(coinbase_in.height == b.height, 0, "wrong miner tx in block: " << get_block_hash(b));
}
return coinbase_in.height;
} else {
return b.height;
}
2014-03-03 23:07:58 +01:00
}
//---------------------------------------------------------------
bool check_inputs_types_supported(const transaction& tx)
{
for(const auto& in: tx.vin)
2014-03-03 23:07:58 +01:00
{
CHECK_AND_ASSERT_MES(std::holds_alternative<txin_to_key>(in), false, "wrong variant type: "
<< tools::type_name(tools::variant_type(in)) << ", expected " << tools::type_name<txin_to_key>()
2014-03-03 23:07:58 +01:00
<< ", in transaction id=" << get_transaction_hash(tx));
}
return true;
}
//-----------------------------------------------------------------------------------------------
bool check_outs_valid(const transaction& tx)
{
if (!tx.is_transfer() && tx.vout.size() != 0)
{
log::warning(logcat, "tx type: {} must have 0 outputs, received: {}, id={}", tx.type, tx.vout.size(), get_transaction_hash(tx));
return false;
}
if (tx.version >= txversion::v3_per_output_unlock_times && tx.vout.size() != tx.output_unlock_times.size())
{
log::warning(logcat, "tx version: {} must have equal number of output unlock times and outputs", tx.version);
return false;
}
for(const tx_out& out: tx.vout)
2014-03-03 23:07:58 +01:00
{
CHECK_AND_ASSERT_MES(std::holds_alternative<txout_to_key>(out.target), false, "wrong variant type: "
<< tools::type_name(tools::variant_type(out.target)) << ", expected " << tools::type_name<txout_to_key>()
2014-03-03 23:07:58 +01:00
<< ", in transaction id=" << get_transaction_hash(tx));
Make tx type and version scoped enums This converts the transaction type and version to scoped enum, giving type safety and making the tx type assignment less error prone because there is no implicit conversion or comparison with raw integers that has to be worried about. This ends up converting any use of `cryptonote::transaction::type_xyz` to `cryptonote::transaction::txtype::xyz`. For version, names like `transaction::version_v4` become `cryptonote::txversion::v4_tx_types`. This also allows/includes various other simplifications related to or enabled by this change: - handle `is_deregister` dynamically in serialization code (setting `type::standard` or `type::deregister` rather than using a version-determined union) - `get_type()` is no longer needed with the above change: it is now much simpler to directly access `type` which will always have the correct value (even for v2 or v3 transaction types). And though there was an assertion on the enum value, `get_type()` was being used only sporadically: many places accessed `.type` directly. - the old unscoped enum didn't have a type but was assumed castable to/from `uint16_t`, which technically meant there was potential undefined behaviour when deserializing any type values >= 8. - tx type range checks weren't being done in all serialization paths; they are now. Because `get_type()` was not used everywhere (lots of places simply accessed `.type` directory) these might not have been caught. - `set_type()` is not needed; it was only being used in a single place (wallet2.cpp) and only for v4 txes, so the version protection code was never doing anything. - added a std::ostream << operator for the enum types so that they can be output with `<< tx_type <<` rather than needing to wrap it in `type_to_string(tx_type)` everywhere. For the versions, you get the annotated version string (e.g. 4_tx_types) rather than just the number 4.
2019-06-11 20:53:46 +02:00
if (tx.version == txversion::v1)
{
if (out.amount <= 0)
{
log::warning(logcat, "zero amount output in transaction id={}", get_transaction_hash(tx));
return false;
}
}
2014-03-03 23:07:58 +01:00
if(!check_key(var::get<txout_to_key>(out.target).key))
2014-03-03 23:07:58 +01:00
return false;
}
return true;
}
//-----------------------------------------------------------------------------------------------
bool check_money_overflow(const transaction& tx)
{
return check_inputs_overflow(tx) && check_outs_overflow(tx);
}
//---------------------------------------------------------------
bool check_inputs_overflow(const transaction& tx)
{
uint64_t money = 0;
for(const auto& in: tx.vin)
2014-03-03 23:07:58 +01:00
{
CHECKED_GET_SPECIFIC_VARIANT(in, txin_to_key, tokey_in, false);
2014-03-03 23:07:58 +01:00
if(money > tokey_in.amount + money)
return false;
money += tokey_in.amount;
}
return true;
}
//---------------------------------------------------------------
bool check_outs_overflow(const transaction& tx)
{
uint64_t money = 0;
for(const auto& o: tx.vout)
2014-03-03 23:07:58 +01:00
{
if(money > o.amount + money)
return false;
money += o.amount;
}
return true;
}
//---------------------------------------------------------------
uint64_t get_outs_money_amount(const transaction& tx)
{
uint64_t outputs_amount = 0;
for(const auto& o: tx.vout)
2014-03-03 23:07:58 +01:00
outputs_amount += o.amount;
return outputs_amount;
}
//---------------------------------------------------------------
std::string short_hash_str(const crypto::hash& h)
{
return oxenc::to_hex(tools::view_guts(h).substr(0, 4)) + "....";
2014-03-03 23:07:58 +01:00
}
//---------------------------------------------------------------
2017-02-19 03:42:10 +01:00
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_pub_keys, size_t output_index)
2014-03-03 23:07:58 +01:00
{
crypto::key_derivation derivation;
bool r = acc.get_device().generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
2014-03-03 23:07:58 +01:00
crypto::public_key pk;
r = acc.get_device().derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key");
2017-02-19 03:42:10 +01:00
if (pk == out_key.key)
return true;
// try additional tx pubkeys if available
if (!additional_tx_pub_keys.empty())
{
CHECK_AND_ASSERT_MES(output_index < additional_tx_pub_keys.size(), false, "wrong number of additional tx pubkeys");
r = acc.get_device().generate_key_derivation(additional_tx_pub_keys[output_index], acc.m_view_secret_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
r = acc.get_device().derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key");
2017-02-19 03:42:10 +01:00
return pk == out_key.key;
}
return false;
2014-03-03 23:07:58 +01:00
}
//---------------------------------------------------------------
2020-06-02 00:30:19 +02:00
std::optional<subaddress_receive_info> is_out_to_acc_precomp(const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::key_derivation& derivation, const std::vector<crypto::key_derivation>& additional_derivations, size_t output_index, hw::device &hwdev)
{
2017-02-19 03:42:10 +01:00
// try the shared tx pubkey
crypto::public_key subaddress_spendkey;
hwdev.derive_subaddress_public_key(out_key, derivation, output_index, subaddress_spendkey);
2017-02-19 03:42:10 +01:00
auto found = subaddresses.find(subaddress_spendkey);
if (found != subaddresses.end())
return subaddress_receive_info{ found->second, derivation };
// try additional tx pubkeys if available
if (!additional_derivations.empty())
{
2020-06-02 00:30:19 +02:00
CHECK_AND_ASSERT_MES(output_index < additional_derivations.size(), std::nullopt, "wrong number of additional derivations");
hwdev.derive_subaddress_public_key(out_key, additional_derivations[output_index], output_index, subaddress_spendkey);
2017-02-19 03:42:10 +01:00
found = subaddresses.find(subaddress_spendkey);
if (found != subaddresses.end())
return subaddress_receive_info{ found->second, additional_derivations[output_index] };
}
2020-06-02 00:30:19 +02:00
return std::nullopt;
}
//---------------------------------------------------------------
2014-03-03 23:07:58 +01:00
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered)
{
2014-05-03 18:19:43 +02:00
crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
if (!tx_pub_key)
2014-05-03 18:19:43 +02:00
return false;
2017-02-19 03:42:10 +01:00
std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
return lookup_acc_outs(acc, tx, tx_pub_key, additional_tx_pub_keys, outs, money_transfered);
2014-03-03 23:07:58 +01:00
}
//---------------------------------------------------------------
2017-02-19 03:42:10 +01:00
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_pub_keys, std::vector<size_t>& outs, uint64_t& money_transfered)
2014-03-03 23:07:58 +01:00
{
2017-02-19 03:42:10 +01:00
CHECK_AND_ASSERT_MES(additional_tx_pub_keys.empty() || additional_tx_pub_keys.size() == tx.vout.size(), false, "wrong number of additional pubkeys" );
2014-03-03 23:07:58 +01:00
money_transfered = 0;
size_t i = 0;
for(const tx_out& o: tx.vout)
2014-03-03 23:07:58 +01:00
{
CHECK_AND_ASSERT_MES(std::holds_alternative<txout_to_key>(o.target), false, "wrong type id in transaction out" );
if(is_out_to_acc(acc, var::get<txout_to_key>(o.target), tx_pub_key, additional_tx_pub_keys, i))
2014-03-03 23:07:58 +01:00
{
outs.push_back(i);
money_transfered += o.amount;
}
i++;
}
return true;
}
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
void get_blob_hash(const std::string_view blob, crypto::hash& res)
2014-03-03 23:07:58 +01:00
{
cn_fast_hash(blob.data(), blob.size(), res);
}
//---------------------------------------------------------------
std::string print_money(uint64_t amount, bool strip_zeros)
{
constexpr unsigned int decimal_point = oxen::DISPLAY_DECIMAL_POINT;
2014-03-03 23:07:58 +01:00
std::string s = std::to_string(amount);
if(s.size() < decimal_point+1)
2014-03-03 23:07:58 +01:00
{
s.insert(0, decimal_point+1 - s.size(), '0');
2014-03-03 23:07:58 +01:00
}
s.insert(s.size() - decimal_point, ".");
if (strip_zeros)
{
while (s.back() == '0')
s.pop_back();
if (s.back() == '.')
s.pop_back();
}
2014-03-03 23:07:58 +01:00
return s;
}
//---------------------------------------------------------------
std::string format_money(uint64_t amount, bool strip_zeros)
{
auto value = print_money(amount, strip_zeros);
value += ' ';
value += get_unit();
return value;
}
//---------------------------------------------------------------
std::string print_tx_verification_context(tx_verification_context const &tvc, transaction const *tx)
{
std::ostringstream os;
if (tvc.m_verbose_error.size())
os << tvc.m_verbose_error << "\n";
if (tvc.m_verifivation_failed) os << "Verification failed, connection should be dropped, "; //bad tx, should drop connection
if (tvc.m_verifivation_impossible) os << "Verification impossible, related to alt chain, "; //the transaction is related with an alternative blockchain
if (!tvc.m_should_be_relayed) os << "TX should NOT be relayed, ";
if (tvc.m_added_to_pool) os << "TX added to pool, ";
if (tvc.m_low_mixin) os << "Insufficient mixin, ";
if (tvc.m_double_spend) os << "Double spend TX, ";
if (tvc.m_invalid_input) os << "Invalid inputs, ";
if (tvc.m_invalid_output) os << "Invalid outputs, ";
2020-06-04 08:49:18 +02:00
if (tvc.m_too_few_outputs) os << "Need at least 2 outputs, ";
if (tvc.m_too_big) os << "TX too big, ";
if (tvc.m_overspend) os << "Overspend, ";
if (tvc.m_fee_too_low) os << "Fee too low, ";
if (tvc.m_invalid_version) os << "TX has invalid version, ";
if (tvc.m_invalid_type) os << "TX has invalid type, ";
if (tvc.m_key_image_locked_by_snode) os << "Key image is locked by service node, ";
if (tvc.m_key_image_blacklisted) os << "Key image is blacklisted on the service node network, ";
Infinite Staking Part 1 (#387) * Remove dead branches in hot-path check_tx_inputs Also renames #define for mixins to better match naming convention * Shuffle around some more code into common branches * Fix min/max tx version rules, since there 1 tx v2 on v9 fork * First draft infinite staking implementation * Actually generate the right key image and expire appropriately * Add framework to lock key images after expiry * Return locked key images for nodes, add request unlock option * Introduce transaction types for key image unlock * Update validation steps to accept tx types, key_image_unlock * Add mapping for lockable key images to amounts * Change inconsistent naming scheme of contributors * Create key image unlock transaction type and process it * Update tx params to allow v4 types and as a result construct_tx* * Fix some serialisation issues not sending all the information * Fix dupe tx extra tag causing incorrect deserialisation * Add warning comments * Fix key image unlocks parsing error * Simplify key image proof checks * Fix rebase errors * Correctly calculate the key image unlock times * Blacklist key image on deregistration * Serialise key image blacklist * Rollback blacklisted key images * Fix expiry logic error * Disallow requesting stake unlock if already unlocked client side * Add double spend checks for key image unlocks * Rename get_staking_requirement_lock_blocks To staking_initial_num_lock_blocks * Begin modifying output selection to not use locked outputs * Modify output selection to avoid locked/blacklisted key images * Cleanup and undoing some protocol breakages * Simplify expiration of nodes * Request unlock schedules entire node for expiration * Fix off by one in expiring nodes * Undo expiring code for pre v10 nodes * Fix RPC returning register as unlock height and not checking 0 * Rename key image unlock height const * Undo testnet hardfork debug changes * Remove is_type for get_type, fix missing var rename * Move serialisable data into public namespace * Serialise tx types properly * Fix typo in no service node known msg * Code review * Fix == to >= on serialising tx type * Code review 2 * Fix tests and key image unlock * Add additional test, fix assert * Remove debug code in wallet * Fix merge dev problem
2019-01-25 04:15:52 +01:00
if (tx)
os << "TX Version: {}, Type: {}"_format(tx->version, tx->type);
Infinite Staking Part 1 (#387) * Remove dead branches in hot-path check_tx_inputs Also renames #define for mixins to better match naming convention * Shuffle around some more code into common branches * Fix min/max tx version rules, since there 1 tx v2 on v9 fork * First draft infinite staking implementation * Actually generate the right key image and expire appropriately * Add framework to lock key images after expiry * Return locked key images for nodes, add request unlock option * Introduce transaction types for key image unlock * Update validation steps to accept tx types, key_image_unlock * Add mapping for lockable key images to amounts * Change inconsistent naming scheme of contributors * Create key image unlock transaction type and process it * Update tx params to allow v4 types and as a result construct_tx* * Fix some serialisation issues not sending all the information * Fix dupe tx extra tag causing incorrect deserialisation * Add warning comments * Fix key image unlocks parsing error * Simplify key image proof checks * Fix rebase errors * Correctly calculate the key image unlock times * Blacklist key image on deregistration * Serialise key image blacklist * Rollback blacklisted key images * Fix expiry logic error * Disallow requesting stake unlock if already unlocked client side * Add double spend checks for key image unlocks * Rename get_staking_requirement_lock_blocks To staking_initial_num_lock_blocks * Begin modifying output selection to not use locked outputs * Modify output selection to avoid locked/blacklisted key images * Cleanup and undoing some protocol breakages * Simplify expiration of nodes * Request unlock schedules entire node for expiration * Fix off by one in expiring nodes * Undo expiring code for pre v10 nodes * Fix RPC returning register as unlock height and not checking 0 * Rename key image unlock height const * Undo testnet hardfork debug changes * Remove is_type for get_type, fix missing var rename * Move serialisable data into public namespace * Serialise tx types properly * Fix typo in no service node known msg * Code review * Fix == to >= on serialising tx type * Code review 2 * Fix tests and key image unlock * Add additional test, fix assert * Remove debug code in wallet * Fix merge dev problem
2019-01-25 04:15:52 +01:00
std::string buf = os.str();
if (buf.size() >= 2 && buf[buf.size() - 2] == ',')
buf.resize(buf.size() - 2);
return buf;
}
//---------------------------------------------------------------
std::unordered_set<std::string> tx_verification_failure_codes(const tx_verification_context& tvc) {
std::unordered_set<std::string> reasons;
if (tvc.m_verifivation_failed) reasons.insert("failed");
if (tvc.m_verifivation_impossible) reasons.insert("altchain");
if (tvc.m_low_mixin) reasons.insert("mixin");
if (tvc.m_double_spend) reasons.insert("double_spend");
if (tvc.m_invalid_input) reasons.insert("invalid_input");
if (tvc.m_invalid_output) reasons.insert("invalid_output");
if (tvc.m_too_few_outputs) reasons.insert("too_few_outputs");
if (tvc.m_too_big) reasons.insert("too_big");
if (tvc.m_overspend) reasons.insert("overspend");
if (tvc.m_fee_too_low) reasons.insert("fee_too_low");
if (tvc.m_invalid_version) reasons.insert("invalid_version");
if (tvc.m_invalid_type) reasons.insert("invalid_type");
if (tvc.m_key_image_locked_by_snode) reasons.insert("snode_locked");
if (tvc.m_key_image_blacklisted) reasons.insert("blacklisted");
if (!tvc.m_should_be_relayed) reasons.insert("not_relayed");
return reasons;
}
//---------------------------------------------------------------
std::string print_vote_verification_context(vote_verification_context const &vvc, service_nodes::quorum_vote_t const *vote)
{
std::ostringstream os;
if (vvc.m_invalid_block_height) os << "Invalid block height: " << (vote ? std::to_string(vote->block_height) : "??") << ", ";
if (vvc.m_duplicate_voters) os << "Index in group was duplicated: " << (vote ? std::to_string(vote->index_in_group) : "??") << ", ";
if (vvc.m_validator_index_out_of_bounds) os << "Validator index out of bounds";
if (vvc.m_worker_index_out_of_bounds) os << "Worker index out of bounds: " << (vote ? std::to_string(vote->state_change.worker_index) : "??") << ", ";
if (vvc.m_signature_not_valid) os << "Signature not valid, ";
if (vvc.m_added_to_pool) os << "Added to pool, ";
if (vvc.m_not_enough_votes) os << "Not enough votes, ";
if (vvc.m_incorrect_voting_group)
{
os << "Incorrect voting group specified";
if (vote)
{
if (vote->group == service_nodes::quorum_group::validator)
os << ": validator";
else if (vote->group == service_nodes::quorum_group::worker)
os << ": worker";
else
os << ": " << static_cast<int>(vote->group);
}
os << ", ";
}
if (vvc.m_invalid_vote_type) os << "Vote type has invalid value: " << (vote ? std::to_string((uint8_t)vote->type) : "??") << ", ";
if (vvc.m_votes_not_sorted) os << "Votes are not stored in ascending order";
std::string buf = os.str();
if (buf.size() >= 2 && buf[buf.size() - 2] == ',')
buf.resize(buf.size() - 2);
Infinite Staking Part 1 (#387) * Remove dead branches in hot-path check_tx_inputs Also renames #define for mixins to better match naming convention * Shuffle around some more code into common branches * Fix min/max tx version rules, since there 1 tx v2 on v9 fork * First draft infinite staking implementation * Actually generate the right key image and expire appropriately * Add framework to lock key images after expiry * Return locked key images for nodes, add request unlock option * Introduce transaction types for key image unlock * Update validation steps to accept tx types, key_image_unlock * Add mapping for lockable key images to amounts * Change inconsistent naming scheme of contributors * Create key image unlock transaction type and process it * Update tx params to allow v4 types and as a result construct_tx* * Fix some serialisation issues not sending all the information * Fix dupe tx extra tag causing incorrect deserialisation * Add warning comments * Fix key image unlocks parsing error * Simplify key image proof checks * Fix rebase errors * Correctly calculate the key image unlock times * Blacklist key image on deregistration * Serialise key image blacklist * Rollback blacklisted key images * Fix expiry logic error * Disallow requesting stake unlock if already unlocked client side * Add double spend checks for key image unlocks * Rename get_staking_requirement_lock_blocks To staking_initial_num_lock_blocks * Begin modifying output selection to not use locked outputs * Modify output selection to avoid locked/blacklisted key images * Cleanup and undoing some protocol breakages * Simplify expiration of nodes * Request unlock schedules entire node for expiration * Fix off by one in expiring nodes * Undo expiring code for pre v10 nodes * Fix RPC returning register as unlock height and not checking 0 * Rename key image unlock height const * Undo testnet hardfork debug changes * Remove is_type for get_type, fix missing var rename * Move serialisable data into public namespace * Serialise tx types properly * Fix typo in no service node known msg * Code review * Fix == to >= on serialising tx type * Code review 2 * Fix tests and key image unlock * Add additional test, fix assert * Remove debug code in wallet * Fix merge dev problem
2019-01-25 04:15:52 +01:00
return buf;
}
//---------------------------------------------------------------
bool is_valid_address(const std::string address, cryptonote::network_type nettype, bool allow_subaddress, bool allow_integrated)
{
cryptonote::address_parse_info addr_info;
bool valid = false;
if(get_account_address_from_str(addr_info, nettype, address))
{
if (addr_info.is_subaddress)
valid = allow_subaddress;
else if (addr_info.has_payment_id)
valid = allow_integrated;
else
valid = true;
}
return valid;
}
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
crypto::hash get_blob_hash(const std::string_view blob)
{
2020-06-02 05:08:48 +02:00
crypto::hash h;
get_blob_hash(blob, h);
return h;
}
//---------------------------------------------------------------
2014-03-03 23:07:58 +01:00
crypto::hash get_transaction_hash(const transaction& t)
{
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
crypto::hash h{};
get_transaction_hash(t, h, NULL);
CHECK_AND_ASSERT_THROW_MES(get_transaction_hash(t, h, NULL), "Failed to calculate transaction hash");
2014-03-03 23:07:58 +01:00
return h;
}
//---------------------------------------------------------------
bool get_transaction_hash(const transaction& t, crypto::hash& res)
{
return get_transaction_hash(t, res, NULL);
}
//---------------------------------------------------------------
2022-05-20 23:29:48 +02:00
[[nodiscard]] bool calculate_transaction_prunable_hash(const transaction& t, const std::string *blob, crypto::hash& res)
{
Make tx type and version scoped enums This converts the transaction type and version to scoped enum, giving type safety and making the tx type assignment less error prone because there is no implicit conversion or comparison with raw integers that has to be worried about. This ends up converting any use of `cryptonote::transaction::type_xyz` to `cryptonote::transaction::txtype::xyz`. For version, names like `transaction::version_v4` become `cryptonote::txversion::v4_tx_types`. This also allows/includes various other simplifications related to or enabled by this change: - handle `is_deregister` dynamically in serialization code (setting `type::standard` or `type::deregister` rather than using a version-determined union) - `get_type()` is no longer needed with the above change: it is now much simpler to directly access `type` which will always have the correct value (even for v2 or v3 transaction types). And though there was an assertion on the enum value, `get_type()` was being used only sporadically: many places accessed `.type` directly. - the old unscoped enum didn't have a type but was assumed castable to/from `uint16_t`, which technically meant there was potential undefined behaviour when deserializing any type values >= 8. - tx type range checks weren't being done in all serialization paths; they are now. Because `get_type()` was not used everywhere (lots of places simply accessed `.type` directory) these might not have been caught. - `set_type()` is not needed; it was only being used in a single place (wallet2.cpp) and only for v4 txes, so the version protection code was never doing anything. - added a std::ostream << operator for the enum types so that they can be output with `<< tx_type <<` rather than needing to wrap it in `type_to_string(tx_type)` everywhere. For the versions, you get the annotated version string (e.g. 4_tx_types) rather than just the number 4.
2019-06-11 20:53:46 +02:00
if (t.version == txversion::v1)
return false;
const unsigned int unprunable_size = t.unprunable_size;
if (blob && unprunable_size)
{
CHECK_AND_ASSERT_MES(unprunable_size <= blob->size(), false, "Inconsistent transaction unprunable and blob sizes");
2020-06-02 05:08:48 +02:00
cryptonote::get_blob_hash(std::string_view{*blob}.substr(unprunable_size), res);
}
else
{
2020-06-02 05:08:48 +02:00
serialization::binary_string_archiver ba;
size_t mixin = 0;
if (t.vin.size() > 0 && std::holds_alternative<txin_to_key>(t.vin[0]))
mixin = var::get<txin_to_key>(t.vin[0]).key_offsets.size() - 1;
2020-06-02 05:08:48 +02:00
try {
const_cast<transaction&>(t).rct_signatures.p.serialize_rctsig_prunable(
ba, t.rct_signatures.type, t.vin.size(), t.vout.size(), mixin);
2020-06-02 05:08:48 +02:00
} catch (const std::exception& e) {
log::error(logcat, "Failed to serialize rct signatures (prunable): {}", e.what());
2020-06-02 05:08:48 +02:00
return false;
}
cryptonote::get_blob_hash(ba.str(), res);
}
return true;
}
//---------------------------------------------------------------
2022-05-20 23:29:48 +02:00
crypto::hash get_transaction_prunable_hash(const transaction& t, const std::string* blobdata)
{
crypto::hash res;
CHECK_AND_ASSERT_THROW_MES(calculate_transaction_prunable_hash(t, blobdata, res), "Failed to calculate tx prunable hash");
return res;
}
//---------------------------------------------------------------
crypto::hash get_pruned_transaction_hash(const transaction& t, const crypto::hash &pruned_data_hash)
{
// v1 transactions hash the entire blob
2020-06-02 05:08:48 +02:00
if (t.version < txversion::v2_ringct)
throw std::runtime_error("Hash for pruned v1 tx cannot be calculated");
// v2 transactions hash different parts together, than hash the set of those hashes
crypto::hash hashes[3];
// prefix
get_transaction_prefix_hash(t, hashes[0]);
transaction &tt = const_cast<transaction&>(t);
// base rct
{
2020-06-02 05:08:48 +02:00
serialization::binary_string_archiver ba;
const size_t inputs = t.vin.size();
const size_t outputs = t.vout.size();
2020-06-02 05:08:48 +02:00
tt.rct_signatures.serialize_rctsig_base(ba, inputs, outputs); // throws on error (good)
cryptonote::get_blob_hash(ba.str(), hashes[1]);
}
// prunable rct
if (t.rct_signatures.type == rct::RCTType::Null)
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
hashes[2].zero();
else
hashes[2] = pruned_data_hash;
// the tx hash is the hash of the 3 hashes
crypto::hash res = cn_fast_hash(hashes, sizeof(hashes));
return res;
}
//---------------------------------------------------------------
bool calculate_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size)
{
// v1 transactions hash the entire blob
Make tx type and version scoped enums This converts the transaction type and version to scoped enum, giving type safety and making the tx type assignment less error prone because there is no implicit conversion or comparison with raw integers that has to be worried about. This ends up converting any use of `cryptonote::transaction::type_xyz` to `cryptonote::transaction::txtype::xyz`. For version, names like `transaction::version_v4` become `cryptonote::txversion::v4_tx_types`. This also allows/includes various other simplifications related to or enabled by this change: - handle `is_deregister` dynamically in serialization code (setting `type::standard` or `type::deregister` rather than using a version-determined union) - `get_type()` is no longer needed with the above change: it is now much simpler to directly access `type` which will always have the correct value (even for v2 or v3 transaction types). And though there was an assertion on the enum value, `get_type()` was being used only sporadically: many places accessed `.type` directly. - the old unscoped enum didn't have a type but was assumed castable to/from `uint16_t`, which technically meant there was potential undefined behaviour when deserializing any type values >= 8. - tx type range checks weren't being done in all serialization paths; they are now. Because `get_type()` was not used everywhere (lots of places simply accessed `.type` directory) these might not have been caught. - `set_type()` is not needed; it was only being used in a single place (wallet2.cpp) and only for v4 txes, so the version protection code was never doing anything. - added a std::ostream << operator for the enum types so that they can be output with `<< tx_type <<` rather than needing to wrap it in `type_to_string(tx_type)` everywhere. For the versions, you get the annotated version string (e.g. 4_tx_types) rather than just the number 4.
2019-06-11 20:53:46 +02:00
if (t.version == txversion::v1)
{
size_t ignored_blob_size, &blob_size_ref = blob_size ? *blob_size : ignored_blob_size;
return get_object_hash(t, res, blob_size_ref);
}
// v2 transactions hash different parts together, than hash the set of those hashes
crypto::hash hashes[3];
// prefix
get_transaction_prefix_hash(t, hashes[0]);
2022-05-20 23:29:48 +02:00
const std::string blob = tx_to_blob(t);
CHECK_AND_ASSERT_MES(!blob.empty(), false, "Failed to convert tx to blob");
2021-01-04 01:09:45 +01:00
// TODO(oxen): Not sure if this is the right fix, we may just want to set
// unprunable size to the size of the prefix because technically that is
// what it is and then keep this code path.
if (t.is_transfer())
{
const unsigned int unprunable_size = t.unprunable_size;
const unsigned int prefix_size = t.prefix_size;
// base rct
CHECK_AND_ASSERT_MES(prefix_size <= unprunable_size && unprunable_size <= blob.size(), false,
"Inconsistent transaction prefix (" << prefix_size << "), unprunable (" << unprunable_size << ") and blob (" << blob.size() << ") sizes in: " << __func__);
2020-06-02 05:08:48 +02:00
cryptonote::get_blob_hash(std::string_view{blob}.substr(prefix_size, unprunable_size - prefix_size), hashes[1]);
}
else
{
transaction &tt = const_cast<transaction&>(t);
2020-06-02 05:08:48 +02:00
serialization::binary_string_archiver ba;
try {
tt.rct_signatures.serialize_rctsig_base(ba, t.vin.size(), t.vout.size());
} catch (const std::exception& e) {
log::error(logcat, "Failed to serialize rct signatures base: {}", e.what());
2020-06-02 05:08:48 +02:00
return false;
}
cryptonote::get_blob_hash(ba.str(), hashes[1]);
}
// prunable rct
if (t.rct_signatures.type == rct::RCTType::Null)
{
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
hashes[2].zero();
}
2020-06-02 05:08:48 +02:00
else if (!calculate_transaction_prunable_hash(t, &blob, hashes[2]))
{
log::error(logcat, "Failed to get tx prunable hash");
2020-06-02 05:08:48 +02:00
return false;
}
// the tx hash is the hash of the 3 hashes
res = cn_fast_hash(hashes, sizeof(hashes));
// we still need the size
if (blob_size)
{
if (!t.is_blob_size_valid())
{
t.blob_size = blob.size();
t.set_blob_size_valid(true);
}
*blob_size = t.blob_size;
}
return true;
2014-03-03 23:07:58 +01:00
}
//---------------------------------------------------------------
bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size)
{
if (t.is_hash_valid())
{
res = t.hash;
if (blob_size)
{
if (!t.is_blob_size_valid())
{
t.blob_size = get_object_blobsize(t);
t.set_blob_size_valid(true);
}
*blob_size = t.blob_size;
}
return true;
}
bool ret = calculate_transaction_hash(t, res, blob_size);
if (!ret)
return false;
t.hash = res;
t.set_hash_valid(true);
if (blob_size)
{
t.blob_size = *blob_size;
t.set_blob_size_valid(true);
}
return true;
}
//---------------------------------------------------------------
2014-03-03 23:07:58 +01:00
bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size)
{
return get_transaction_hash(t, res, &blob_size);
2014-03-03 23:07:58 +01:00
}
//---------------------------------------------------------------
2022-05-20 23:29:48 +02:00
std::string get_block_hashing_blob(const block& b)
2014-03-03 23:07:58 +01:00
{
2022-05-20 23:29:48 +02:00
std::string blob = t_serializable_object_to_blob(static_cast<block_header>(b));
2014-04-02 18:00:17 +02:00
crypto::hash tree_root_hash = get_tx_tree_hash(b);
2014-06-30 21:17:24 +02:00
blob.append(reinterpret_cast<const char*>(&tree_root_hash), sizeof(tree_root_hash));
2014-03-03 23:07:58 +01:00
blob.append(tools::get_varint_data(b.tx_hashes.size()+1));
return blob;
}
//---------------------------------------------------------------
bool calculate_block_hash(const block& b, crypto::hash& res)
2014-03-03 23:07:58 +01:00
{
bool hash_result = get_object_hash(get_block_hashing_blob(b), res);
return hash_result;
2014-03-03 23:07:58 +01:00
}
//---------------------------------------------------------------
bool get_block_hash(const block& b, crypto::hash& res)
{
if (b.is_hash_valid())
{
res = b.hash;
return true;
}
bool ret = calculate_block_hash(b, res);
if (!ret)
return false;
b.hash = res;
b.set_hash_valid(true);
return true;
}
//---------------------------------------------------------------
2014-03-03 23:07:58 +01:00
crypto::hash get_block_hash(const block& b)
{
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
crypto::hash p{};
2014-03-03 23:07:58 +01:00
get_block_hash(b, p);
return p;
}
//---------------------------------------------------------------
std::vector<uint64_t> relative_output_offsets_to_absolute(const std::vector<uint64_t>& off)
{
std::vector<uint64_t> res = off;
for(size_t i = 1; i < res.size(); i++)
res[i] += res[i-1];
return res;
}
//---------------------------------------------------------------
std::vector<uint64_t> absolute_output_offsets_to_relative(const std::vector<uint64_t>& off)
{
std::vector<uint64_t> res = off;
CHECK_AND_ASSERT_THROW_MES(not off.empty(), "absolute index to relative offset, no indices provided");
// vector must be sorted before calling this, else an index' offset would be negative
CHECK_AND_ASSERT_THROW_MES(std::is_sorted(res.begin(), res.end()), "absolute index to relative offset, indices not sorted");
2014-03-03 23:07:58 +01:00
for(size_t i = res.size()-1; i != 0; i--)
res[i] -= res[i-1];
return res;
}
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
[[nodiscard]] bool parse_and_validate_block_from_blob(const std::string_view b_blob, block& b, crypto::hash* block_hash)
2014-03-03 23:07:58 +01:00
{
2020-06-02 05:08:48 +02:00
serialization::binary_string_unarchiver ba{b_blob};
try {
serialization::serialize(ba, b);
} catch (const std::exception& e) {
log::error(logcat, "Failed to parse block from blob: {}", e.what());
2020-06-02 05:08:48 +02:00
return false;
}
b.invalidate_hashes();
b.miner_tx.invalidate_hashes();
if (block_hash)
{
calculate_block_hash(b, *block_hash);
b.hash = *block_hash;
b.set_hash_valid(true);
}
2014-03-03 23:07:58 +01:00
return true;
}
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
bool parse_and_validate_block_from_blob(const std::string_view b_blob, block& b)
{
2020-06-02 05:08:48 +02:00
return parse_and_validate_block_from_blob(b_blob, b, nullptr);
}
//---------------------------------------------------------------
2020-06-02 05:08:48 +02:00
bool parse_and_validate_block_from_blob(const std::string_view b_blob, block& b, crypto::hash& block_hash)
{
return parse_and_validate_block_from_blob(b_blob, b, &block_hash);
}
//---------------------------------------------------------------
2022-05-20 23:29:48 +02:00
std::string block_to_blob(const block& b)
2014-03-03 23:07:58 +01:00
{
return t_serializable_object_to_blob(b);
}
//---------------------------------------------------------------
2022-05-20 23:29:48 +02:00
bool block_to_blob(const block& b, std::string& b_blob)
2014-03-03 23:07:58 +01:00
{
return t_serializable_object_to_blob(b, b_blob);
}
//---------------------------------------------------------------
2022-05-20 23:29:48 +02:00
std::string tx_to_blob(const transaction& tx)
2014-03-03 23:07:58 +01:00
{
return t_serializable_object_to_blob(tx);
}
//---------------------------------------------------------------
2022-05-20 23:29:48 +02:00
bool tx_to_blob(const transaction& tx, std::string& b_blob)
2014-03-03 23:07:58 +01:00
{
return t_serializable_object_to_blob(tx, b_blob);
}
//---------------------------------------------------------------
void get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes, crypto::hash& h)
{
tree_hash(tx_hashes.data(), tx_hashes.size(), h);
}
//---------------------------------------------------------------
crypto::hash get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes)
{
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
crypto::hash h{};
2014-03-03 23:07:58 +01:00
get_tx_tree_hash(tx_hashes, h);
return h;
}
//---------------------------------------------------------------
crypto::hash get_tx_tree_hash(const block& b)
{
std::vector<crypto::hash> txs_ids;
txs_ids.reserve(1 + b.tx_hashes.size());
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
crypto::hash h{};
2014-03-03 23:07:58 +01:00
size_t bl_sz = 0;
CHECK_AND_ASSERT_THROW_MES(get_transaction_hash(b.miner_tx, h, bl_sz), "Failed to calculate transaction hash");
2014-03-03 23:07:58 +01:00
txs_ids.push_back(h);
for(auto& th: b.tx_hashes)
2014-03-03 23:07:58 +01:00
txs_ids.push_back(th);
return get_tx_tree_hash(txs_ids);
}
//---------------------------------------------------------------
crypto::secret_key encrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
{
crypto::hash hash;
crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash, crypto::cn_slow_hash_type::heavy_v1);
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
sc_add(key.data(), key.data(), hash.data());
return key;
}
//---------------------------------------------------------------
crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
{
crypto::hash hash;
crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash, crypto::cn_slow_hash_type::heavy_v1);
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
sc_sub(key.data(), key.data(), hash.data());
return key;
}
}