// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Boolberry developers
// Copyright (c) 2017-2025 Lethean (https://lt.hn)
//
// Licensed under the European Union Public Licence (EUPL) version 1.2.
// You may obtain a copy of the licence at:
//
//     https://joinup.ec.europa.eu/software/page/eupl/licence-eupl
//
// The EUPL is a copyleft licence that is compatible with the MIT/X11
// licence used by the original projects; the MIT terms are therefore
// considered “grandfathered” under the EUPL for this code.
//
// SPDX‑License‑Identifier: EUPL-1.2
//

#pragma once

#include <type_traits>
#include <boost/variant.hpp>
#include <boost/functional/hash/hash.hpp>
#include <boost/mpl/back_inserter.hpp>
#include <boost/mpl/copy.hpp>
#include <boost/mpl/unique.hpp>
#include <boost/mpl/list.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/vector/vector30.hpp>
#include <boost/type_traits/is_same.hpp>

#include <vector>
#include <cstring>  // memcmp
#include <sstream>
#include <chrono>

#include "include_base_utils.h"

#include "common/boost_version_check.h"
#include "serialization/binary_archive.h"
#include "common/crypto_serialization.h"
#include "serialization/stl_containers.h"
#include "serialization/serialization.h"
#include "serialization/variant.h"
#include "serialization/boost_types.h"
#include "serialization/json_archive.h"
#include "serialization/debug_archive.h"
#include "serialization/keyvalue_serialization.h" // epee key-value serialization
#include "string_tools.h"
#include "config/currency_config.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "crypto/range_proofs.h"
#include "crypto/zarcanum.h"
#include "crypto/eth_signature.h"
#include "misc_language.h"
#include "block_flags.h"
#include "etc_custom_serialization.h"
#include "difficulty.h"

namespace currency
{

  const static crypto::hash null_hash = AUTO_VAL_INIT(null_hash);
  const static crypto::public_key null_pkey = AUTO_VAL_INIT(null_pkey);
  const static crypto::key_image null_ki = AUTO_VAL_INIT(null_ki);
  const static crypto::secret_key null_skey = AUTO_VAL_INIT(null_skey);
  const static crypto::signature null_sig = AUTO_VAL_INIT(null_sig);
  const static crypto::key_derivation null_derivation = AUTO_VAL_INIT(null_derivation);
  const static crypto::eth_public_key null_eth_public_key = AUTO_VAL_INIT(null_eth_public_key);
  const static crypto::eth_signature null_eth_signature = AUTO_VAL_INIT(null_eth_signature);
  

  const static crypto::hash gdefault_genesis = epee::string_tools::hex_to_pod<crypto::hash>("CC608F59F8080E2FBFE3C8C80EB6E6A953D47CF2D6AEBD345BADA3A1CAB99852");

  // Using C++17 extended aggregate initialization (P0017R1). C++17, finally! -- sowle
  const static crypto::public_key native_coin_asset_id       = {{'\xd6', '\x32', '\x9b', '\x5b', '\x1f', '\x7c', '\x08', '\x05', '\xb5', '\xc3', '\x45', '\xf4', '\x95', '\x75', '\x54', '\x00', '\x2a', '\x2f', '\x55', '\x78', '\x45', '\xf6', '\x4d', '\x76', '\x45', '\xda', '\xe0', '\xe0', '\x51', '\xa6', '\x49', '\x8a'}}; // == crypto::c_point_H, checked in crypto_constants
  const static crypto::public_key native_coin_asset_id_1div8 = {{'\x74', '\xc3', '\x2d', '\x3e', '\xaa', '\xfa', '\xfc', '\x62', '\x3b', '\xf4', '\x83', '\xe8', '\x58', '\xd4', '\x2e', '\x8b', '\xf4', '\xec', '\x7d', '\xf0', '\x64', '\xad', '\xa2', '\xe3', '\x49', '\x34', '\x46', '\x9c', '\xff', '\x6b', '\x62', '\x68'}}; // == 1/8 * crypto::c_point_H, checked in crypto_constants
  const static crypto::point_t    native_coin_asset_id_pt      {{ 20574939, 16670001, -29137604, 14614582, 24883426, 3503293, 2667523, 420631, 2267646, -4769165, -11764015, -12206428, -14187565, -2328122, -16242653, -788308, -12595746, -8251557, -10110987, 853396, -4982135, 6035602, -21214320, 16156349, 977218, 2807645, 31002271, 5694305, -16054128, 5644146, -15047429, -568775, -22568195, -8089957, -27721961, -10101877, -29459620, -13359100, -31515170, -6994674 }}; // c_point_H

  const static wide_difficulty_type global_difficulty_pow_starter = DIFFICULTY_POW_STARTER;
  const static wide_difficulty_type global_difficulty_pos_starter = DIFFICULTY_POS_STARTER;
  const static uint64_t             global_difficulty_pos_target  = DIFFICULTY_POS_TARGET;
  const static uint64_t             global_difficulty_pow_target  = DIFFICULTY_POW_TARGET;

  typedef std::string payment_id_t;


  /************************************************************************/
  /*                                                                      */
  /************************************************************************/

  struct asset_descriptor_operation_v1;

//since structure used in blockchain as a key accessor, then be sure that there is no padding inside
#pragma pack(push, 1)
  struct account_public_address_old
  {
    crypto::public_key spend_public_key;
    crypto::public_key view_public_key;

    BEGIN_SERIALIZE_OBJECT()
      FIELD(spend_public_key)
      FIELD(view_public_key)
    END_SERIALIZE()

    BEGIN_KV_SERIALIZE_MAP()
      KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(spend_public_key, "m_spend_public_key")
      KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(view_public_key, "m_view_public_key")
    END_KV_SERIALIZE_MAP()
  };
#pragma pack(pop)


#define ACCOUNT_PUBLIC_ADDRESS_SERIZALIZATION_VER 1

#define ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE 0x01 // auditable address

//since structure used in blockchain as a key accessor, then be sure that there is no padding inside
#pragma pack(push, 1)
  struct account_public_address
  {
    crypto::public_key spend_public_key;
    crypto::public_key view_public_key;
    uint8_t flags;

    DEFINE_SERIALIZATION_VERSION(ACCOUNT_PUBLIC_ADDRESS_SERIZALIZATION_VER)
    BEGIN_SERIALIZE_OBJECT()
      FIELD(spend_public_key)
      FIELD(view_public_key)
      FIELD(flags)
    END_SERIALIZE()

    BEGIN_KV_SERIALIZE_MAP()
      KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(spend_public_key, "m_spend_public_key")
      KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(view_public_key, "m_view_public_key")
      KV_SERIALIZE(flags)
    END_KV_SERIALIZE_MAP()

    bool is_auditable() const
    {
      return (flags & ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE) != 0;
    }

    static account_public_address from_old(const account_public_address_old& rhs)
    {
      account_public_address result = AUTO_VAL_INIT(result);
      result.spend_public_key = rhs.spend_public_key;
      result.view_public_key = rhs.view_public_key;
      return result;
    }

