diff --git a/binding.gyp b/binding.gyp index ff6fdd5..db9c628 100644 --- a/binding.gyp +++ b/binding.gyp @@ -31,7 +31,7 @@ "-fno-exceptions -std=gnu11 -march=native -fPIC -DNDEBUG -Ofast -funroll-loops -fvariable-expansion-in-unroller -ftree-loop-if-convert-stores -fmerge-all-constants -fbranch-target-load-optimize2" ], "cflags_cc": [ - "-fexceptions -frtti -std=gnu++11 -march=native -fPIC -DNDEBUG -Ofast -s -funroll-loops -fvariable-expansion-in-unroller -ftree-loop-if-convert-stores -fmerge-all-constants -fbranch-target-load-optimize2" + "-fexceptions -frtti -std=c++14 -march=native -fPIC -DNDEBUG -Ofast -s -funroll-loops -fvariable-expansion-in-unroller -ftree-loop-if-convert-stores -fmerge-all-constants -fbranch-target-load-optimize2" ], "xcode_settings": { "OTHER_CFLAGS": [ "-fexceptions -frtti" ] diff --git a/package.json b/package.json index 143a24a..c2e716a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cryptoforknote-util", - "version": "14.0.1", + "version": "15.0.0", "main": "cryptoforknote-util", "author": { "name": "LucasJones", diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 06b3beb..e158b1d 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -1,12 +1,13 @@ #pragma once -#define CURRENT_TRANSACTION_VERSION 1 -#define POU_TRANSACTION_VERSION 6 -#define COLLATERAL_TRANSACTION_VERSION 7 -#define OFFSHORE_TRANSACTION_VERSION 3 -#define HF_VERSION_XASSET_FEES_V2 17 -#define HF_VERSION_HAVEN2 18 -#define HF_VERSION_USE_COLLATERAL 20 +#define CURRENT_TRANSACTION_VERSION 1 +#define POU_TRANSACTION_VERSION 6 +#define COLLATERAL_TRANSACTION_VERSION 7 +#define HAVEN_TYPES_TRANSACTION_VERSION 8 +#define OFFSHORE_TRANSACTION_VERSION 3 +#define HF_VERSION_XASSET_FEES_V2 17 +#define HF_VERSION_HAVEN2 18 +#define HF_VERSION_USE_COLLATERAL 20 // UNLOCK TIMES #define TX_V6_OFFSHORE_UNLOCK_BLOCKS 21*720 // 21 day unlock time diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index d61df75..f1d5852 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -89,6 +89,48 @@ namespace cryptonote END_SERIALIZE() }; + // outputs <= HF_VERSION_VIEW_TAGS + struct txout_haven_key + { + txout_haven_key() { } + txout_haven_key(const crypto::public_key &_key, const std::string &_asset_type, const uint64_t &_unlock_time, const bool &_is_collateral, const bool &_is_collateral_change) : key(_key), asset_type(_asset_type), unlock_time(_unlock_time), is_collateral(_is_collateral), is_collateral_change(_is_collateral_change) { } + crypto::public_key key; + std::string asset_type; + uint64_t unlock_time; + bool is_collateral; + bool is_collateral_change; + + BEGIN_SERIALIZE_OBJECT() + FIELD(key) + FIELD(asset_type) + VARINT_FIELD(unlock_time) + FIELD(is_collateral) + FIELD(is_collateral_change) + END_SERIALIZE() + }; + + // outputs >= HF_VERSION_VIEW_TAGS + struct txout_haven_tagged_key + { + txout_haven_tagged_key() { } + txout_haven_tagged_key(const crypto::public_key &_key, const std::string &_asset_type, const uint64_t &_unlock_time, const bool &_is_collateral, const bool &_is_collateral_change, const crypto::view_tag &_view_tag) : key(_key), asset_type(_asset_type), unlock_time(_unlock_time), is_collateral(_is_collateral), is_collateral_change(_is_collateral_change), view_tag(_view_tag) { } + crypto::public_key key; + std::string asset_type; + uint64_t unlock_time; + bool is_collateral; + bool is_collateral_change; + crypto::view_tag view_tag; // optimization to reduce scanning time + + BEGIN_SERIALIZE_OBJECT() + FIELD(key) + FIELD(asset_type) + VARINT_FIELD(unlock_time) + FIELD(is_collateral) + FIELD(is_collateral_change) + FIELD(view_tag) + END_SERIALIZE() + }; + struct txout_offshore { txout_offshore() { } @@ -203,6 +245,21 @@ namespace cryptonote END_SERIALIZE() }; + struct txin_haven_key + { + uint64_t amount; + std::string asset_type; + std::vector key_offsets; + crypto::key_image k_image; // double spending protection + + BEGIN_SERIALIZE_OBJECT() + VARINT_FIELD(amount) + FIELD(asset_type) + FIELD(key_offsets) + FIELD(k_image) + END_SERIALIZE() + }; + struct txin_xasset { uint64_t amount; @@ -233,11 +290,11 @@ namespace cryptonote END_SERIALIZE() }; - typedef boost::variant txin_v; + typedef boost::variant txin_v; typedef boost::variant txin_zephyr_v; typedef boost::variant txout_target_v; - typedef boost::variant txout_xhv_target_v; + typedef boost::variant txout_xhv_target_v; typedef boost::variant txout_stablero_target_v; @@ -327,62 +384,288 @@ namespace cryptonote }; BEGIN_SERIALIZE() - VARINT_FIELD(version) - if (version > loki_version_2 && (blob_type == BLOB_TYPE_CRYPTONOTE_LOKI || blob_type == BLOB_TYPE_CRYPTONOTE_XTNC)) - { - FIELD(output_unlock_times) - if (version == loki_version_3_per_output_unlock_times) - FIELD(is_deregister) - } - if (blob_type != BLOB_TYPE_CRYPTONOTE_XHV || version < POU_TRANSACTION_VERSION) - VARINT_FIELD(unlock_time) - if (blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR) - FIELD(vin_zephyr) - else + if (blob_type == BLOB_TYPE_CRYPTONOTE_XHV) { + VARINT_FIELD(version) + //if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false; + + // Only transactions prior to HAVEN_TYPES_TRANSACTION_VERSION are permitted to be anything other than HAVEN_TYPES and need translation + if (version < HAVEN_TYPES_TRANSACTION_VERSION) { + + if (version < POU_TRANSACTION_VERSION) { + VARINT_FIELD(unlock_time) + } + if (!typename Archive::is_saving()) { + FIELD(vin) + FIELD(vout_xhv) + FIELD(extra) + if(version >= OFFSHORE_TRANSACTION_VERSION) { + VARINT_FIELD(pricing_record_height) + if (version < 5) + FIELD(offshore_data) + if (version >= POU_TRANSACTION_VERSION) { + FIELD(output_unlock_times) + if (vout_xhv.size() != output_unlock_times.size()) { + return false; + } + } + VARINT_FIELD(amount_burnt) + VARINT_FIELD(amount_minted) + if (version >= COLLATERAL_TRANSACTION_VERSION && amount_burnt) { + FIELD(collateral_indices) + if (collateral_indices.size() != 2) { + return false; + } + for (const auto vout_idx: collateral_indices) { + if (vout_idx >= vout_xhv.size()) + return false; + } + } + } + std::vector vin_tmp(vin); + bool is_conversion_tx = (amount_burnt != 0); + bool is_offshore_tx = is_conversion_tx; + bool is_onshore_tx = false; + vin.clear(); + for (auto &vin_entry: vin_tmp) { + if (vin_entry.type() == typeid(txin_gen)) { + vin.push_back(vin_entry); + continue; + } + txin_haven_key in; + if (vin_entry.type() == typeid(txin_to_key)) { + in.asset_type = "XHV"; + in.amount = boost::get(vin_entry).amount; + in.key_offsets = boost::get(vin_entry).key_offsets; + in.k_image = boost::get(vin_entry).k_image; + } else if (vin_entry.type() == typeid(txin_offshore)) { + is_offshore_tx = false; + is_onshore_tx = false; + in.asset_type = "XUSD"; + in.amount = boost::get(vin_entry).amount; + in.key_offsets = boost::get(vin_entry).key_offsets; + in.k_image = boost::get(vin_entry).k_image; + } else if (vin_entry.type() == typeid(txin_onshore)) { + is_offshore_tx = false; + is_onshore_tx = true; + in.asset_type = "XUSD"; + in.amount = boost::get(vin_entry).amount; + in.key_offsets = boost::get(vin_entry).key_offsets; + in.k_image = boost::get(vin_entry).k_image; + } else if (vin_entry.type() == typeid(txin_xasset)) { + is_offshore_tx = false; + is_onshore_tx = false; + in.amount = boost::get(vin_entry).amount; + in.key_offsets = boost::get(vin_entry).key_offsets; + in.k_image = boost::get(vin_entry).k_image; + in.asset_type = boost::get(vin_entry).asset_type; + } else { + return false; + } + vin.push_back(in); + } + std::vector vout_tmp(vout_xhv); + vout_xhv.clear(); + for (size_t i=0; i(vout_tmp[i].target).key; + } else if (vout_tmp[i].target.type() == typeid(txout_offshore)) { + out.asset_type = "XUSD"; + out.key = boost::get(vout_tmp[i].target).key; + } else if (vout_tmp[i].target.type() == typeid(txout_xasset)) { + out.asset_type = boost::get(vout_tmp[i].target).asset_type; + out.key = boost::get(vout_tmp[i].target).key; + } else { + return false; + } + out.unlock_time = (version >= POU_TRANSACTION_VERSION) ? output_unlock_times[i] : unlock_time; + out.is_collateral = false; + out.is_collateral_change = false; + if (version >= COLLATERAL_TRANSACTION_VERSION && amount_burnt) { + if (((is_onshore_tx) && + (collateral_indices[0] == i)) || + ((!is_onshore_tx) && + (is_offshore_tx) && + (collateral_indices[0] == i && collateral_indices[1] == 0))) { + out.is_collateral = true; + } + if (is_onshore_tx && collateral_indices[1] == i) { + out.is_collateral_change = true; + } + } + tx_out_xhv foo; + foo.amount = vout_tmp[i].amount; + foo.target = out; + vout_xhv.push_back(foo); + } + return true; + } + bool is_offshore_tx = (amount_burnt != 0); + bool is_onshore_tx = false; + std::vector vin_tmp; + vin_tmp.reserve(vin.size()); + for (auto &vin_entry_v: vin) { + if (vin_entry_v.type() == typeid(txin_gen)) { + vin_tmp.push_back(vin_entry_v); + continue; + } + txin_haven_key vin_entry = boost::get(vin_entry_v); + if (vin_entry.asset_type == "XHV") { + txin_to_key in; + in.amount = vin_entry.amount; + in.key_offsets = vin_entry.key_offsets; + in.k_image = vin_entry.k_image; + vin_tmp.push_back(in); + } else if (vin_entry.asset_type == "XUSD") { + is_offshore_tx = false; + int xhv_outputs = std::count_if(vout_xhv.begin(), vout_xhv.end(), [](tx_out_xhv &foo_v) { + if (foo_v.target.type() == typeid(txout_haven_key)) { + txout_haven_key out = boost::get(foo_v.target); + return out.asset_type == "XHV"; + } else if (foo_v.target.type() == typeid(txout_haven_tagged_key)) { + txout_haven_tagged_key out = boost::get(foo_v.target); + return out.asset_type == "XHV"; + } else { + return false; + } + }); + if (xhv_outputs) { + is_onshore_tx = true; + txin_onshore in; + in.amount = vin_entry.amount; + in.key_offsets = vin_entry.key_offsets; + in.k_image = vin_entry.k_image; + vin_tmp.push_back(in); + } else { + txin_offshore in; + in.amount = vin_entry.amount; + in.key_offsets = vin_entry.key_offsets; + in.k_image = vin_entry.k_image; + vin_tmp.push_back(in); + } + } else { + is_offshore_tx = false; + txin_xasset in; + in.amount = vin_entry.amount; + in.asset_type = vin_entry.asset_type; + in.key_offsets = vin_entry.key_offsets; + in.k_image = vin_entry.k_image; + vin_tmp.push_back(in); + } + } + std::vector vout_tmp; + vout_tmp.reserve(vout_xhv.size()); + output_unlock_times.resize(vout_xhv.size()); + std::vector collateral_indices_temp; + collateral_indices_temp.resize(2); + for (size_t i=0; i(vout_xhv[i].target); + tx_out_xhv foo; + foo.amount = vout_xhv[i].amount; + if (outhk.asset_type == "XHV") { + txout_to_key out; + out.key = outhk.key; + foo.target = out; + } else if (outhk.asset_type == "XUSD") { + txout_offshore out; + out.key = outhk.key; + foo.target = out; + } else { + txout_xasset out; + out.asset_type = outhk.asset_type; + out.key = outhk.key; + foo.target = out; + } + output_unlock_times[i] = outhk.unlock_time; + if (outhk.is_collateral) { + collateral_indices_temp[0] = i; + } else if (outhk.is_collateral_change) { + collateral_indices_temp[1] = i; + } + vout_tmp.push_back(foo); + } + FIELD_N("vin", vin_tmp) + FIELD_N("vout", vout_tmp) + FIELD(extra) + if(version >= OFFSHORE_TRANSACTION_VERSION) { + VARINT_FIELD(pricing_record_height) + if (version < 5) + FIELD(offshore_data) + if (version >= POU_TRANSACTION_VERSION) { + FIELD(output_unlock_times) + if (vout_xhv.size() != output_unlock_times.size()) { + return false; + } + } + VARINT_FIELD(amount_burnt) + VARINT_FIELD(amount_minted) + if (version >= COLLATERAL_TRANSACTION_VERSION && amount_burnt) { + if (collateral_indices.size() != 2) { + if ((is_offshore_tx || is_onshore_tx) && collateral_indices_temp.size() != 2) { + return false; + } + if (is_offshore_tx || is_onshore_tx) { + collateral_indices = collateral_indices_temp; + } else { + collateral_indices.clear(); + collateral_indices.push_back(0); + collateral_indices.push_back(0); + } + } + FIELD(collateral_indices) + for (const auto vout_idx: collateral_indices) { + if (vout_idx >= vout_xhv.size()) + return false; + } + } + } + return true; + } + FIELD(vin) - - if (blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR) - FIELD(vout_zephyr) - else if (blob_type == BLOB_TYPE_CRYPTONOTE_XHV) FIELD(vout_xhv) - else - FIELD(vout) - - if (blob_type == BLOB_TYPE_CRYPTONOTE_LOKI || blob_type == BLOB_TYPE_CRYPTONOTE_XTNC) - { - if (version >= loki_version_3_per_output_unlock_times && vout.size() != output_unlock_times.size()) return false; - } - FIELD(extra) - if ((blob_type == BLOB_TYPE_CRYPTONOTE_LOKI || blob_type == BLOB_TYPE_CRYPTONOTE_XTNC) && version >= loki_version_4_tx_types) - { - VARINT_FIELD(type) - if (static_cast(type) >= loki_type_count) return false; - } - if (blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR) { + FIELD(extra) VARINT_FIELD(pricing_record_height) VARINT_FIELD(amount_burnt) VARINT_FIELD(amount_minted) - } - if (blob_type == BLOB_TYPE_CRYPTONOTE_XHV && version >= OFFSHORE_TRANSACTION_VERSION) { - VARINT_FIELD(pricing_record_height) - if (version < 5) - FIELD(offshore_data) - if (version >= POU_TRANSACTION_VERSION) + + } else { + + VARINT_FIELD(version) + if (version > loki_version_2 && (blob_type == BLOB_TYPE_CRYPTONOTE_LOKI || blob_type == BLOB_TYPE_CRYPTONOTE_XTNC)) { FIELD(output_unlock_times) + if (version == loki_version_3_per_output_unlock_times) + FIELD(is_deregister) } - if (version >= POU_TRANSACTION_VERSION && vout_xhv.size() != output_unlock_times.size()) return false; - VARINT_FIELD(amount_burnt) - VARINT_FIELD(amount_minted) - if (version >= COLLATERAL_TRANSACTION_VERSION && amount_burnt) { - FIELD(collateral_indices) - if (collateral_indices.size() != 2) { - return false; - } - for (const auto vout_idx: collateral_indices) { - if (vout_idx >= vout.size()) - return false; - } + + VARINT_FIELD(unlock_time) + + if (blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR) + FIELD(vin_zephyr) + else + FIELD(vin) + + if (blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR) + FIELD(vout_zephyr) + else + FIELD(vout) + + if (blob_type == BLOB_TYPE_CRYPTONOTE_LOKI || blob_type == BLOB_TYPE_CRYPTONOTE_XTNC) + { + if (version >= loki_version_3_per_output_unlock_times && vout.size() != output_unlock_times.size()) return false; + } + FIELD(extra) + if ((blob_type == BLOB_TYPE_CRYPTONOTE_LOKI || blob_type == BLOB_TYPE_CRYPTONOTE_XTNC) && version >= loki_version_4_tx_types) + { + VARINT_FIELD(type) + if (static_cast(type) >= loki_type_count) return false; + } + if (blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR) { + VARINT_FIELD(pricing_record_height) + VARINT_FIELD(amount_burnt) + VARINT_FIELD(amount_minted) } } END_SERIALIZE() @@ -463,7 +746,10 @@ namespace cryptonote vin.size() > 0 && vin[0].type() == typeid(txin_to_key) ? boost::get(vin[0]).key_offsets.size() - 1 : vin.size() > 0 && vin[0].type() == typeid(txin_offshore) ? boost::get(vin[0]).key_offsets.size() - 1 : vin.size() > 0 && vin[0].type() == typeid(txin_onshore) ? boost::get(vin[0]).key_offsets.size() - 1 : - vin.size() > 0 && vin[0].type() == typeid(txin_xasset) ? boost::get(vin[0]).key_offsets.size() - 1 : 0); + vin.size() > 0 && vin[0].type() == typeid(txin_xasset) ? boost::get(vin[0]).key_offsets.size() - 1 : + vin.size() > 0 && vin[0].type() == typeid(txin_haven_key) ? boost::get(vin[0]).key_offsets.size() - 1 : + 0 + ); } else { r = rct_signatures.p.serialize_rctsig_prunable(ar, rct_signatures.type, vin.size(), vout.size(), vin[0].type() == typeid(txin_to_key) ? boost::get(vin[0]).key_offsets.size() - 1 : 0); @@ -523,6 +809,7 @@ namespace cryptonote size_t operator()(const txin_offshore& txin) const {return txin.key_offsets.size();} size_t operator()(const txin_onshore& txin) const {return txin.key_offsets.size();} size_t operator()(const txin_xasset& txin) const {return txin.key_offsets.size();} + size_t operator()(const txin_haven_key& txin) const {return txin.key_offsets.size();} size_t operator()(const txin_zephyr_key& txin) const {return txin.key_offsets.size();} }; @@ -755,6 +1042,7 @@ VARIANT_TAG(binary_archive, cryptonote::txin_zephyr_key, 0x2); VARIANT_TAG(binary_archive, cryptonote::txin_offshore, 0x3); VARIANT_TAG(binary_archive, cryptonote::txin_onshore, 0x4); VARIANT_TAG(binary_archive, cryptonote::txin_xasset, 0x5); +VARIANT_TAG(binary_archive, cryptonote::txin_haven_key, 0x6); 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); @@ -762,5 +1050,7 @@ VARIANT_TAG(binary_archive, cryptonote::txout_zephyr_tagged_key, 0x2); VARIANT_TAG(binary_archive, cryptonote::txout_to_tagged_key, 0x3); VARIANT_TAG(binary_archive, cryptonote::txout_offshore, 0x3); VARIANT_TAG(binary_archive, cryptonote::txout_xasset, 0x5); +VARIANT_TAG(binary_archive, cryptonote::txout_haven_key, 0x6); +VARIANT_TAG(binary_archive, cryptonote::txout_haven_tagged_key, 0x7); VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc); VARIANT_TAG(binary_archive, cryptonote::block, 0xbb); diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 4e5497d..14c8932 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -247,6 +247,7 @@ namespace cryptonote t.vin[0].type() == typeid(txin_offshore) ? boost::get(t.vin[0]).key_offsets.size() - 1 : t.vin[0].type() == typeid(txin_onshore) ? boost::get(t.vin[0]).key_offsets.size() - 1 : t.vin[0].type() == typeid(txin_xasset) ? boost::get(t.vin[0]).key_offsets.size() - 1 : + t.vin[0].type() == typeid(txin_haven_key) ? boost::get(t.vin[0]).key_offsets.size() - 1 : 0; } else { mixin = t.vin.empty() ? 0 : t.vin[0].type() == typeid(txin_to_key) ? boost::get(t.vin[0]).key_offsets.size() - 1 : 0; @@ -370,7 +371,7 @@ namespace cryptonote ss << b_blob; binary_archive ba(ss); bool r = ::serialization::serialize(ba, b); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob"); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob 1"); return true; } //--------------------------------------------------------------- diff --git a/src/main.cc b/src/main.cc index d8dd9ca..cc17650 100644 --- a/src/main.cc +++ b/src/main.cc @@ -145,7 +145,7 @@ NAN_METHOD(convert_blob) { // (parentBlockBuffer, cnBlobType) block b = AUTO_VAL_INIT(b); b.set_blob_type(blob_type); - if (!parse_and_validate_block_from_blob(input, b)) return THROW_ERROR_EXCEPTION("Failed to parse block"); + if (!parse_and_validate_block_from_blob(input, b)) return THROW_ERROR_EXCEPTION("Failed to parse block 2"); if (blob_type == BLOB_TYPE_FORKNOTE2) { block parent_block; diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index bdde60d..2ce91e5 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -236,11 +236,48 @@ namespace rct { END_SERIALIZE() }; + struct BulletproofPlus + { + rct::keyV V; + rct::key A, A1, B; + rct::key r1, s1, d1; + rct::keyV L, R; + + BulletproofPlus() {} + BulletproofPlus(const rct::key &V, const rct::key &A, const rct::key &A1, const rct::key &B, const rct::key &r1, const rct::key &s1, const rct::key &d1, const rct::keyV &L, const rct::keyV &R): + V({V}), A(A), A1(A1), B(B), r1(r1), s1(s1), d1(d1), L(L), R(R) {} + BulletproofPlus(const rct::keyV &V, const rct::key &A, const rct::key &A1, const rct::key &B, const rct::key &r1, const rct::key &s1, const rct::key &d1, const rct::keyV &L, const rct::keyV &R): + V(V), A(A), A1(A1), B(B), r1(r1), s1(s1), d1(d1), L(L), R(R) {} + + bool operator==(const BulletproofPlus &other) const { return V == other.V && A == other.A && A1 == other.A1 && B == other.B && r1 == other.r1 && s1 == other.s1 && d1 == other.d1 && L == other.L && R == other.R; } + + BEGIN_SERIALIZE_OBJECT() + // Commitments aren't saved, they're restored via outPk + // FIELD(V) + FIELD(A) + FIELD(A1) + FIELD(B) + FIELD(r1) + FIELD(s1) + FIELD(d1) + FIELD(L) + FIELD(R) + + if (L.empty() || L.size() != R.size()) + return false; + END_SERIALIZE() + }; + size_t n_bulletproof_amounts(const Bulletproof &proof); size_t n_bulletproof_max_amounts(const Bulletproof &proof); size_t n_bulletproof_amounts(const std::vector &proofs); size_t n_bulletproof_max_amounts(const std::vector &proofs); + size_t n_bulletproof_plus_amounts(const BulletproofPlus &proof); + size_t n_bulletproof_plus_max_amounts(const BulletproofPlus &proof); + size_t n_bulletproof_plus_amounts(const std::vector &proofs); + size_t n_bulletproof_plus_max_amounts(const std::vector &proofs); + //A container to hold all signatures necessary for RingCT // rangeSigs holds all the rangeproof data of a transaction // MG holds the MLSAG signature of a transaction @@ -258,11 +295,18 @@ namespace rct { RCTTypeCLSAGN = 6, RCTTypeHaven2 = 7, // Add public mask sum terms, remove extraneous fields (txnFee_usd,txnFee_xasset,txnOffshoreFee_usd,txnOffshoreFee_xasset) RCTTypeHaven3 = 8, // Add public mask sum term for collateral + RCTTypeBulletproofPlus = 9, }; enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof }; struct RCTConfig { RangeProofType range_proof_type; int bp_version; + + BEGIN_SERIALIZE_OBJECT() + VERSION_FIELD(0) + VARINT_FIELD(range_proof_type) + VARINT_FIELD(bp_version) + END_SERIALIZE() }; struct rctSigBase { uint8_t type; @@ -288,6 +332,64 @@ namespace rct { FIELD(type) if (type == RCTTypeNull) return ar.stream().good(); + if (type != RCTTypeBulletproofPlus) + return serialize_rctsig_base_old(ar, inputs, outputs); + VARINT_FIELD(txnFee) + VARINT_FIELD(txnOffshoreFee) + // inputs/outputs not saved, only here for serialization help + // FIELD(message) - not serialized, it can be reconstructed + // FIELD(mixRing) - not serialized, it can be reconstructed + ar.tag("ecdhInfo"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, ecdhInfo); + if (ecdhInfo.size() != outputs) + return false; + for (size_t i = 0; i < outputs; ++i) + { + ar.begin_object(); + if (!typename Archive::is_saving()) + memset(ecdhInfo[i].amount.bytes, 0, sizeof(ecdhInfo[i].amount.bytes)); + crypto::hash8 &amount = (crypto::hash8&)ecdhInfo[i].amount; + FIELD(amount); + ar.end_object(); + if (outputs - i > 1) + ar.delimit_array(); + } + ar.end_array(); + + ar.tag("outPk"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, outPk); + if (outPk.size() != outputs) + return false; + for (size_t i = 0; i < outputs; ++i) + { + FIELDS(outPk[i].mask) + if (outputs - i > 1) + ar.delimit_array(); + } + ar.end_array(); + + // if txnOffshoreFee is not 0, it is a conversion tx + if (txnOffshoreFee) { + ar.tag("maskSums"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(3, maskSums); + if (maskSums.size() != 3) + return false; + FIELDS(maskSums[0]) + ar.delimit_array(); + FIELDS(maskSums[1]) + ar.delimit_array(); + FIELDS(maskSums[2]) + ar.end_array(); + } + return ar.stream().good(); + } + + template class Archive> + bool serialize_rctsig_base_old(Archive &ar, size_t inputs, size_t outputs) + { if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeCLSAGN && type != RCTTypeHaven2 && type != RCTTypeHaven3) return false; VARINT_FIELD(txnFee) @@ -432,10 +534,23 @@ namespace rct { } return ar.stream().good(); } + + BEGIN_SERIALIZE_OBJECT() + FIELD(type) + FIELD(message) + FIELD(mixRing) + FIELD(pseudoOuts) + FIELD(ecdhInfo) + FIELD(outPk) + VARINT_FIELD(txnFee) + VARINT_FIELD(txnOffshoreFee) + FIELD(maskSums) + END_SERIALIZE() }; struct rctSigPrunable { std::vector rangeSigs; std::vector bulletproofs; + std::vector bulletproofs_plus; std::vector MGs; // simple rct has N, full has 1 std::vector CLSAGs; keyV pseudoOuts; //C - for simple rct @@ -444,11 +559,36 @@ namespace rct { template class Archive> bool serialize_rctsig_prunable(Archive &ar, uint8_t type, size_t inputs, size_t outputs, size_t mixin) { + if (inputs >= 0xffffffff) + return false; + if (outputs >= 0xffffffff) + return false; + if (mixin >= 0xffffffff) + return false; if (type == RCTTypeNull) return ar.stream().good(); - if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeCLSAGN && type != RCTTypeHaven2 && type != RCTTypeHaven3) + if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeCLSAGN && type != RCTTypeHaven2 && type != RCTTypeHaven3 && type != RCTTypeBulletproofPlus) return false; - if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3) + if (type == RCTTypeBulletproofPlus) + { + uint32_t nbp = bulletproofs_plus.size(); + VARINT_FIELD(nbp) + ar.tag("bpp"); + ar.begin_array(); + if (nbp > outputs) + return false; + PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs_plus); + for (size_t i = 0; i < nbp; ++i) + { + FIELDS(bulletproofs_plus[i]) + if (nbp - i > 1) + ar.delimit_array(); + } + if (n_bulletproof_plus_max_amounts(bulletproofs_plus) < outputs) + return false; + ar.end_array(); + } + else if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3) { uint32_t nbp = bulletproofs.size(); if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3) @@ -486,7 +626,7 @@ namespace rct { ar.end_array(); } - if ((type == RCTTypeCLSAG) || (type == RCTTypeCLSAGN) || (type == RCTTypeHaven2) || (type == RCTTypeHaven3)) + if (type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3 || type == RCTTypeBulletproofPlus) { ar.tag("CLSAGs"); ar.begin_array(); @@ -577,7 +717,7 @@ namespace rct { } ar.end_array(); } - if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3) + if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3 || type == RCTTypeBulletproofPlus) { ar.tag("pseudoOuts"); ar.begin_array(); @@ -595,19 +735,32 @@ namespace rct { return ar.stream().good(); } + BEGIN_SERIALIZE_OBJECT() + FIELD(rangeSigs) + FIELD(bulletproofs) + FIELD(bulletproofs_plus) + FIELD(MGs) + FIELD(CLSAGs) + FIELD(pseudoOuts) + END_SERIALIZE() }; struct rctSig: public rctSigBase { rctSigPrunable p; keyV& get_pseudo_outs() { - return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3 ? p.pseudoOuts : pseudoOuts; + return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3 || type == RCTTypeBulletproofPlus ? p.pseudoOuts : pseudoOuts; } keyV const& get_pseudo_outs() const { - return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3 ? p.pseudoOuts : pseudoOuts; + return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3 || type == RCTTypeBulletproofPlus ? p.pseudoOuts : pseudoOuts; } + + BEGIN_SERIALIZE_OBJECT() + FIELDS((rctSigBase&)*this) + FIELD(p) + END_SERIALIZE() }; //other basepoint H = toPoint(cn_fast_hash(G)), G the basepoint @@ -713,7 +866,9 @@ namespace rct { bool is_rct_simple(int type); bool is_rct_bulletproof(int type); + bool is_rct_bulletproof_plus(int type); bool is_rct_borromean(int type); + bool is_rct_clsag(int type); static inline const rct::key &pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; } static inline const rct::key &sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; } @@ -769,5 +924,6 @@ VARIANT_TAG(binary_archive, rct::Bulletproof, 0x9c); VARIANT_TAG(binary_archive, rct::multisig_kLRki, 0x9d); VARIANT_TAG(binary_archive, rct::multisig_out, 0x9e); VARIANT_TAG(binary_archive, rct::clsag, 0x9f); +VARIANT_TAG(binary_archive, rct::BulletproofPlus, 0xa0); #endif /* RCTTYPES_H */ diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index 6802cdc..8b94308 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -109,6 +109,13 @@ inline bool do_serialize(Archive &ar, bool &v) ar.serialize_varint(f); \ if (!ar.stream().good()) return false; \ } while(0); +#define VERSION_FIELD(v) \ + uint32_t version = v; \ + do { \ + ar.tag("version"); \ + ar.serialize_varint(version); \ + if (!ar.stream().good()) return false; \ + } while(0); namespace serialization { namespace detail