// Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once #include #include #include #include #include // memcmp #include #include "serialization/serialization.h" #include "serialization/variant.h" #include "serialization/vector.h" #include "serialization/binary_archive.h" #include "serialization/json_archive.h" #include "serialization/debug_archive.h" #include "serialization/crypto.h" #include "serialization/keyvalue_serialization.h" // eepe named serialization #include "string_tools.h" #include "cryptonote_config.h" #include "crypto/crypto.h" #include "crypto/hash.h" #include "misc_language.h" #include "tx_extra.h" namespace cryptonote { struct block; class transaction; struct tx_extra_merge_mining_tag; // Implemented in cryptonote_format_utils.cpp bool get_transaction_hash(const transaction& t, crypto::hash& res); bool get_mm_tag_from_extra(const std::vector& tx, tx_extra_merge_mining_tag& mm_tag); const static crypto::hash null_hash = AUTO_VAL_INIT(null_hash); const static crypto::public_key null_pkey = AUTO_VAL_INIT(null_pkey); typedef std::vector ring_signature; /* outputs */ struct txout_to_script { std::vector keys; std::vector script; BEGIN_SERIALIZE_OBJECT() FIELD(keys) FIELD(script) END_SERIALIZE() }; struct txout_to_scripthash { crypto::hash hash; }; struct txout_to_key { txout_to_key() { } txout_to_key(const crypto::public_key &_key) : key(_key) { } crypto::public_key key; }; #pragma pack(push, 1) struct bb_txout_to_key { bb_txout_to_key() { } bb_txout_to_key(const crypto::public_key &_key) : key(_key) { } 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() }; struct txin_to_script { crypto::hash prev; size_t prevout; std::vector 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 sigset; BEGIN_SERIALIZE_OBJECT() FIELD(prev) VARINT_FIELD(prevout) FIELD(script) FIELD(sigset) END_SERIALIZE() }; struct txin_to_key { uint64_t amount; std::vector key_offsets; crypto::key_image k_image; // double spending protection BEGIN_SERIALIZE_OBJECT() VARINT_FIELD(amount) FIELD(key_offsets) FIELD(k_image) END_SERIALIZE() }; typedef boost::variant txin_v; typedef boost::variant txout_target_v; typedef boost::variant bb_txout_target_v; //typedef std::pair out_t; struct tx_out { uint64_t amount; txout_target_v target; BEGIN_SERIALIZE_OBJECT() VARINT_FIELD(amount) FIELD(target) END_SERIALIZE() }; struct bb_tx_out { uint64_t amount; bb_txout_target_v target; BEGIN_SERIALIZE_OBJECT() VARINT_FIELD(amount) FIELD(target) END_SERIALIZE() }; class transaction_prefix { public: // tx information size_t version; uint64_t unlock_time; //number of block (or time), used as a limitation like: spend this tx not early then block/time std::vector vin; std::vector vout; //extra std::vector extra; BEGIN_SERIALIZE() VARINT_FIELD(version) if(CURRENT_TRANSACTION_VERSION < version) return false; VARINT_FIELD(unlock_time) FIELD(vin) FIELD(vout) FIELD(extra) END_SERIALIZE() protected: transaction_prefix(){} }; class transaction: public transaction_prefix { public: std::vector > signatures; //count signatures always the same as inputs count transaction(); virtual ~transaction(); void set_null(); BEGIN_SERIALIZE_OBJECT() FIELDS(*static_cast(this)) ar.tag("signatures"); ar.begin_array(); PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures); bool signatures_not_expected = signatures.empty(); if (!signatures_not_expected && vin.size() != signatures.size()) return false; for (size_t i = 0; i < vin.size(); ++i) { size_t signature_size = get_signature_size(vin[i]); if (signatures_not_expected) { if (0 == signature_size) continue; else return false; } PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]); if (signature_size != signatures[i].size()) return false; FIELDS(signatures[i]); if (vin.size() - i > 1) ar.delimit_array(); } ar.end_array(); END_SERIALIZE() private: static size_t get_signature_size(const txin_v& tx_in); }; class bb_transaction_prefix { public: // tx information size_t version; uint64_t unlock_time; //number of block (or time), used as a limitation like: spend this tx not early then block/time std::vector vin; std::vector vout; //extra std::vector extra; BEGIN_SERIALIZE() VARINT_FIELD(version) VARINT_FIELD(unlock_time) FIELD(vin) FIELD(vout) FIELD(extra) END_SERIALIZE() protected: bb_transaction_prefix(){} }; class bb_transaction: public bb_transaction_prefix { public: std::vector > signatures; //count signatures always the same as inputs count bb_transaction(); virtual ~bb_transaction(); void set_null(); BEGIN_SERIALIZE_OBJECT() FIELDS(*static_cast(this)) FIELD(signatures) END_SERIALIZE() static size_t get_signature_size(const txin_v& tx_in); }; inline transaction::transaction() { set_null(); } inline transaction::~transaction() { //set_null(); } inline void transaction::set_null() { version = 0; unlock_time = 0; vin.clear(); vout.clear(); extra.clear(); signatures.clear(); } inline size_t transaction::get_signature_size(const txin_v& tx_in) { struct txin_signature_size_visitor : public boost::static_visitor { size_t operator()(const txin_gen& txin) const{return 0;} size_t operator()(const txin_to_script& txin) const{return 0;} size_t operator()(const txin_to_scripthash& txin) const{return 0;} size_t operator()(const txin_to_key& txin) const {return txin.key_offsets.size();} }; return boost::apply_visitor(txin_signature_size_visitor(), tx_in); } inline bb_transaction::bb_transaction() { set_null(); } inline bb_transaction::~bb_transaction() { //set_null(); } inline void bb_transaction::set_null() { version = 0; unlock_time = 0; vin.clear(); vout.clear(); extra.clear(); signatures.clear(); } inline size_t bb_transaction::get_signature_size(const txin_v& tx_in) { struct txin_signature_size_visitor : public boost::static_visitor { size_t operator()(const txin_gen& txin) const{return 0;} size_t operator()(const txin_to_script& txin) const{return 0;} size_t operator()(const txin_to_scripthash& txin) const{return 0;} size_t operator()(const txin_to_key& txin) const {return txin.key_offsets.size();} }; return boost::apply_visitor(txin_signature_size_visitor(), tx_in); } /************************************************************************/ /* */ /************************************************************************/ const uint8_t CURRENT_BYTECOIN_BLOCK_MAJOR_VERSION = 1; struct bytecoin_block { uint8_t major_version; uint8_t minor_version; crypto::hash prev_id; uint32_t nonce; size_t number_of_transactions; std::vector miner_tx_branch; transaction miner_tx; std::vector blockchain_branch; }; struct serializable_bytecoin_block { bytecoin_block& b; uint64_t& timestamp; bool hashing_serialization; bool header_only; serializable_bytecoin_block(bytecoin_block& b_, uint64_t& timestamp_, bool hashing_serialization_, bool header_only_) : b(b_), timestamp(timestamp_), hashing_serialization(hashing_serialization_), header_only(header_only_) { } BEGIN_SERIALIZE_OBJECT() VARINT_FIELD_N("major_version", b.major_version); if(b.major_version > CURRENT_BYTECOIN_BLOCK_MAJOR_VERSION) return false; VARINT_FIELD_N("minor_version", b.minor_version); VARINT_FIELD(timestamp); FIELD_N("prev_id", b.prev_id); FIELD_N("nonce", b.nonce); if (hashing_serialization) { crypto::hash miner_tx_hash; if (!get_transaction_hash(b.miner_tx, miner_tx_hash)) return false; crypto::hash merkle_root; crypto::tree_hash_from_branch(b.miner_tx_branch.data(), b.miner_tx_branch.size(), miner_tx_hash, 0, merkle_root); FIELD(merkle_root); } VARINT_FIELD_N("number_of_transactions", b.number_of_transactions); if (b.number_of_transactions < 1) return false; if (!header_only) { ar.tag("miner_tx_branch"); ar.begin_array(); size_t branch_size = crypto::tree_depth(b.number_of_transactions); PREPARE_CUSTOM_VECTOR_SERIALIZATION(branch_size, const_cast(b).miner_tx_branch); if (b.miner_tx_branch.size() != branch_size) return false; for (size_t i = 0; i < branch_size; ++i) { FIELDS(b.miner_tx_branch[i]); if (i + 1 < branch_size) ar.delimit_array(); } ar.end_array(); FIELD(b.miner_tx); tx_extra_merge_mining_tag mm_tag; if (!get_mm_tag_from_extra(b.miner_tx.extra, mm_tag)) return false; ar.tag("blockchain_branch"); ar.begin_array(); PREPARE_CUSTOM_VECTOR_SERIALIZATION(mm_tag.depth, const_cast(b).blockchain_branch); if (mm_tag.depth != b.blockchain_branch.size()) return false; for (size_t i = 0; i < mm_tag.depth; ++i) { FIELDS(b.blockchain_branch[i]); if (i + 1 < mm_tag.depth) ar.delimit_array(); } ar.end_array(); } END_SERIALIZE() }; // Implemented below inline serializable_bytecoin_block make_serializable_bytecoin_block(const block& b, bool hashing_serialization, bool header_only); struct block_header { uint8_t major_version; uint8_t minor_version; uint64_t timestamp; crypto::hash prev_id; uint32_t nonce; BEGIN_SERIALIZE() VARINT_FIELD(major_version) if(major_version > BLOCK_MAJOR_VERSION_2) return false; VARINT_FIELD(minor_version) if (BLOCK_MAJOR_VERSION_1 == major_version) { VARINT_FIELD(timestamp) } FIELD(prev_id) if (BLOCK_MAJOR_VERSION_1 == major_version) { FIELD(nonce) } END_SERIALIZE() }; struct block: public block_header { bytecoin_block parent_block; transaction miner_tx; std::vector tx_hashes; BEGIN_SERIALIZE_OBJECT() FIELDS(*static_cast(this)) if (BLOCK_MAJOR_VERSION_2 <= major_version) { auto sbb = make_serializable_bytecoin_block(*this, false, false); FIELD_N("parent_block", sbb); } FIELD(miner_tx) FIELD(tx_hashes) END_SERIALIZE() }; inline serializable_bytecoin_block make_serializable_bytecoin_block(const block& b, bool hashing_serialization, bool header_only) { block& block_ref = const_cast(b); return serializable_bytecoin_block(block_ref.parent_block, block_ref.timestamp, hashing_serialization, header_only); } struct bb_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) FIELD(nonce) FIELD(prev_id) VARINT_FIELD(minor_version) VARINT_FIELD(timestamp) FIELD(flags) END_SERIALIZE() }; struct bb_block: public bb_block_header { bb_transaction miner_tx; std::vector tx_hashes; BEGIN_SERIALIZE() FIELDS(*static_cast(this)) FIELD(miner_tx) FIELD(tx_hashes) END_SERIALIZE() }; /************************************************************************/ /* */ /************************************************************************/ 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() }; struct keypair { crypto::public_key pub; crypto::secret_key sec; static inline keypair generate() { keypair k; generate_keys(k.pub, k.sec); return k; } }; //--------------------------------------------------------------- } BLOB_SERIALIZER(cryptonote::txout_to_key); BLOB_SERIALIZER(cryptonote::bb_txout_to_key); BLOB_SERIALIZER(cryptonote::txout_to_scripthash); VARIANT_TAG(binary_archive, cryptonote::txin_gen, 0xff); VARIANT_TAG(binary_archive, cryptonote::txin_to_script, 0x0); VARIANT_TAG(binary_archive, cryptonote::txin_to_scripthash, 0x1); VARIANT_TAG(binary_archive, cryptonote::txin_to_key, 0x2); VARIANT_TAG(binary_archive, cryptonote::txout_to_script, 0x0); VARIANT_TAG(binary_archive, cryptonote::txout_to_scripthash, 0x1); VARIANT_TAG(binary_archive, cryptonote::txout_to_key, 0x2); VARIANT_TAG(binary_archive, cryptonote::bb_txout_to_key, 0x2); VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc); VARIANT_TAG(binary_archive, cryptonote::block, 0xbb); VARIANT_TAG(json_archive, cryptonote::txin_gen, "gen"); VARIANT_TAG(json_archive, cryptonote::txin_to_script, "script"); VARIANT_TAG(json_archive, cryptonote::txin_to_scripthash, "scripthash"); VARIANT_TAG(json_archive, cryptonote::txin_to_key, "key"); VARIANT_TAG(json_archive, cryptonote::txout_to_script, "script"); VARIANT_TAG(json_archive, cryptonote::txout_to_scripthash, "scripthash"); VARIANT_TAG(json_archive, cryptonote::txout_to_key, "key"); VARIANT_TAG(json_archive, cryptonote::bb_txout_to_key, "key"); VARIANT_TAG(json_archive, cryptonote::transaction, "tx"); VARIANT_TAG(json_archive, cryptonote::block, "block"); VARIANT_TAG(debug_archive, cryptonote::txin_gen, "gen"); VARIANT_TAG(debug_archive, cryptonote::txin_to_script, "script"); VARIANT_TAG(debug_archive, cryptonote::txin_to_scripthash, "scripthash"); VARIANT_TAG(debug_archive, cryptonote::txin_to_key, "key"); VARIANT_TAG(debug_archive, cryptonote::txout_to_script, "script"); VARIANT_TAG(debug_archive, cryptonote::txout_to_scripthash, "scripthash"); VARIANT_TAG(debug_archive, cryptonote::txout_to_key, "key"); VARIANT_TAG(debug_archive, cryptonote::bb_txout_to_key, "key"); VARIANT_TAG(debug_archive, cryptonote::transaction, "tx"); VARIANT_TAG(debug_archive, cryptonote::block, "block");