    account_public_address_old to_old() const
    {
      account_public_address_old result = AUTO_VAL_INIT(result);
      result.spend_public_key = spend_public_key;
      result.view_public_key = view_public_key;
      return result;
    }
  };
#pragma pack(pop)


  const static account_public_address null_pub_addr = AUTO_VAL_INIT(null_pub_addr);

  typedef std::vector<crypto::signature> ring_signature;

  /************************************************************************/
  /* extra structures                                                     */
  /************************************************************************/
  struct extra_attachment_info
  {
    uint64_t sz;
    crypto::hash hsh;
    uint64_t cnt;

    BEGIN_SERIALIZE()
      VARINT_FIELD(sz)
      FIELD(hsh)
      VARINT_FIELD(cnt)
    END_SERIALIZE()
  };

  /* outputs */

// 'mix_attr' possible values
#define CURRENCY_TO_KEY_OUT_RELAXED                   0 // the output may be mixed with any fake outputs
#define CURRENCY_TO_KEY_OUT_FORCED_NO_MIX             1 // the output can't be mixed, only direct spend allowed
#define CURRENCY_TO_KEY_OUT_FORCED_MIX_LOWER_BOUND    2 // this and greather values means minimum number of total outputs (fakes + 1) must be mixed together for using that one

  #pragma pack(push, 1)
  struct txout_to_key
  {
    txout_to_key() : key(null_pkey), mix_attr(0) { }
    txout_to_key(const crypto::public_key &_key) : key(_key), mix_attr(0) { }

    crypto::public_key key;
    uint8_t mix_attr;
  };
  #pragma pack(pop)

  /* inputs */

  struct txin_gen
  {
    size_t height;

    BEGIN_SERIALIZE_OBJECT()
      VARINT_FIELD(height)
    END_SERIALIZE()
  };

  // ref_by_id is used by txin_to_key to reference an output by source transaction hash and output's internal index,
  // rather than amount and global output index (by default). Useful when output global index is unknown or may change.
  struct ref_by_id
  {
    crypto::hash tx_id; // source transaction hash
    uint32_t n;         // output index in source transaction

    BEGIN_SERIALIZE_OBJECT()
      FIELD(tx_id)
      VARINT_FIELD(n)
    END_SERIALIZE()
  };

  typedef boost::variant<uint64_t, ref_by_id> txout_ref_v;
  

  struct signed_parts
  {
    BEGIN_SERIALIZE_OBJECT()
      VARINT_FIELD(n_outs)
      VARINT_FIELD(n_extras)
    END_SERIALIZE()

    uint32_t n_outs;
    uint32_t n_extras;
  };


  typedef boost::variant<signed_parts, extra_attachment_info> txin_etc_details_v;


  struct referring_input
  {
    std::vector<txout_ref_v> key_offsets;
  };

  struct txin_to_key : public referring_input
  {
    uint64_t amount;
    crypto::key_image k_image;                    // double spending protection
    std::vector<txin_etc_details_v> etc_details;  // see also TX_FLAG_SIGNATURE_MODE_SEPARATE

    BEGIN_SERIALIZE_OBJECT()
      VARINT_FIELD(amount)
      FIELD(key_offsets) // from referring_input
      FIELD(k_image)
      FIELD(etc_details)
    END_SERIALIZE()
  };

  struct txin_htlc: public txin_to_key
  {
    std::string hltc_origin;
    BEGIN_SERIALIZE_OBJECT()
      FIELD(hltc_origin)
      FIELDS(*static_cast<txin_to_key*>(this))
    END_SERIALIZE()
  };

  struct txin_multisig
  {
    uint64_t amount;
    crypto::hash multisig_out_id;
    uint32_t sigs_count; // actual number of signatures that are used to sign this input; needed to calculate tx blob size; must be equal to minimum_sigs of corresponding ms output
    std::vector<txin_etc_details_v> etc_details; 

    BEGIN_SERIALIZE_OBJECT()
      VARINT_FIELD(amount)
      FIELD(multisig_out_id)
      VARINT_FIELD(sigs_count)
      FIELD(etc_details)
    END_SERIALIZE()
  };


  struct txout_multisig
  {
    uint32_t minimum_sigs;
    std::vector<crypto::public_key> keys;
    
    BEGIN_SERIALIZE_OBJECT()
      VARINT_FIELD(minimum_sigs)
      FIELD(keys)
    END_SERIALIZE()
  };

#define CURRENCY_TXOUT_HTLC_FLAGS_HASH_TYPE_MASK   0x01 // 0 - SHA256, 1 - RIPEMD160

  struct txout_htlc
  {
    crypto::hash htlc_hash;
    uint8_t flags;      //select type of the hash, may be some extra info in future
    uint64_t expiration; 
    crypto::public_key pkey_redeem; //works before expiration
    crypto::public_key pkey_refund; //works after expiration

    BEGIN_SERIALIZE_OBJECT()
      FIELD(htlc_hash)
      FIELD(flags)
      VARINT_FIELD(expiration)
      FIELD(pkey_redeem)
      FIELD(pkey_refund)
    END_SERIALIZE()
  };

  typedef boost::variant<txout_to_key, txout_multisig, txout_htlc> txout_target_v;

  //typedef std::pair<uint64_t, txout> out_t;
  struct tx_out_bare
  {
    uint64_t amount;
    txout_target_v target;

    BEGIN_SERIALIZE_OBJECT()
      VARINT_FIELD(amount)
      FIELD(target)
    END_SERIALIZE()
  };


  /////////////////////////////////////////////////////////////////////////////
  // Zarcanum structures
  //

  struct txin_zc_input : public referring_input
  {
    txin_zc_input() {}
    // Boost's Assignable concept
    txin_zc_input(const txin_zc_input&)           = default;
    txin_zc_input& operator=(const txin_zc_input&)= default;

    crypto::key_image               k_image;
    std::vector<txin_etc_details_v> etc_details;


    BEGIN_SERIALIZE_OBJECT()
      FIELD(key_offsets) // referring_input
      FIELD(k_image)
      FIELD(etc_details)
    END_SERIALIZE()

    BEGIN_BOOST_SERIALIZATION()
      BOOST_SERIALIZE(key_offsets) // referring_input
      BOOST_SERIALIZE(k_image)
      BOOST_SERIALIZE(etc_details)
    END_BOOST_SERIALIZATION()
  };

  struct tx_out_zarcanum
  {
    tx_out_zarcanum() {}
    
    // Boost's Assignable concept
    tx_out_zarcanum(const tx_out_zarcanum&)            = default;
    tx_out_zarcanum& operator=(const tx_out_zarcanum&) = default;

    crypto::public_key  stealth_address;
    crypto::public_key  concealing_point;  // group element Q, see also Zarcanum paper, premultiplied by 1/8
    crypto::public_key  amount_commitment; // premultiplied by 1/8
    crypto::public_key  blinded_asset_id;  // group element T, premultiplied by 1/8
    uint64_t            encrypted_amount = 0;
    uint8_t             mix_attr = 0;

