mirror of
https://github.com/oxen-io/oxen-core.git
synced 2023-12-14 02:22:56 +01:00
dfe566480b
- Replace all cryptonote_config macros with constexpr variables. Some become integer types, some become chrono types. - generally this involved removing a "CRYPTONOTE_" prefix since the values are now in the `cryptonote` namespace - some constants are grouped into sub-namespaces (e.g. cryptonote::p2p) - deprecated constants (i.e. for old HFs) are in the `cryptonote::old` namespace. - all the magic hash key domain separating strings are now in cryptonote::hashkey::WHATEVER. - Move some economy-related constants to oxen_economy.h instead - Replaced the BLOCKS_EXPECTED_IN_DAYS constexpr functions with more straightforward `BLOCKS_PER_DAY` value (i.e. old `BLOCKS_EXPECTED_IN_DAYS(10)` is now `BLOCKS_PER_DAY * 10`. - Replaced `network_version` unscoped enum with a scoped enum `cryptonote::hf`, replacing all the raw uint8_t values where it was currently accepted with the new `hf` type. - Made `network_type` a scoped enum so that it now has to be qualified (network_type::TESTNET) and can't be arbitrarily/unintentionally converted to/from an int. - HARDFORK_WHATEVER macros have become cryptonote::feature::WHATEVER constexpr hf values. - Add `revision` to rpc hard_fork_info response - Don't build trezor code at all (previously we were pointlessly building an empty dummy lib).
631 lines
20 KiB
C++
631 lines
20 KiB
C++
// Copyright (c) 2014-2019, The Monero Project
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other
|
|
// materials provided with the distribution.
|
|
//
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without specific
|
|
// prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
// 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
|
|
|
|
#pragma once
|
|
|
|
#include <vector>
|
|
#include <sstream>
|
|
#include <atomic>
|
|
#include "serialization/variant.h"
|
|
#include "serialization/vector.h"
|
|
#include "serialization/binary_archive.h"
|
|
#include "serialization/json_archive.h"
|
|
#include "serialization/crypto.h"
|
|
#include "epee/serialization/keyvalue_serialization.h" // eepe named serialization
|
|
#include "cryptonote_config.h"
|
|
#include "crypto/crypto.h"
|
|
#include "crypto/hash.h"
|
|
#include "ringct/rctTypes.h"
|
|
#include "device/device.hpp"
|
|
#include "txtypes.h"
|
|
|
|
namespace service_nodes
|
|
{
|
|
struct quorum_signature
|
|
{
|
|
uint16_t voter_index;
|
|
char padding[6] = {0};
|
|
crypto::signature signature;
|
|
|
|
quorum_signature() = default;
|
|
quorum_signature(uint16_t voter_index, crypto::signature const &signature) :
|
|
voter_index(voter_index),
|
|
signature(signature)
|
|
{}
|
|
|
|
BEGIN_SERIALIZE_OBJECT()
|
|
FIELD(voter_index)
|
|
FIELD(signature)
|
|
END_SERIALIZE()
|
|
};
|
|
};
|
|
|
|
namespace cryptonote
|
|
{
|
|
/* outputs */
|
|
struct txout_to_script
|
|
{
|
|
std::vector<crypto::public_key> keys;
|
|
std::vector<uint8_t> script;
|
|
|
|
BEGIN_SERIALIZE_OBJECT()
|
|
FIELD(keys)
|
|
FIELD(script)
|
|
END_SERIALIZE()
|
|
};
|
|
|
|
struct txout_to_scripthash
|
|
{
|
|
crypto::hash hash;
|
|
};
|
|
|
|
struct txout_to_key
|
|
{
|
|
txout_to_key() = default;
|
|
txout_to_key(const crypto::public_key &_key) : key(_key) { }
|
|
crypto::public_key key;
|
|
};
|
|
|
|
|
|
/* inputs */
|
|
|
|
struct txin_gen
|
|
{
|
|
size_t height;
|
|
|
|
BEGIN_SERIALIZE_OBJECT()
|
|
VARINT_FIELD(height)
|
|
END_SERIALIZE()
|
|
};
|
|
|
|
struct txin_to_script
|
|
{
|
|
crypto::hash prev;
|
|
size_t prevout;
|
|
std::vector<uint8_t> sigset;
|
|
|
|
BEGIN_SERIALIZE_OBJECT()
|
|
FIELD(prev)
|
|
VARINT_FIELD(prevout)
|
|
FIELD(sigset)
|
|
END_SERIALIZE()
|
|
};
|
|
|
|
struct txin_to_scripthash
|
|
{
|
|
crypto::hash prev;
|
|
size_t prevout;
|
|
txout_to_script script;
|
|
std::vector<uint8_t> sigset;
|
|
|
|
BEGIN_SERIALIZE_OBJECT()
|
|
FIELD(prev)
|
|
VARINT_FIELD(prevout)
|
|
FIELD(script)
|
|
FIELD(sigset)
|
|
END_SERIALIZE()
|
|
};
|
|
|
|
struct txin_to_key
|
|
{
|
|
uint64_t amount;
|
|
std::vector<uint64_t> key_offsets;
|
|
crypto::key_image k_image; // double spending protection
|
|
|
|
BEGIN_SERIALIZE_OBJECT()
|
|
VARINT_FIELD(amount)
|
|
FIELD(key_offsets)
|
|
FIELD(k_image)
|
|
END_SERIALIZE()
|
|
};
|
|
|
|
|
|
using txin_v = std::variant<txin_gen, txin_to_script, txin_to_scripthash, txin_to_key>;
|
|
|
|
using txout_target_v = std::variant<txout_to_script, txout_to_scripthash, txout_to_key>;
|
|
|
|
//typedef std::pair<uint64_t, txout> out_t;
|
|
struct tx_out
|
|
{
|
|
uint64_t amount;
|
|
txout_target_v target;
|
|
|
|
BEGIN_SERIALIZE_OBJECT()
|
|
VARINT_FIELD(amount)
|
|
FIELD(target)
|
|
END_SERIALIZE()
|
|
|
|
|
|
};
|
|
|
|
// Blink quorum statuses. Note that the underlying numeric values is used in the RPC. `none` is
|
|
// only used in places like the RPC where we return a value even if not a blink at all.
|
|
enum class blink_result { none = 0, rejected, accepted, timeout };
|
|
|
|
class transaction_prefix
|
|
{
|
|
|
|
public:
|
|
static char const* version_to_string(txversion v);
|
|
static char const* type_to_string(txtype type);
|
|
|
|
static constexpr txversion get_min_version_for_hf(hf hf_version);
|
|
static txversion get_max_version_for_hf(hf hf_version);
|
|
static constexpr txtype get_max_type_for_hf (hf hf_version);
|
|
|
|
// tx information
|
|
txversion version;
|
|
txtype type;
|
|
|
|
bool is_transfer() const { return type == txtype::standard || type == txtype::stake || type == txtype::oxen_name_system; }
|
|
|
|
// not used after version 2, but remains for compatibility
|
|
uint64_t unlock_time; //number of block (or time), used as a limitation like: spend this tx not early then block/time
|
|
std::vector<txin_v> vin;
|
|
std::vector<tx_out> vout;
|
|
std::vector<uint8_t> extra;
|
|
std::vector<uint64_t> output_unlock_times;
|
|
|
|
BEGIN_SERIALIZE()
|
|
ENUM_FIELD(version, version >= txversion::v1 && version < txversion::_count);
|
|
if (version >= txversion::v3_per_output_unlock_times)
|
|
{
|
|
FIELD(output_unlock_times)
|
|
if (version == txversion::v3_per_output_unlock_times) {
|
|
bool is_state_change = type == txtype::state_change;
|
|
FIELD(is_state_change)
|
|
type = is_state_change ? txtype::state_change : txtype::standard;
|
|
}
|
|
}
|
|
VARINT_FIELD(unlock_time)
|
|
FIELD(vin)
|
|
FIELD(vout)
|
|
if (version >= txversion::v3_per_output_unlock_times && vout.size() != output_unlock_times.size())
|
|
{
|
|
throw std::invalid_argument{"v3 tx without correct unlock times"};
|
|
}
|
|
FIELD(extra)
|
|
if (version >= txversion::v4_tx_types)
|
|
ENUM_FIELD_N("type", type, type < txtype::_count);
|
|
END_SERIALIZE()
|
|
|
|
transaction_prefix() { set_null(); }
|
|
void set_null();
|
|
|
|
// This function is inlined because device_ledger code needs to call it, but doesn't link
|
|
// against cryptonote_basic.
|
|
uint64_t get_unlock_time(size_t out_index) const {
|
|
if (version >= txversion::v3_per_output_unlock_times)
|
|
{
|
|
if (out_index >= output_unlock_times.size())
|
|
{
|
|
LOG_ERROR("Tried to get unlock time of a v3 transaction with missing output unlock time");
|
|
return unlock_time;
|
|
}
|
|
return output_unlock_times[out_index];
|
|
}
|
|
return unlock_time;
|
|
}
|
|
|
|
};
|
|
|
|
class transaction final : public transaction_prefix
|
|
{
|
|
private:
|
|
// hash cache
|
|
mutable std::atomic<bool> hash_valid;
|
|
mutable std::atomic<bool> blob_size_valid;
|
|
|
|
public:
|
|
std::vector<std::vector<crypto::signature>> signatures; //count signatures always the same as inputs count
|
|
rct::rctSig rct_signatures;
|
|
|
|
// hash cache
|
|
mutable crypto::hash hash;
|
|
mutable size_t blob_size;
|
|
|
|
bool pruned;
|
|
|
|
std::atomic<unsigned int> unprunable_size;
|
|
std::atomic<unsigned int> prefix_size;
|
|
|
|
transaction() { set_null(); }
|
|
transaction(const transaction &t);
|
|
transaction& operator=(const transaction& t);
|
|
void set_null();
|
|
void invalidate_hashes();
|
|
bool is_hash_valid() const { return hash_valid.load(std::memory_order_acquire); }
|
|
void set_hash_valid(bool v) const { hash_valid.store(v,std::memory_order_release); }
|
|
bool is_blob_size_valid() const { return blob_size_valid.load(std::memory_order_acquire); }
|
|
void set_blob_size_valid(bool v) const { blob_size_valid.store(v,std::memory_order_release); }
|
|
void set_hash(const crypto::hash &h) { hash = h; set_hash_valid(true); }
|
|
void set_blob_size(size_t sz) { blob_size = sz; set_blob_size_valid(true); }
|
|
|
|
BEGIN_SERIALIZE_OBJECT()
|
|
constexpr bool Binary = serialization::is_binary<Archive>;
|
|
|
|
if (Archive::is_deserializer)
|
|
{
|
|
set_hash_valid(false);
|
|
set_blob_size_valid(false);
|
|
}
|
|
|
|
const unsigned int start_pos = Binary ? ar.streampos() : 0;
|
|
|
|
serialization::value(ar, static_cast<transaction_prefix&>(*this));
|
|
|
|
if (Binary)
|
|
prefix_size = ar.streampos() - start_pos;
|
|
|
|
if (version == txversion::v1)
|
|
{
|
|
if (Binary)
|
|
unprunable_size = ar.streampos() - start_pos;
|
|
|
|
ar.tag("signatures");
|
|
auto arr = ar.begin_array();
|
|
if (Archive::is_deserializer)
|
|
signatures.resize(vin.size());
|
|
bool signatures_expected = !signatures.empty();
|
|
if (signatures_expected && vin.size() != signatures.size())
|
|
throw std::invalid_argument{"Incorrect number of signatures"};
|
|
|
|
const size_t vin_sigs = pruned ? 0 : vin.size();
|
|
for (size_t i = 0; i < vin_sigs; ++i)
|
|
{
|
|
size_t signature_size = get_signature_size(vin[i]);
|
|
if (!signatures_expected)
|
|
{
|
|
if (signature_size > 0)
|
|
throw std::invalid_argument{"Invalid unexpected signature"};
|
|
continue;
|
|
}
|
|
|
|
if (Archive::is_deserializer)
|
|
signatures[i].resize(signature_size);
|
|
else if (signature_size != signatures[i].size())
|
|
throw std::invalid_argument{"Invalid signature size (expected " + std::to_string(signature_size) + ", have " + std::to_string(signatures[i].size()) + ")"};
|
|
|
|
value(arr.element(), signatures[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!vin.empty())
|
|
{
|
|
{
|
|
ar.tag("rct_signatures");
|
|
auto obj = ar.begin_object();
|
|
rct_signatures.serialize_rctsig_base(ar, vin.size(), vout.size());
|
|
}
|
|
|
|
if (Binary)
|
|
unprunable_size = ar.streampos() - start_pos;
|
|
|
|
if (!pruned && rct_signatures.type != rct::RCTType::Null)
|
|
{
|
|
ar.tag("rctsig_prunable");
|
|
auto obj = ar.begin_object();
|
|
rct_signatures.p.serialize_rctsig_prunable(ar, rct_signatures.type, vin.size(), vout.size(),
|
|
vin.size() > 0 && std::holds_alternative<txin_to_key>(vin[0]) ? var::get<txin_to_key>(vin[0]).key_offsets.size() - 1 : 0);
|
|
}
|
|
}
|
|
}
|
|
if (Archive::is_deserializer)
|
|
pruned = false;
|
|
END_SERIALIZE()
|
|
|
|
template<class Archive>
|
|
void serialize_base(Archive& ar)
|
|
{
|
|
serialization::value(ar, static_cast<transaction_prefix&>(*this));
|
|
|
|
if (version != txversion::v1)
|
|
{
|
|
if (!vin.empty())
|
|
{
|
|
ar.tag("rct_signatures");
|
|
auto obj = ar.begin_object();
|
|
rct_signatures.serialize_rctsig_base(ar, vin.size(), vout.size());
|
|
}
|
|
}
|
|
if (Archive::is_deserializer)
|
|
pruned = true;
|
|
}
|
|
|
|
private:
|
|
static size_t get_signature_size(const txin_v& tx_in);
|
|
};
|
|
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/************************************************************************/
|
|
struct pulse_random_value
|
|
{
|
|
unsigned char data[16];
|
|
bool operator==(pulse_random_value const &other) const { return std::memcmp(data, other.data, sizeof(data)) == 0; }
|
|
|
|
static constexpr bool binary_serializable = true;
|
|
};
|
|
|
|
struct pulse_header
|
|
{
|
|
pulse_random_value random_value;
|
|
uint8_t round;
|
|
uint16_t validator_bitset;
|
|
};
|
|
|
|
template <typename Archive>
|
|
void serialize_value(Archive& ar, pulse_header& p)
|
|
{
|
|
auto obj = ar.begin_object();
|
|
serialization::field(ar, "random_value", p.random_value);
|
|
serialization::field(ar, "round", p.round);
|
|
serialization::field(ar, "validator_bitset", p.validator_bitset);
|
|
}
|
|
|
|
struct block_header
|
|
{
|
|
hf major_version = hf::hf7;
|
|
uint8_t minor_version = 0;
|
|
uint64_t timestamp;
|
|
crypto::hash prev_id;
|
|
uint32_t nonce;
|
|
pulse_header pulse = {};
|
|
};
|
|
|
|
struct block: public block_header
|
|
{
|
|
private:
|
|
// hash cache
|
|
mutable std::atomic<bool> hash_valid{false};
|
|
void copy_hash(const block &b) { bool v = b.is_hash_valid(); hash = b.hash; set_hash_valid(v); }
|
|
|
|
public:
|
|
block() = default;
|
|
block(const block& b);
|
|
block(block&& b);
|
|
block& operator=(const block& b);
|
|
block& operator=(block&& b);
|
|
void invalidate_hashes() { set_hash_valid(false); }
|
|
bool is_hash_valid() const;
|
|
void set_hash_valid(bool v) const;
|
|
|
|
transaction miner_tx;
|
|
size_t height;
|
|
crypto::public_key service_node_winner_key;
|
|
uint64_t reward = 0;
|
|
std::vector<crypto::hash> tx_hashes;
|
|
|
|
// hash cache
|
|
mutable crypto::hash hash;
|
|
std::vector<service_nodes::quorum_signature> signatures;
|
|
};
|
|
|
|
template <class Archive>
|
|
void serialize_value(Archive& ar, block_header& b) {
|
|
using namespace serialization;
|
|
field(ar, "major_version", b.major_version);
|
|
field_varint(ar, "minor_version", b.minor_version);
|
|
field_varint(ar, "timestamp", b.timestamp);
|
|
field(ar, "prev_id", b.prev_id);
|
|
field(ar, "nonce", b.nonce);
|
|
if (b.major_version >= hf::hf16_pulse)
|
|
field(ar, "pulse", b.pulse);
|
|
}
|
|
|
|
template <class Archive>
|
|
void serialize_value(Archive& ar, block& b) {
|
|
auto _obj = ar.begin_object();
|
|
if constexpr (Archive::is_deserializer)
|
|
b.set_hash_valid(false);
|
|
|
|
serialization::value(ar, static_cast<block_header&>(b));
|
|
field(ar, "miner_tx", b.miner_tx);
|
|
field(ar, "tx_hashes", b.tx_hashes);
|
|
if (b.tx_hashes.size() > MAX_TX_PER_BLOCK)
|
|
throw std::invalid_argument{"too many txs in block"};
|
|
if (b.major_version >= hf::hf16_pulse)
|
|
field(ar, "signatures", b.signatures);
|
|
if (b.major_version >= hf::hf19)
|
|
{
|
|
field_varint(ar, "height", b.height);
|
|
field(ar, "service_node_winner_key", b.service_node_winner_key);
|
|
field(ar, "reward", b.reward);
|
|
}
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/************************************************************************/
|
|
struct account_public_address
|
|
{
|
|
crypto::public_key m_spend_public_key;
|
|
crypto::public_key m_view_public_key;
|
|
|
|
BEGIN_SERIALIZE_OBJECT()
|
|
FIELD(m_spend_public_key)
|
|
FIELD(m_view_public_key)
|
|
END_SERIALIZE()
|
|
|
|
BEGIN_KV_SERIALIZE_MAP()
|
|
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_public_key)
|
|
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_public_key)
|
|
END_KV_SERIALIZE_MAP()
|
|
|
|
bool operator==(const account_public_address& rhs) const
|
|
{
|
|
return m_spend_public_key == rhs.m_spend_public_key &&
|
|
m_view_public_key == rhs.m_view_public_key;
|
|
}
|
|
|
|
bool operator!=(const account_public_address& rhs) const
|
|
{
|
|
return !(*this == rhs);
|
|
}
|
|
|
|
uint64_t modulus(uint64_t interval) const;
|
|
uint64_t next_payout_height(uint64_t current_height, uint64_t interval) const;
|
|
|
|
};
|
|
inline constexpr account_public_address null_address{};
|
|
|
|
struct keypair
|
|
{
|
|
crypto::public_key pub;
|
|
crypto::secret_key sec;
|
|
|
|
keypair() = default;
|
|
|
|
// Constructs from a copied public/secret key
|
|
keypair(const crypto::public_key& pub, const crypto::secret_key& sec) : pub{pub}, sec{sec} {}
|
|
// Default copy and move
|
|
keypair(const keypair&) = default;
|
|
keypair(keypair&&) = default;
|
|
keypair& operator=(const keypair&) = default;
|
|
keypair& operator=(keypair&&) = default;
|
|
|
|
// Constructs by generating a keypair via the given hardware device:
|
|
explicit keypair(hw::device& hwdev) { hwdev.generate_keys(pub, sec); }
|
|
};
|
|
|
|
using byte_and_output_fees = std::pair<uint64_t, uint64_t>;
|
|
|
|
//---------------------------------------------------------------
|
|
constexpr txversion transaction_prefix::get_min_version_for_hf(hf hf_version)
|
|
{
|
|
if (hf_version >= hf::hf7 && hf_version <= hf::hf10_bulletproofs)
|
|
return txversion::v2_ringct;
|
|
return txversion::v4_tx_types;
|
|
}
|
|
|
|
// Used in the test suite to disable the older max version values below so that some test suite
|
|
// tests can still use particular hard forks without needing to actually generate pre-v4 txes.
|
|
namespace hack { inline bool test_suite_permissive_txes = false; }
|
|
|
|
inline txversion transaction_prefix::get_max_version_for_hf(hf hf_version)
|
|
{
|
|
if (!hack::test_suite_permissive_txes) {
|
|
if (hf_version >= hf::hf7 && hf_version <= hf::hf8)
|
|
return txversion::v2_ringct;
|
|
|
|
if (hf_version >= hf::hf9_service_nodes && hf_version <= hf::hf10_bulletproofs)
|
|
return txversion::v3_per_output_unlock_times;
|
|
}
|
|
|
|
return txversion::v4_tx_types;
|
|
}
|
|
|
|
constexpr txtype transaction_prefix::get_max_type_for_hf(hf hf_version)
|
|
{
|
|
txtype result = txtype::standard;
|
|
if (hf_version >= hf::hf15_ons) result = txtype::oxen_name_system;
|
|
else if (hf_version >= hf::hf14_blink) result = txtype::stake;
|
|
else if (hf_version >= hf::hf11_infinite_staking) result = txtype::key_image_unlock;
|
|
else if (hf_version >= hf::hf9_service_nodes) result = txtype::state_change;
|
|
|
|
return result;
|
|
}
|
|
|
|
inline const char* transaction_prefix::version_to_string(txversion v)
|
|
{
|
|
switch(v)
|
|
{
|
|
case txversion::v1: return "1";
|
|
case txversion::v2_ringct: return "2_ringct";
|
|
case txversion::v3_per_output_unlock_times: return "3_per_output_unlock_times";
|
|
case txversion::v4_tx_types: return "4_tx_types";
|
|
default: assert(false); return "xx_unhandled_version";
|
|
}
|
|
}
|
|
|
|
inline const char* transaction_prefix::type_to_string(txtype type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case txtype::standard: return "standard";
|
|
case txtype::state_change: return "state_change";
|
|
case txtype::key_image_unlock: return "key_image_unlock";
|
|
case txtype::stake: return "stake";
|
|
case txtype::oxen_name_system: return "oxen_name_system";
|
|
default: assert(false); return "xx_unhandled_type";
|
|
}
|
|
}
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, txtype t) {
|
|
return os << transaction::type_to_string(t);
|
|
}
|
|
inline std::ostream& operator<<(std::ostream& os, txversion v) {
|
|
return os << transaction::version_to_string(v);
|
|
}
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, hf v) = delete;/*{
|
|
return os << "HF" << static_cast<int>(v);
|
|
}*/
|
|
|
|
// Serialization for the `hf` type; this is simply writing/reading the underlying uint8_t value
|
|
template <class Archive>
|
|
void serialize_value(Archive& ar, hf& x) {
|
|
auto val = static_cast<std::underlying_type_t<hf>>(x);
|
|
serialization::value(ar, val);
|
|
if constexpr (Archive::is_deserializer)
|
|
x = static_cast<hf>(val);
|
|
}
|
|
}
|
|
|
|
namespace std {
|
|
template <>
|
|
struct hash<cryptonote::account_public_address>
|
|
{
|
|
std::size_t operator()(const cryptonote::account_public_address& addr) const
|
|
{
|
|
// https://stackoverflow.com/a/17017281
|
|
size_t res = 17;
|
|
res = res * 31 + hash<crypto::public_key>()(addr.m_spend_public_key);
|
|
res = res * 31 + hash<crypto::public_key>()(addr.m_view_public_key);
|
|
return res;
|
|
}
|
|
};
|
|
}
|
|
|
|
BLOB_SERIALIZER(cryptonote::txout_to_key);
|
|
BLOB_SERIALIZER(cryptonote::txout_to_scripthash);
|
|
|
|
VARIANT_TAG(cryptonote::txin_gen, "gen", 0xff);
|
|
VARIANT_TAG(cryptonote::txin_to_script, "script", 0x0);
|
|
VARIANT_TAG(cryptonote::txin_to_scripthash, "scripthash", 0x1);
|
|
VARIANT_TAG(cryptonote::txin_to_key, "key", 0x2);
|
|
VARIANT_TAG(cryptonote::txout_to_script, "script", 0x0);
|
|
VARIANT_TAG(cryptonote::txout_to_scripthash, "scripthash", 0x1);
|
|
VARIANT_TAG(cryptonote::txout_to_key, "key", 0x2);
|
|
VARIANT_TAG(cryptonote::transaction, "tx", 0xcc);
|
|
VARIANT_TAG(cryptonote::block, "block", 0xbb);
|