    BEGIN_SERIALIZE_OBJECT()
      FIELD(stealth_address)
      FIELD(concealing_point)
      FIELD(amount_commitment)
      FIELD(blinded_asset_id)
      FIELD(encrypted_amount)
      FIELD(mix_attr)
    END_SERIALIZE()

    BEGIN_BOOST_SERIALIZATION()
      BOOST_SERIALIZE(stealth_address)
      BOOST_SERIALIZE(concealing_point)
      BOOST_SERIALIZE(amount_commitment)
      BOOST_SERIALIZE(blinded_asset_id)
      BOOST_SERIALIZE(encrypted_amount)
      BOOST_SERIALIZE(mix_attr)
    END_BOOST_SERIALIZATION()
  };

  struct zarcanum_tx_data_v1
  {
    uint64_t fee;

    BEGIN_SERIALIZE_OBJECT()
      FIELD(fee)
    END_SERIALIZE()

    BEGIN_BOOST_SERIALIZATION()
      BOOST_SERIALIZE(fee)
    END_BOOST_SERIALIZATION()
  };

  struct zc_asset_surjection_proof
  {
    std::vector<crypto::BGE_proof_s> bge_proofs; // one per output, non-aggregated version of Groth-Bootle-Esgin yet, need to be upgraded later -- sowle

    BEGIN_SERIALIZE_OBJECT()
      FIELD(bge_proofs)
    END_SERIALIZE()

    BEGIN_BOOST_SERIALIZATION()
      BOOST_SERIALIZE(bge_proofs)
    END_BOOST_SERIALIZATION()
  };

  // non-consoditated txs must have one of this objects in the attachments (elements count == vout.size())
  // consolidated -- one pre consolidated part (sum(elements count) == vout.size())
  struct zc_outs_range_proof
  {
    crypto::bpp_signature_serialized bpp; // for commitments in form: amount * U + mask * G
    crypto::vector_UG_aggregation_proof_serialized aggregation_proof; // E'_j = e_j * U + y'_j * G    +   vector Shnorr

    BEGIN_SERIALIZE_OBJECT()
      FIELD(bpp)
      FIELD(aggregation_proof)
    END_SERIALIZE()

    BEGIN_BOOST_SERIALIZATION()
      BOOST_SERIALIZE(bpp)
      BOOST_SERIALIZE(aggregation_proof)
    END_BOOST_SERIALIZATION()
  };

  // Zarcanum-aware CLSAG signature (one per ZC input)
  struct ZC_sig
  {
    crypto::public_key pseudo_out_amount_commitment = null_pkey; // premultiplied by 1/8
    crypto::public_key pseudo_out_blinded_asset_id  = null_pkey; // premultiplied by 1/8
    crypto::CLSAG_GGX_signature_serialized clsags_ggx;
    
    BEGIN_SERIALIZE_OBJECT()
      FIELD(pseudo_out_amount_commitment)
      FIELD(pseudo_out_blinded_asset_id)
      FIELD(clsags_ggx)
    END_SERIALIZE()

    BEGIN_BOOST_SERIALIZATION()
      BOOST_SERIALIZE(pseudo_out_amount_commitment)
      BOOST_SERIALIZE(pseudo_out_blinded_asset_id)
      BOOST_SERIALIZE(clsags_ggx)
    END_BOOST_SERIALIZATION()
  };

  // First part of a double Schnorr proof:
  //   1) for txs without ZC inputs: proves that balance point = lin(G) (cancels out G component of outputs' amount commitments, asset tags assumed to be H (native coin) and non-blinded)
  //   2) for txs with    ZC inputs: proves that balance point = lin(X) (cancels out X component of blinded asset tags within amount commitments for both outputs and inputs (pseudo outs))
  // Second part:
  //   proof of knowing transaction secret key (with respect to G)
  struct zc_balance_proof
  {
    crypto::generic_double_schnorr_sig_s dss;

    BEGIN_SERIALIZE_OBJECT()
      FIELD(dss)
    END_SERIALIZE()

    BEGIN_BOOST_SERIALIZATION()
      BOOST_SERIALIZE(dss)
    END_BOOST_SERIALIZATION()
  };


  struct zarcanum_sig : public crypto::zarcanum_proof
  {
    BEGIN_SERIALIZE_OBJECT()
      FIELD(d)
      FIELD(C)
      FIELD(C_prime);
      FIELD(E);
      FIELD(c);
      FIELD(y0);
      FIELD(y1);
      FIELD(y2);
      FIELD(y3);
      FIELD(y4);
      FIELD_N("E_range_proof", (crypto::bppe_signature_serialized&)E_range_proof);
      FIELD(pseudo_out_amount_commitment);
      FIELD_N("clsag_ggxxg", (crypto::CLSAG_GGXXG_signature_serialized&)clsag_ggxxg);
    END_SERIALIZE()

    BEGIN_BOOST_SERIALIZATION()
      BOOST_SERIALIZE(d)
      BOOST_SERIALIZE(C)
      BOOST_SERIALIZE(C_prime);
      BOOST_SERIALIZE(E);
      BOOST_SERIALIZE(c);
      BOOST_SERIALIZE(y0);
      BOOST_SERIALIZE(y1);
      BOOST_SERIALIZE(y2);
      BOOST_SERIALIZE(y3);
      BOOST_SERIALIZE(y4);
      BOOST_SERIALIZE((crypto::bppe_signature_serialized&)E_range_proof);
      BOOST_SERIALIZE(pseudo_out_amount_commitment);
      BOOST_SERIALIZE((crypto::CLSAG_GGXXG_signature_serialized&)clsag_ggxxg);
    END_BOOST_SERIALIZATION()
  };

//#pragma pack(pop)

  typedef boost::variant<txin_gen, txin_to_key, txin_multisig, txin_htlc, txin_zc_input> txin_v;

  typedef boost::variant<tx_out_bare, tx_out_zarcanum> tx_out_v;


  struct tx_comment
  {
    std::string comment;

    BEGIN_SERIALIZE()
      FIELD(comment)
    END_SERIALIZE()
  };

  struct tx_payer_old
  {
    account_public_address_old acc_addr;

    BEGIN_SERIALIZE()
      FIELD(acc_addr)
    END_SERIALIZE()
  };

  struct tx_payer
  {
    tx_payer() = default;
    tx_payer(const tx_payer_old& old) : acc_addr(account_public_address::from_old(old.acc_addr)) {}

    account_public_address acc_addr{};

    BEGIN_SERIALIZE()
      FIELD(acc_addr)
    END_SERIALIZE()
  };

  struct tx_receiver_old
  {
    account_public_address_old acc_addr;

    BEGIN_SERIALIZE()
      FIELD(acc_addr)
    END_SERIALIZE()
  };

  struct tx_receiver
  {
    tx_receiver() = default;
    tx_receiver(const tx_receiver_old& old) : acc_addr(account_public_address::from_old(old.acc_addr)) {}

    account_public_address acc_addr{};

    BEGIN_SERIALIZE()
      FIELD(acc_addr)
    END_SERIALIZE()
  };

  struct tx_crypto_checksum
  {
    //we put tx encrypted key_derivation into tx attachment/extra, to let sender decrypt attachments that had been encrypted. 
    // key_derivation encrypted on sender private send address
    crypto::key_derivation encrypted_key_derivation;
    uint32_t derivation_hash;

    BEGIN_SERIALIZE()
      FIELD(encrypted_key_derivation)
      FIELD(derivation_hash)
    END_SERIALIZE()
  };

  struct tx_derivation_hint
  {
    std::string msg;

    BEGIN_SERIALIZE()
      FIELD(msg)
    END_SERIALIZE()
  };

  struct tx_service_attachment
  {
    std::string service_id;    //string identifying service which addressed this attachment
    std::string instruction;   //string identifying specific instructions for service/way to interpret data
    std::string body;          //any data identifying service, options etc
    std::vector<crypto::public_key> security; //some of commands need proof of owner
    uint8_t flags;             //special flags (ex: TX_SERVICE_ATTACHMENT_ENCRYPT_BODY), see below

    BEGIN_SERIALIZE()
      FIELD(service_id)
      FIELD(instruction)
      FIELD(body)
      FIELD(security)
      FIELD(flags)
    END_SERIALIZE()

    BEGIN_KV_SERIALIZE_MAP()
      KV_SERIALIZE(service_id)                     DOC_DSCR("Service ID, identificator that diferent one service from another") DOC_EXMP("C")     DOC_END
      KV_SERIALIZE(instruction)                    DOC_DSCR("Instruction that make sence for this particular service") DOC_EXMP("K")              DOC_END
      KV_SERIALIZE_BLOB_AS_HEX_STRING(body)        DOC_DSCR("Hex-encoded body of the attachment") DOC_EXMP("dcfd7e055a6a3043ea3541a571a57a63e25dcc64e4a270f14fa9a58ac5dbec85dcfd7e055a6a3043ea3541a571a57a63e25dcc64e4a270f14fa9a58ac5dbec85")              DOC_END
      KV_SERIALIZE_CONTAINER_POD_AS_HEX(security)  DOC_DSCR("Hex-encoded public key of the owner, optional") DOC_EXMP("d8f6e37f28a632c06b0b3466db1b9d2d1b36a580ee35edfd971dc1423bc412a5")              DOC_END
      KV_SERIALIZE(flags)                          DOC_DSCR("Flags that help wallet to automatically process some properties of the attachment(combination of TX_SERVICE_ATTACHMENT_ENCRYPT_BODY=1, TX_SERVICE_ATTACHMENT_DEFLATE_BODY=2, TX_SERVICE_ATTACHMENT_ENCRYPT_BODY_ISOLATE_AUDITABLE=4,TX_SERVICE_ATTACHMENT_ENCRYPT_ADD_PROOF=8 )")  DOC_END
    END_KV_SERIALIZE_MAP()
  };

// applicable flags for tx_service_attachment::flags, can be combined using bitwise OR
#define TX_SERVICE_ATTACHMENT_ENCRYPT_BODY                    static_cast<uint8_t>(1 << 0)
#define TX_SERVICE_ATTACHMENT_DEFLATE_BODY                    static_cast<uint8_t>(1 << 1)

// with this flag enabled body encrypted/decrypted with the key created as a derivation from onetime key and "spend keys" of receiver
#define TX_SERVICE_ATTACHMENT_ENCRYPT_BODY_ISOLATE_AUDITABLE  static_cast<uint8_t>(1 << 2)  
// add proof of content, without revealing secrete
#define TX_SERVICE_ATTACHMENT_ENCRYPT_ADD_PROOF               static_cast<uint8_t>(1 << 3)  

  //,

  
  struct extra_user_data
  {
    std::string buff;
    
    BEGIN_SERIALIZE()
      FIELD(buff)
    END_SERIALIZE()
  };


  struct extra_alias_entry_base_old
  {
    account_public_address_old m_address;
    std::string m_text_comment;
    std::vector<crypto::secret_key> m_view_key; // only one or zero elments expected (std::vector is using as memory efficient container for such a case)
    std::vector<crypto::signature> m_sign;      // only one or zero elments expected (std::vector is using as memory efficient container for such a case)

    BEGIN_SERIALIZE()
      FIELD(m_address)
      FIELD(m_text_comment)
      FIELD(m_view_key)
      FIELD(m_sign)
    END_SERIALIZE()
  };

  struct extra_alias_entry_old : public extra_alias_entry_base_old
  {
    std::string m_alias;

    BEGIN_SERIALIZE()
      FIELD(m_alias)
      FIELDS(*static_cast<extra_alias_entry_base_old*>(this))
   END_SERIALIZE()
  };

  struct extra_alias_entry_base
  {
    extra_alias_entry_base() = default;
    extra_alias_entry_base(const extra_alias_entry_base_old& old)
      : m_address(account_public_address::from_old(old.m_address))
      , m_text_comment(old.m_text_comment)
      , m_view_key(old.m_view_key)
      , m_sign(old.m_sign)
    {
    }

    account_public_address m_address;
    std::string m_text_comment;
    std::vector<crypto::secret_key> m_view_key; // only one or zero elments expected (std::vector is using as memory efficient container for such a case)
    std::vector<crypto::signature> m_sign;      // only one or zero elments expected (std::vector is using as memory efficient container for such a case)

    BEGIN_SERIALIZE()
      FIELD(m_address)
      FIELD(m_text_comment)
      FIELD(m_view_key)
      FIELD(m_sign)
    END_SERIALIZE()
  };

  struct extra_alias_entry : public extra_alias_entry_base
  {
    extra_alias_entry() = default;
    extra_alias_entry(const extra_alias_entry_old& old)
      : extra_alias_entry_base(old)
      , m_alias(old.m_alias)
    {}
    
    std::string m_alias;

    BEGIN_SERIALIZE()
      FIELD(m_alias)
      FIELDS(*static_cast<extra_alias_entry_base*>(this))
    END_SERIALIZE()

    extra_alias_entry_old to_old() const
    {
      extra_alias_entry_old result = AUTO_VAL_INIT(result);
      result.m_address = m_address.to_old();
      result.m_text_comment = m_text_comment;
      result.m_view_key = m_view_key;
      result.m_sign = m_sign;
      result.m_alias = m_alias;
      return result;
    }
  };

#define ASSET_DESCRIPTOR_BASE_HF4_VER    0
#define ASSET_DESCRIPTOR_BASE_HF5_VER    2
#define ASSET_DESCRIPTOR_BASE_LAST_VER   2

  struct dummy
  {
      BEGIN_SERIALIZE() 
      END_SERIALIZE()

      BEGIN_BOOST_SERIALIZATION()
      END_BOOST_SERIALIZATION_TOTAL_FIELDS(0)
  };

  typedef boost::variant<dummy> asset_descriptor_base_etc_fields;
  typedef boost::variant<crypto::public_key, crypto::eth_public_key> asset_owner_pub_key_v;

  struct asset_descriptor_base
  {
    uint64_t            total_max_supply = 0;
    uint64_t            current_supply = 0;
    uint8_t             decimal_point = 0;
    std::string         ticker;
    std::string         full_name;
    std::string         meta_info;
    crypto::public_key  owner = currency::null_pkey; // consider premultipling by 1/8
    bool                hidden_supply = false;
    uint8_t             version = ASSET_DESCRIPTOR_BASE_HF4_VER;
    //version 1 members
    boost::optional<crypto::eth_public_key> owner_eth_pub_key; // note: the size is 33 bytes (if present) // NOTE: using boost::optional instead of std::optional because of the Boost compilation issue: https://github.com/boostorg/serialization/issues/319 -- sowle
    //version 2 members
    std::vector<asset_descriptor_base_etc_fields> etc;  //container for future use if we would be adding some optional parameters that is not known yet, but without mess related to format version


    BEGIN_VERSIONED_SERIALIZE(ASSET_DESCRIPTOR_BASE_LAST_VER, version)
      FIELD(total_max_supply)
      FIELD(current_supply)
      FIELD(decimal_point)
      FIELD(ticker)
      FIELD(full_name)
      FIELD(meta_info)
      FIELD(owner)
      FIELD(hidden_supply)
      END_VERSION_UNDER(1)
      FIELD(owner_eth_pub_key)
      END_VERSION_UNDER(2)
      FIELD(etc)
    END_SERIALIZE()

    BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(2)
    BEGIN_BOOST_SERIALIZATION()
      BOOST_SERIALIZE(total_max_supply)
      BOOST_SERIALIZE(current_supply)
      BOOST_SERIALIZE(decimal_point)
      BOOST_SERIALIZE(ticker)
      BOOST_SERIALIZE(full_name)
      BOOST_SERIALIZE(meta_info)
      BOOST_SERIALIZE(owner)
      BOOST_SERIALIZE(hidden_supply)
      BOOST_END_VERSION_UNDER(1)
      BOOST_SERIALIZE(owner_eth_pub_key)
      BOOST_END_VERSION_UNDER(2)
      BOOST_SERIALIZE(etc)
      BOOST_SERIALIZE(version)
    END_BOOST_SERIALIZATION_TOTAL_FIELDS(11)

    BEGIN_KV_SERIALIZE_MAP()
      KV_SERIALIZE(total_max_supply)  DOC_DSCR("Maximum possible supply for a given asset, cannot be changed after deployment.") DOC_EXMP(1000000000000000000)   DOC_END
      KV_SERIALIZE(current_supply)    DOC_DSCR("Currently emitted supply for the given asset (ignored for REGISTER operation).") DOC_EXMP(500000000000000000)    DOC_END
      KV_SERIALIZE(decimal_point)     DOC_DSCR("Decimal point.")                      DOC_EXMP(12)                        DOC_END
      KV_SERIALIZE(ticker)            DOC_DSCR("Ticker associated with the asset.")   DOC_EXMP("ZABC")                    DOC_END
      KV_SERIALIZE(full_name)         DOC_DSCR("Full name of the asset.")             DOC_EXMP("Lethean wrapped ABC")        DOC_END
      KV_SERIALIZE(meta_info)         DOC_DSCR("Any other information associated with the asset, by default in a json format.")           DOC_EXMP("{ \"some_arbitrary_field_name\": \"some arbitrary value\"}")      DOC_END
      KV_SERIALIZE_POD_AS_HEX_STRING(owner) DOC_DSCR("Owner's key, used only for EMIT and UPDATE validation, can be changed by transferring asset ownership.")   DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8")        DOC_END
      KV_SERIALIZE(hidden_supply)     DOC_DSCR("This field is reserved for future use and will be documented later.") DOC_END
      KV_SERIALIZE_POD_AS_HEX_STRING(owner_eth_pub_key) DOC_DSCR("[Optional] Owner's key in the case when ETH signature is used.") DOC_END
    END_KV_SERIALIZE_MAP()
  };


  struct asset_descriptor_with_id: public asset_descriptor_base
  {
    crypto::public_key asset_id = currency::null_pkey;

    /*
    BEGIN_VERSIONED_SERIALIZE()
      FIELD(*static_cast<asset_descriptor_base>(this))
      FIELD(asset_id)
    END_SERIALIZE()
    */

    BEGIN_KV_SERIALIZE_MAP()
      KV_CHAIN_BASE(asset_descriptor_base)
      KV_SERIALIZE_POD_AS_HEX_STRING(asset_id)      DOC_DSCR("Asset ID") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8")   DOC_END
    END_KV_SERIALIZE_MAP()
  };


#define ASSET_DESCRIPTOR_OPERATION_UNDEFINED     0
#define ASSET_DESCRIPTOR_OPERATION_REGISTER      1
#define ASSET_DESCRIPTOR_OPERATION_EMIT          2
#define ASSET_DESCRIPTOR_OPERATION_UPDATE        3
#define ASSET_DESCRIPTOR_OPERATION_PUBLIC_BURN   4

#define ASSET_DESCRIPTOR_OPERATION_HF4_VER       1
#define ASSET_DESCRIPTOR_OPERATION_HF5_VER       2
#define ASSET_DESCRIPTOR_OPERATION_LAST_VER      2

  typedef boost::variant<dummy> asset_descriptor_operation_etc_fields;

  struct asset_descriptor_operation
  {
    uint8_t                                     operation_type = ASSET_DESCRIPTOR_OPERATION_UNDEFINED;
    uint8_t                                     version = ASSET_DESCRIPTOR_OPERATION_HF4_VER;
                                                                       // register  emit  burn  update
    boost::optional<crypto::public_key>         opt_amount_commitment; //    +       +     +      -      (premultiplied by 1/8)
    boost::optional<crypto::public_key>         opt_asset_id;          //    -       +     +      +
    boost::optional<asset_descriptor_base>      opt_descriptor;        //    +       -     -      +
    boost::optional<uint64_t>                   opt_amount;            //    ?       ?     ?      -      (only for non-hidden supply)
    boost::optional<uint32_t>                   opt_asset_id_salt;     //    ?       -     -      -      (optional)
    std::vector<asset_descriptor_operation_etc_fields> etc;            //                                (reserved for future use)


    BEGIN_VERSIONED_SERIALIZE(ASSET_DESCRIPTOR_OPERATION_LAST_VER, version)
      CHAIN_TRANSITION_VER(0, asset_descriptor_operation_v1)
      CHAIN_TRANSITION_VER(1, asset_descriptor_operation_v1)
      FIELD(operation_type)
      FIELD(opt_amount_commitment)
      FIELD(opt_asset_id)
      FIELD(opt_descriptor)
      FIELD(opt_amount)
      FIELD(opt_asset_id_salt)
      FIELD(etc)
    END_SERIALIZE()

    BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(2)
    BEGIN_BOOST_SERIALIZATION()
      BOOST_CHAIN_TRANSITION_VER(1, asset_descriptor_operation_v1)
      BOOST_CHAIN_TRANSITION_VER(0, asset_descriptor_operation_v1)
      BOOST_SERIALIZE(version)
      BOOST_SERIALIZE(operation_type)
      BOOST_SERIALIZE(opt_amount_commitment)
      BOOST_SERIALIZE(opt_asset_id)
      BOOST_SERIALIZE(opt_descriptor)
      BOOST_SERIALIZE(opt_amount)
      BOOST_SERIALIZE(opt_asset_id_salt)
      BOOST_SERIALIZE(etc)
    END_BOOST_SERIALIZATION_TOTAL_FIELDS(8)

    BEGIN_KV_SERIALIZE_MAP()
      KV_SERIALIZE(version)                                     DOC_DSCR("Asset operation type struct version") DOC_EXMP(2) DOC_END
      KV_SERIALIZE(operation_type)                              DOC_DSCR("Asset operation type identifier") DOC_EXMP(1) DOC_END
      KV_SERIALIZE_POD_AS_HEX_STRING(opt_amount_commitment)     DOC_DSCR("(optional) Asset operation amount commitment (register/emit/burn).") DOC_EXMP("5688b56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
      KV_SERIALIZE_POD_AS_HEX_STRING(opt_asset_id)              DOC_DSCR("(optional) ID of an asset (emit/burn/update).") DOC_EXMP("cc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6") DOC_END
      KV_SERIALIZE(opt_descriptor)                              DOC_DSCR("(optional) Asset operation descriptor (register/update).") DOC_EXMP_AUTO()   DOC_END
      KV_SERIALIZE(opt_amount)                                  DOC_DSCR("(optional) Asset operation amount (register/emit/burn when supply is non-hidden).") DOC_EXMP_AUTO() DOC_END
      KV_SERIALIZE(opt_asset_id_salt)                           DOC_DSCR("(optional) Asset ID salt. May only be used for asset registration.") DOC_EXMP_AUTO() DOC_END
      //KV_SERIALIZE(etc)                                         DOC_DSCR("Extra operations") DOC_EXMP_AUTO() DOC_END <---- serialization for variant not supported yet
    END_KV_SERIALIZE_MAP()

  };

  struct asset_operation_proof
  {
    // linear composition proof for the fact amount_commitment = lin(asset_id, G)
    boost::optional<crypto::linear_composition_proof_s> opt_amount_commitment_composition_proof; // for hidden supply
    boost::optional<crypto::signature> opt_amount_commitment_g_proof; // for non-hidden supply, proofs that amount_commitment - supply * asset_id = lin(G)
    uint8_t version = 0;

    BEGIN_VERSIONED_SERIALIZE(0, version)
      FIELD(opt_amount_commitment_composition_proof)
      FIELD(opt_amount_commitment_g_proof)
    END_SERIALIZE()

    BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(1)
    BEGIN_BOOST_SERIALIZATION()
      BOOST_SERIALIZE(opt_amount_commitment_composition_proof)
      BOOST_SERIALIZE(opt_amount_commitment_g_proof)
      BOOST_END_VERSION_UNDER(1)
      BOOST_SERIALIZE(version)
    END_BOOST_SERIALIZATION()
  };


  struct asset_operation_ownership_proof
  {
    crypto::generic_schnorr_sig_s gss;
    uint8_t version = 0;

    BEGIN_VERSIONED_SERIALIZE(0, version)
      FIELD(gss)
    END_SERIALIZE()

    BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(1)
    BEGIN_BOOST_SERIALIZATION()
      BOOST_SERIALIZE(gss)
      BOOST_END_VERSION_UNDER(1)
      BOOST_SERIALIZE(version)
    END_BOOST_SERIALIZATION()
  };


  struct asset_operation_ownership_proof_eth
  {
    crypto::eth_signature eth_sig;            // 64 bytes
    uint8_t version = 0;

    BEGIN_VERSIONED_SERIALIZE(0, version)
      FIELD(eth_sig)
    END_SERIALIZE()

    BEGIN_BOOST_SERIALIZATION()
      BOOST_SERIALIZE(eth_sig)
      BOOST_SERIALIZE(version)
    END_BOOST_SERIALIZATION()

    BEGIN_KV_SERIALIZE_MAP()
      KV_SERIALIZE_POD_AS_HEX_STRING(eth_sig)    DOC_DSCR("HEX-encoded ETH signature (64 bytes)") DOC_EXMP("674bb56a5b4fa562e679ccacc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6add697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END
      KV_SERIALIZE(version)                      DOC_DSCR("Structure version") DOC_EXMP(0) DOC_END
    END_KV_SERIALIZE_MAP()
  };

  struct extra_padding
  {
    std::vector<uint8_t> buff; //stub
    
    BEGIN_SERIALIZE()
      FIELD(buff)
    END_SERIALIZE()
  };


  //number of block (or timestamp if v bigger then CURRENCY_MAX_BLOCK_NUMBER), used as a limitation: spend this tx not early then block/time
  struct etc_tx_details_unlock_time
  {
    uint64_t v;
    BEGIN_SERIALIZE()
      VARINT_FIELD(v)
    END_SERIALIZE()
  };

  //number of block (or timestamp if unlock_time_array[i] bigger then CURRENCY_MAX_BLOCK_NUMBER), used as a limitation: spend this tx not early then block/time
  //unlock_time_array[i], i - index of output, unlock_time_array.size() == vout.size()
  struct etc_tx_details_unlock_time2
  {
    std::vector<uint64_t> unlock_time_array;
    BEGIN_SERIALIZE()
      FIELD(unlock_time_array)
    END_SERIALIZE()
  };

  struct etc_tx_details_expiration_time
  {
    uint64_t v;
    BEGIN_SERIALIZE()
      VARINT_FIELD(v)
    END_SERIALIZE()
  };

  /*
  this structure used to put real time into PoS block(or could be other meaning), to have more suitable dates in transactions
  */
  struct etc_tx_time
  {
    uint64_t v;
    BEGIN_SERIALIZE()
      VARINT_FIELD(v)
    END_SERIALIZE()
  };

  struct etc_tx_details_flags
  {
    uint64_t v;
    BEGIN_SERIALIZE()
      VARINT_FIELD(v)
    END_SERIALIZE()
  };

  struct etc_tx_flags16_t 
  {
    uint16_t v;
    BEGIN_SERIALIZE()
      FIELD(v)
    END_SERIALIZE()
  };

  typedef boost::mpl::vector23<
    tx_service_attachment, tx_comment, tx_payer_old, tx_receiver_old, tx_derivation_hint, std::string, tx_crypto_checksum, etc_tx_time, etc_tx_details_unlock_time, etc_tx_details_expiration_time,
    etc_tx_details_flags, crypto::public_key, extra_attachment_info, extra_alias_entry_old, extra_user_data, extra_padding, etc_tx_flags16_t, etc_tx_details_unlock_time2,
    tx_payer, tx_receiver, extra_alias_entry, zarcanum_tx_data_v1, asset_descriptor_operation
  > all_payload_types;
  
  typedef boost::make_variant_over<all_payload_types>::type payload_items_v;
  typedef payload_items_v extra_v;
  typedef payload_items_v attachment_v;




  //classic CryptoNote signature by Nicolas Van Saberhagen
  struct NLSAG_sig
  {
    std::vector<crypto::signature> s;

    BEGIN_SERIALIZE_OBJECT()
      FIELD(s)
    END_SERIALIZE()

    BEGIN_BOOST_SERIALIZATION()
      BOOST_SERIALIZE(s)
    END_BOOST_SERIALIZATION()
  };

  struct void_sig
  {
    //TODO:
    BEGIN_SERIALIZE_OBJECT()
    END_SERIALIZE()

    BEGIN_BOOST_SERIALIZATION()
    END_BOOST_SERIALIZATION()
  };

  typedef boost::variant<NLSAG_sig, void_sig, ZC_sig, zarcanum_sig> signature_v;

  typedef boost::variant<zc_asset_surjection_proof, zc_outs_range_proof, zc_balance_proof, asset_operation_proof, asset_operation_ownership_proof, asset_operation_ownership_proof_eth> proof_v;


  //include backward compatibility defintions
  #include "currency_basic_backward_comp.inl"

  class transaction_prefix
  {
  public:
    uint64_t version = 0;
    uint8_t hardfork_id = 0;
    std::vector<txin_v> vin;
    std::vector<extra_v> extra;
    std::vector<tx_out_v> vout;

    BEGIN_SERIALIZE()
      VARINT_FIELD(version)
      CHAIN_TRANSITION_VER(TRANSACTION_VERSION_INITAL, transaction_prefix_v1)
      CHAIN_TRANSITION_VER(TRANSACTION_VERSION_PRE_HF4, transaction_prefix_v1)
      if(CURRENT_TRANSACTION_VERSION < version) return false;
      FIELD(vin)
      FIELD(extra)
      FIELD(vout)
      if(version < TRANSACTION_VERSION_POST_HF5) return true;
      FIELD(hardfork_id)
    END_SERIALIZE()
  };

/*
  TX_FLAG_SIGNATURE_MODE_SEPARATE - flag that set different signature validation mode.
  With this mode each signature sign prefix with it's own txin entry only, that allow 
  construct transaction partially, supposed to be in use to construct multisig-based escrow transaction. 
*/
#define TX_FLAG_SIGNATURE_MODE_SEPARATE  0x01




  class transaction: public transaction_prefix
  {
  public:
    std::vector<attachment_v> attachment;
    std::vector<signature_v> signatures;
    std::vector<proof_v> proofs;

    BEGIN_SERIALIZE_OBJECT()
      FIELDS(*static_cast<transaction_prefix *>(this))
      CHAIN_TRANSITION_VER(TRANSACTION_VERSION_INITAL, transaction_v1)
      CHAIN_TRANSITION_VER(TRANSACTION_VERSION_PRE_HF4, transaction_v1)
      FIELD(attachment)
      FIELD(signatures)
      FIELD(proofs)
    END_SERIALIZE()


    BOOST_SERIALIZATION_CURRENT_ARCHIVE_VER(0)
    BEGIN_BOOST_SERIALIZATION()
      BOOST_SERIALIZE(version)
      BOOST_SERIALIZE(vin)
      BOOST_SERIALIZE(vout)
      BOOST_SERIALIZE(extra)
      BOOST_SERIALIZE(signatures)
      BOOST_SERIALIZE(attachment)
      BOOST_END_VERSION_UNDER(1)
      BOOST_SERIALIZE(proofs)
    END_BOOST_SERIALIZATION()
  };

  



  /************************************************************************/
  /*                                                                      */
  /************************************************************************/
  struct block_header
  {
    uint8_t major_version;
    uint8_t minor_version;
    uint64_t timestamp;
    crypto::hash  prev_id;
    uint64_t nonce;
    uint8_t flags;

    BEGIN_SERIALIZE()
      FIELD(major_version)
      if(major_version > CURRENT_BLOCK_MAJOR_VERSION) return false;
      FIELD(nonce)
      FIELD(prev_id)
      VARINT_FIELD(minor_version)
      VARINT_FIELD(timestamp)
      FIELD(flags)
    END_SERIALIZE()
  };

  struct block: public block_header
  {
    transaction miner_tx;
    std::vector<crypto::hash> tx_hashes;
    
    BEGIN_SERIALIZE_OBJECT()
      FIELDS(*static_cast<block_header *>(this))
      FIELD(miner_tx)
      FIELD(tx_hashes)
    END_SERIALIZE()
  };




  struct keypair
  {
    crypto::public_key pub;
    crypto::secret_key sec;

    static inline keypair generate()
    {
      keypair k;
      generate_keys(k.pub, k.sec);
      return k;
    }
  };
  const static keypair null_keypair = AUTO_VAL_INIT(null_keypair);
  //---------------------------------------------------------------
  //PoS
  //based from ppcoin/novacoin approach

  /*
  POS PROTOCOL, stake modifier
  */
  //-------------------------------------------------------------------------------------------------------------------

#pragma pack(push, 1)
  struct stake_modifier_type
  {
    crypto::hash last_pow_id;
    crypto::hash last_pos_kernel_id;
  };

  struct stake_kernel
  {
    stake_modifier_type stake_modifier;
    uint64_t block_timestamp;             //this block timestamp
    crypto::key_image kimage;
  };
#pragma pack(pop)

  struct pos_entry
  {
    uint64_t amount;
    uint64_t g_index;              // global output index. (could be WALLET_GLOBAL_OUTPUT_INDEX_UNDEFINED)
    crypto::key_image keyimage;
    uint64_t block_timestamp;
    uint64_t stake_unlock_time;

    crypto::hash tx_id;          // stake output source tx id
    uint64_t tx_out_index;       // stake output local index in its tx

    //not for serialization

    uint64_t wallet_index;       // transfer id index

    BEGIN_KV_SERIALIZE_MAP()
      KV_SERIALIZE(amount)
      KV_SERIALIZE(g_index)
      KV_SERIALIZE(stake_unlock_time)
      KV_SERIALIZE(block_timestamp)
      KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(keyimage)
      KV_SERIALIZE(tx_id)
      KV_SERIALIZE(tx_out_index)
    END_KV_SERIALIZE_MAP()
  };

  bool operator ==(const currency::transaction& a, const currency::transaction& b);
  bool operator ==(const currency::block& a, const currency::block& b);
  bool operator ==(const currency::extra_attachment_info& a, const currency::extra_attachment_info& b);
  bool operator ==(const currency::NLSAG_sig& a, const currency::NLSAG_sig& b);
  bool operator ==(const currency::void_sig& a, const currency::void_sig& b);
  bool operator ==(const currency::ZC_sig& a, const currency::ZC_sig& b);
  bool operator ==(const currency::zarcanum_sig& a, const currency::zarcanum_sig& b);
  bool operator ==(const currency::ref_by_id& a, const currency::ref_by_id& b);

  // TODO: REPLACE all of the following operators to "bool operator==(..) const = default" once we moved to C++20 -- sowle
  bool operator ==(const currency::signed_parts& a, const currency::signed_parts& b);
  bool operator ==(const currency::txin_gen& a, const currency::txin_gen& b);
  bool operator ==(const currency::txin_to_key& a, const currency::txin_to_key& b);
  bool operator ==(const currency::txin_multisig& a, const currency::txin_multisig& b);
  bool operator ==(const currency::txin_htlc& a, const currency::txin_htlc& b);
  bool operator ==(const currency::txin_zc_input& a, const currency::txin_zc_input& b);
} // namespace currency

POD_MAKE_HASHABLE(currency, account_public_address);
POD_MAKE_HASHABLE(currency, account_public_address_old);

BLOB_SERIALIZER(currency::txout_to_key);

#define SET_VARIANT_TAGS(type_name, id, json_tag)  \
  VARIANT_TAG(binary_archive, type_name, id); \
  VARIANT_TAG(json_archive, type_name, json_tag)



LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::asset_descriptor_operation);
LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::asset_descriptor_base);
LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::asset_operation_proof);
LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::asset_operation_ownership_proof);
LOOP_BACK_BOOST_SERIALIZATION_VERSION(currency::transaction);


// txin_v variant currency
SET_VARIANT_TAGS(currency::txin_gen, 0, "gen");
SET_VARIANT_TAGS(currency::txin_to_key, 1, "key");
SET_VARIANT_TAGS(currency::txin_multisig, 2, "multisig");
// txout_target_v variant definitions
SET_VARIANT_TAGS(currency::txout_to_key, 3, "key");
SET_VARIANT_TAGS(currency::txout_multisig, 4, "multisig");
SET_VARIANT_TAGS(currency::transaction, 5, "tx");
SET_VARIANT_TAGS(currency::block, 6, "block");
//attachment_v definitions 
SET_VARIANT_TAGS(currency::tx_comment, 7, "comment");
SET_VARIANT_TAGS(currency::tx_payer_old, 8, "payer");
SET_VARIANT_TAGS(std::string, 9, "string");
SET_VARIANT_TAGS(currency::tx_crypto_checksum, 10, "checksum");
SET_VARIANT_TAGS(currency::tx_derivation_hint, 11, "derivation_hint");
SET_VARIANT_TAGS(currency::tx_service_attachment, 12, "attachment");
//SET_VARIANT_TAGS(currency::tx_onetime_secret_key, 13, "sec_key");
SET_VARIANT_TAGS(currency::etc_tx_details_unlock_time, 14, "unlock_time");
SET_VARIANT_TAGS(currency::etc_tx_details_expiration_time, 15, "expiration_time");
SET_VARIANT_TAGS(currency::etc_tx_details_flags, 16, "flags");
//txin_etc_details_v definitions 
SET_VARIANT_TAGS(currency::signed_parts, 17, "signed_outs");
//extra_v definitions
SET_VARIANT_TAGS(currency::extra_attachment_info, 18, "extra_attach_info");
SET_VARIANT_TAGS(currency::extra_user_data, 19, "user_data");
SET_VARIANT_TAGS(currency::extra_alias_entry_old, 20, "alias_entry");
SET_VARIANT_TAGS(currency::extra_padding, 21, "extra_padding");
SET_VARIANT_TAGS(crypto::public_key, 22, "pub_key");
SET_VARIANT_TAGS(currency::etc_tx_flags16_t, 23, "etc_tx_flags16");
SET_VARIANT_TAGS(uint16_t, 24, "derive_xor");
//txout_v 
SET_VARIANT_TAGS(currency::ref_by_id, 25, "ref_by_id");
SET_VARIANT_TAGS(uint64_t, 26, "uint64_t");
//etc
SET_VARIANT_TAGS(currency::etc_tx_time, 27, "etc_tx_time");
SET_VARIANT_TAGS(uint32_t, 28, "uint32_t");
SET_VARIANT_TAGS(currency::tx_receiver_old, 29, "payer"); // -- original
//SET_VARIANT_TAGS(currency::tx_receiver_old, 29, "receiver");
SET_VARIANT_TAGS(currency::etc_tx_details_unlock_time2, 30, "unlock_time2");

SET_VARIANT_TAGS(currency::tx_payer, 31, "payer2");
SET_VARIANT_TAGS(currency::tx_receiver, 32, "receiver2");

// @#@ TODO @#@
SET_VARIANT_TAGS(currency::extra_alias_entry, 33, "alias_entry2");

//htlc
SET_VARIANT_TAGS(currency::txin_htlc, 34, "txin_htlc");
SET_VARIANT_TAGS(currency::txout_htlc, 35, "txout_htlc");

SET_VARIANT_TAGS(currency::tx_out_bare, 36, "tx_out_bare");

// Zarcanum
SET_VARIANT_TAGS(currency::txin_zc_input, 37, "txin_zc_input");
SET_VARIANT_TAGS(currency::tx_out_zarcanum, 38, "tx_out_zarcanum");
SET_VARIANT_TAGS(currency::zarcanum_tx_data_v1, 39, "zarcanum_tx_data_v1");
SET_VARIANT_TAGS(crypto::bpp_signature_serialized, 40, "bpp_signature_serialized");
SET_VARIANT_TAGS(crypto::bppe_signature_serialized, 41, "bppe_signature_serialized");
SET_VARIANT_TAGS(currency::NLSAG_sig, 42, "NLSAG_sig");
SET_VARIANT_TAGS(currency::ZC_sig, 43, "ZC_sig");
SET_VARIANT_TAGS(currency::void_sig, 44, "void_sig");
SET_VARIANT_TAGS(currency::zarcanum_sig, 45, "zarcanum_sig");
SET_VARIANT_TAGS(currency::zc_asset_surjection_proof, 46, "zc_asset_surjection_proof");
SET_VARIANT_TAGS(currency::zc_outs_range_proof, 47, "zc_outs_range_proof");
SET_VARIANT_TAGS(currency::zc_balance_proof, 48, "zc_balance_proof");

SET_VARIANT_TAGS(currency::asset_descriptor_operation, 49, "asset_descriptor_base");
SET_VARIANT_TAGS(currency::asset_operation_proof, 50, "asset_operation_proof");
SET_VARIANT_TAGS(currency::asset_operation_ownership_proof, 51, "asset_operation_ownership_proof");
SET_VARIANT_TAGS(currency::asset_operation_ownership_proof_eth, 52, "asset_operation_ownership_proof_eth");

SET_VARIANT_TAGS(crypto::eth_public_key, 60, "eth_public_key");
//SET_VARIANT_TAGS(crypto::eth_signature, 61, "eth_signature");
SET_VARIANT_TAGS(currency::dummy, 62, "dummy");




#undef SET_VARIANT_TAGS
