From f5ccc22d2c7195291ac45f722bd4cfec4a5dff2d Mon Sep 17 00:00:00 2001 From: Neil Coggins Date: Thu, 14 Oct 2021 09:08:53 +0100 Subject: [PATCH] updated to support Haven 2.0 (untested) --- src/cryptonote_config.h | 1 + src/cryptonote_core/cryptonote_basic.h | 3 +- .../cryptonote_format_utils.cpp | 40 +++- src/offshore/pricing_record.cpp | 155 ++++++++---- src/offshore/pricing_record.h | 129 +++++++--- src/ringct/rctTypes.h | 225 ++++++++++-------- 6 files changed, 364 insertions(+), 189 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index e66ee80..3158f60 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -3,6 +3,7 @@ #define CURRENT_TRANSACTION_VERSION 1 #define OFFSHORE_TRANSACTION_VERSION 3 #define HF_VERSION_XASSET_FEES_V2 17 +#define HF_VERSION_HAVEN2 18 enum BLOB_TYPE { BLOB_TYPE_CRYPTONOTE = 0, diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index e8a6b41..b2134a9 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -273,7 +273,8 @@ namespace cryptonote } if (blob_type == BLOB_TYPE_CRYPTONOTE_XHV && version >= OFFSHORE_TRANSACTION_VERSION) { VARINT_FIELD(pricing_record_height) - FIELD(offshore_data) + if (version < 5) + FIELD(offshore_data) VARINT_FIELD(amount_burnt) VARINT_FIELD(amount_minted) } diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index ed19b39..4244b03 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -85,25 +85,39 @@ namespace cryptonote { uint64_t amount_in = 0; uint64_t amount_out = 0; - if ((tx.blob_type == BLOB_TYPE_CRYPTONOTE_XHV) && (tx.version > 1)) + if (tx.blob_type == BLOB_TYPE_CRYPTONOTE_XHV) { // This is the correct way to get the fee for Haven, because outs may be in different currencies to ins - fee = tx.rct_signatures.txnFee + tx.rct_signatures.txnOffshoreFee; + switch (tx.version) { + case 5: + fee = tx.rct_signatures.txnFee + tx.rct_signatures.txnOffshoreFee; + break; + case 4: + case 3: + if (tx.vin[0].type() == typeid(txin_to_key)) { + fee = tx.rct_signatures.txnFee + tx.rct_signatures.txnOffshoreFee; + } else if (tx.vin[0].type() == typeid(txin_offshore)) { + fee = tx.rct_signatures.txnFee_usd + tx.rct_signatures.txnOffshoreFee_usd; + } else if (tx.vin[0].type() == typeid(txin_onshore)) { + fee = tx.rct_signatures.txnFee_usd + tx.rct_signatures.txnOffshoreFee_usd; + } else if (tx.vin[0].type() == typeid(txin_xasset)) { + fee = tx.rct_signatures.txnFee_xasset + tx.rct_signatures.txnOffshoreFee_xasset; + } else { + CHECK_AND_ASSERT_MES(false, false, "unexpected type id in transaction"); + return false; + } + break; + case 2: + case 1: + fee = tx.rct_signatures.txnFee; + break; + } return true; } BOOST_FOREACH(auto& in, tx.vin) { - if (tx.blob_type != BLOB_TYPE_CRYPTONOTE_XHV) { - CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), 0, "unexpected type id in transaction"); - amount_in += boost::get(in).amount; - } else { - CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key) || in.type() == typeid(txin_offshore) || in.type() == typeid(txin_onshore) || in.type() == typeid(txin_xasset), 0, "unexpected type id in transaction"); - amount_in += - in.type() == typeid(txin_to_key) ? boost::get(in).amount : - in.type() == typeid(txin_onshore) ? boost::get(in).amount : - in.type() == typeid(txin_offshore) ? boost::get(in).amount : - boost::get(in).amount; - } + CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), 0, "unexpected type id in transaction"); + amount_in += boost::get(in).amount; } BOOST_FOREACH(auto& o, tx.vout) amount_out += o.amount; diff --git a/src/offshore/pricing_record.cpp b/src/offshore/pricing_record.cpp index 6cf1606..678b166 100644 --- a/src/offshore/pricing_record.cpp +++ b/src/offshore/pricing_record.cpp @@ -32,6 +32,11 @@ #include "serialization/keyvalue_serialization.h" #include "storages/portable_storage.h" +#include "string_tools.h" + +#define PRICING_RECORD_VALID_BLOCKS 10 +#define PRICING_RECORD_VALID_TIME_DIFF_FROM_BLOCK 120 // seconds + namespace offshore { @@ -127,8 +132,8 @@ namespace offshore unused3 = in.unused3; timestamp = in.timestamp; for (unsigned int i = 0; i < in.signature.length(); i += 2) { - std::string byteString = in.signature.substr(i, 2); - signature[i>>1] = (char) strtol(byteString.c_str(), NULL, 16); + std::string byteString = in.signature.substr(i, 2); + signature[i>>1] = (char) strtol(byteString.c_str(), NULL, 16); } return true; } @@ -194,7 +199,7 @@ namespace offshore return *this; } - uint64_t pricing_record::operator[](const std::string asset_type) const + uint64_t pricing_record::operator[](const std::string& asset_type) const { if (asset_type == "XHV") { return 1000000000000; @@ -251,17 +256,44 @@ namespace offshore !::memcmp(signature, other.signature, sizeof(signature))); } - bool pricing_record::is_empty() const noexcept + bool pricing_record::empty() const noexcept { const pricing_record empty_pr = offshore::pricing_record(); return (*this).equal(empty_pr); } - bool pricing_record::verifySignature(EVP_PKEY* public_key) const noexcept + bool pricing_record::verifySignature() const { - // Sanity check - accept empty pricing records - if ((*this).is_empty()) - return true; + // Oracle public keys + std::string const mainnet_public_key = "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5YBxWx1AZCA9jTUk8Pr2uZ9jpfRt\n" + "KWv3Vo1/Gny+1vfaxsXhBQiG1KlHkafNGarzoL0WHW4ocqaaqF5iv8i35A==\n" + "-----END PUBLIC KEY-----\n"; + std::string const testnet_public_key = "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtWqvQh7OdXrdgXcDeBMRVfLWTW3F\n" + "wByeoVJFBfZymScJIJl46j66xG6ngnyj4ai4/QPFnSZ1I9jjMRlTWC4EPA==\n" + "-----END PUBLIC KEY-----\n"; + std::string const stagenet_public_key = "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtWqvQh7OdXrdgXcDeBMRVfLWTW3F\n" + "wByeoVJFBfZymScJIJl46j66xG6ngnyj4ai4/QPFnSZ1I9jjMRlTWC4EPA==\n" + "-----END PUBLIC KEY-----\n"; + + // Comment out all but 1 of the following lines to select the correct Oracle PK + std::string const public_key = mainnet_public_key; + //std::string const public_key = testnet_public_key; + //std::string const public_key = stagenet_public_key; + + CHECK_AND_ASSERT_THROW_MES(!public_key.empty(), "Pricing record verification failed. NULL public key. PK Size: " << public_key.size()); // TODO: is this necessary or the one below already covers this case, meannin it will produce empty pubkey? + + // extract the key + EVP_PKEY* pubkey; + BIO* bio = BIO_new_mem_buf(public_key.c_str(), public_key.size()); + if (!bio) { + return false; + } + pubkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); + BIO_free(bio); + CHECK_AND_ASSERT_THROW_MES(pubkey != NULL, "Pricing record verification failed. NULL public key."); // Convert our internal 64-byte binary representation into 128-byte hex string std::string sig_hex; @@ -330,50 +362,23 @@ namespace offshore compact += (byte); } - // Check to see if we have been passed a public key to use - EVP_PKEY* pubkey = NULL; - if (public_key) { - - // Take a copy for local use - pubkey = public_key; - - } else { - - // No public key provided - failover to embedded key - static const char public_key[] = "-----BEGIN PUBLIC KEY-----\n" - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5YBxWx1AZCA9jTUk8Pr2uZ9jpfRt\n" - "KWv3Vo1/Gny+1vfaxsXhBQiG1KlHkafNGarzoL0WHW4ocqaaqF5iv8i35A==\n" - "-----END PUBLIC KEY-----\n"; - - BIO* bio = BIO_new_mem_buf(public_key, (int)sizeof(public_key)); - if (!bio) { - return false; - } - pubkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); - BIO_free(bio); - } - assert(pubkey != NULL); - // Create a verify digest from the message EVP_MD_CTX *ctx = EVP_MD_CTX_create(); int ret = 0; if (ctx) { - ret=EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, pubkey); + ret = EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, pubkey); if (ret == 1) { - ret=EVP_DigestVerifyUpdate(ctx, message.data(), message.length()); - if (ret == 1) { - ret=EVP_DigestVerifyFinal(ctx, (const unsigned char *)compact.data(), compact.length()); - } + ret = EVP_DigestVerifyUpdate(ctx, message.data(), message.length()); + if (ret == 1) { + ret = EVP_DigestVerifyFinal(ctx, (const unsigned char *)compact.data(), compact.length()); + } } } + // Cleanup the context we created EVP_MD_CTX_destroy(ctx); - - // Was the key provided by the caller? - if (pubkey != public_key) { - // Cleanup the openssl stuff - EVP_PKEY_free(pubkey); - } + // Cleanup the openssl stuff + EVP_PKEY_free(pubkey); if (ret == 1) return true; @@ -383,4 +388,68 @@ namespace offshore return false; } + + void pricing_record::set_for_height_821428() { + const std::string pr_821428 = "9b3f6f2f8f0000003d620e1202000000be71be2555120000b8627010000000000000000000000000ea0885b2270d00000000000000000000f797ff9be00b0000ddbdb005270a0000fc90cfe02b01060000000000000000000000000000000000d0a28224000e000000d643be960e0000002e8bb6a40e000000f8a817f80d00002f5d27d45cdbfbac3d0f6577103f68de30895967d7562fbd56c161ae90130f54301b1ea9d5fd062f37dac75c3d47178bc6f149d21da1ff0e8430065cb762b93a"; + this->xAG = 614976143259; + this->xAU = 8892867133; + this->xAUD = 20156914758078; + this->xBTC = 275800760; + this->xCAD = 0; + this->xCHF = 14464149948650; + this->xCNY = 0; + this->xEUR = 13059317798903; + this->xGBP = 11162715471325; + this->xJPY = 1690137827184892; + this->xNOK = 0; + this->xNZD = 0; + this->xUSD = 15393775330000; + this->unused1 = 16040600000000; + this->unused2 = 16100600000000; + this->unused3 = 15359200000000; + this->timestamp = 0; + std::string sig = "2f5d27d45cdbfbac3d0f6577103f68de30895967d7562fbd56c161ae90130f54301b1ea9d5fd062f37dac75c3d47178bc6f149d21da1ff0e8430065cb762b93a"; + int j=0; + for (unsigned int i = 0; i < sig.size(); i += 2) { + std::string byteString = sig.substr(i, 2); + this->signature[j++] = (char) strtol(byteString.c_str(), NULL, 16); + } + } + + // overload for pr validation for block + bool pricing_record::valid(uint32_t hf_version, uint64_t bl_timestamp, uint64_t last_bl_timestamp) const + { + // check for empty pr + if (hf_version >= HF_VERSION_XASSET_FEES_V2) { + if (this->empty()) + return true; + } else { + unsigned char test_sig[64]; + std::memset(test_sig, 0, sizeof(test_sig)); + if (std::memcmp(test_sig, this->signature, sizeof(this->signature)) == 0) { + return true; + } + } + + // verify the signature + if (!verifySignature()) { + LOG_ERROR("Invalid pricing record signature."); + return false; + } + + // valiadte the timestmap + if (hf_version >= HF_VERSION_XASSET_FEES_V2) { + if (this->timestamp > bl_timestamp + PRICING_RECORD_VALID_TIME_DIFF_FROM_BLOCK) { + LOG_ERROR("Pricing record timestamp is too far in the future."); + return false; + } + if (this->timestamp <= last_bl_timestamp) { + LOG_ERROR("Pricing record timestamp is too old."); + return false; + } + } + + return true; + } + } diff --git a/src/offshore/pricing_record.h b/src/offshore/pricing_record.h index 06a3b18..93f3889 100644 --- a/src/offshore/pricing_record.h +++ b/src/offshore/pricing_record.h @@ -42,6 +42,8 @@ #include #include #include +#include "cryptonote_config.h" +#include "crypto/hash.h" namespace epee { @@ -80,9 +82,61 @@ namespace offshore class pricing_record { - public: + public: - // Fields + // Fields + uint64_t xAG; + uint64_t xAU; + uint64_t xAUD; + uint64_t xBTC; + uint64_t xCAD; + uint64_t xCHF; + uint64_t xCNY; + uint64_t xEUR; + uint64_t xGBP; + uint64_t xJPY; + uint64_t xNOK; + uint64_t xNZD; + uint64_t xUSD; + uint64_t unused1; + uint64_t unused2; + uint64_t unused3; + uint64_t timestamp; + unsigned char signature[64]; + + // Default c'tor + pricing_record() noexcept; + //! Load from epee p2p format + bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent); + //! Store in epee p2p format + bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const; + pricing_record(const pricing_record& orig) noexcept; + ~pricing_record() = default; + void set_for_height_821428(); + bool equal(const pricing_record& other) const noexcept; + bool empty() const noexcept; + bool verifySignature() const; + bool valid(uint32_t hf_version, uint64_t bl_timestamp, uint64_t last_bl_timestamp) const; + + pricing_record& operator=(const pricing_record& orig) noexcept; + uint64_t operator[](const std::string& asset_type) const; + }; + + inline bool operator==(const pricing_record& a, const pricing_record& b) noexcept + { + return a.equal(b); + } + + inline bool operator!=(const pricing_record& a, const pricing_record& b) noexcept + { + return !a.equal(b); + } + + // did not have a timestamp + class pricing_record_v1 + { + + public: uint64_t xAG; uint64_t xAU; uint64_t xAUD; @@ -102,37 +156,52 @@ namespace offshore uint64_t timestamp; unsigned char signature[64]; - // Default c'tor - pricing_record() noexcept; - - //! Load from epee p2p format - bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent); - - //! Store in epee p2p format - bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const; - pricing_record(const pricing_record& orig) noexcept; - ~pricing_record() = default; - pricing_record& operator=(const pricing_record& orig) noexcept; + bool write_to_pr(offshore::pricing_record &pr) + { + pr.xAG = xAG; + pr.xAU = xAU; + pr.xAUD = xAUD; + pr.xBTC = xBTC; + pr.xCAD = xCAD; + pr.xCHF = xCHF; + pr.xCNY = xCNY; + pr.xEUR = xEUR; + pr.xGBP = xGBP; + pr.xJPY = xJPY; + pr.xNOK = xNOK; + pr.xNZD = xNZD; + pr.xUSD = xUSD; + pr.unused1 = unused1; + pr.unused2 = unused2; + pr.unused3 = unused3; + pr.timestamp = 0; + ::memcpy(pr.signature, signature, sizeof(pr.signature)); + return true; + }; - uint64_t operator[](const std::string asset_type) const; - - bool equal(const pricing_record& other) const noexcept; - - bool is_empty() const noexcept; - - bool verifySignature(EVP_PKEY* public_key = NULL) const noexcept; + bool read_from_pr(offshore::pricing_record &pr) + { + xAG = pr.xAG; + xAU = pr.xAU; + xAUD = pr.xAUD; + xBTC = pr.xBTC; + xCAD = pr.xCAD; + xCHF = pr.xCHF; + xCNY = pr.xCNY; + xEUR = pr.xEUR; + xGBP = pr.xGBP; + xJPY = pr.xJPY; + xNOK = pr.xNOK; + xNZD = pr.xNZD; + xUSD = pr.xUSD; + unused1 = pr.unused1; + unused2 = pr.unused2; + unused3 = pr.unused3; + ::memcpy(signature, pr.signature, sizeof(signature)); + return true; + }; }; - inline bool operator==(const pricing_record& a, const pricing_record& b) noexcept - { - return a.equal(b); - } - - inline bool operator!=(const pricing_record& a, const pricing_record& b) noexcept - { - return !a.equal(b); - } - // did not have a timestamp class pricing_record_v1 { diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 0909b0e..7d60d70 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -48,6 +48,7 @@ extern "C" { #include "hex.h" #include "span.h" +#include "memwipe.h" #include "serialization/vector.h" #include "serialization/debug_archive.h" #include "serialization/binary_archive.h" @@ -106,6 +107,8 @@ namespace rct { key L; key R; key ki; + + ~multisig_kLRki() { memwipe(&k, sizeof(k)); } }; struct multisig_out { @@ -255,6 +258,7 @@ namespace rct { RCTTypeBulletproof2 = 4, RCTTypeCLSAG = 5, RCTTypeCLSAGN = 6, + RCTTypeHaven2 = 7, // Add public mask sum terms, remove extraneous fields (txnFee_usd,txnFee_xasset,txnOffshoreFee_usd,txnOffshoreFee_xasset) }; enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof }; struct RCTConfig { @@ -262,108 +266,125 @@ namespace rct { int bp_version; }; struct rctSigBase { - uint8_t type; - key message; - ctkeyM mixRing; //the set of all pubkeys / copy - //pairs that you mix with - keyV pseudoOuts; //C - for simple rct - std::vector ecdhInfo; - ctkeyV outPk; - ctkeyV outPk_usd; - ctkeyV outPk_xasset; - xmr_amount txnFee; // contains b - xmr_amount txnFee_usd; - xmr_amount txnFee_xasset; - xmr_amount txnOffshoreFee; - xmr_amount txnOffshoreFee_usd; - xmr_amount txnOffshoreFee_xasset; + uint8_t type; + key message; + ctkeyM mixRing; //the set of all pubkeys / copy + //pairs that you mix with + keyV pseudoOuts; //C - for simple rct + std::vector ecdhInfo; + ctkeyV outPk; + ctkeyV outPk_usd; + ctkeyV outPk_xasset; + xmr_amount txnFee = 0; // contains b + xmr_amount txnFee_usd = 0; + xmr_amount txnFee_xasset = 0; + xmr_amount txnOffshoreFee = 0; + xmr_amount txnOffshoreFee_usd = 0; + xmr_amount txnOffshoreFee_xasset = 0; + keyV maskSums; // contains 2 elements. 1. is the sum of masks of inputs. 2. is the sum of masks of changes. - template class Archive> - bool serialize_rctsig_base(Archive &ar, size_t inputs, size_t outputs) + template class Archive> + bool serialize_rctsig_base(Archive &ar, size_t inputs, size_t outputs) + { + FIELD(type) + if (type == RCTTypeNull) + return ar.stream().good(); + if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeCLSAGN && type != RCTTypeHaven2) + return false; + VARINT_FIELD(txnFee) + if (type == RCTTypeHaven2) { + // serialize offshore fee + VARINT_FIELD(txnOffshoreFee) + } else if (type == RCTTypeCLSAG || type == RCTTypeCLSAGN) { + VARINT_FIELD(txnFee_usd) + if (type == RCTTypeCLSAGN) + { + VARINT_FIELD(txnFee_xasset) + } + VARINT_FIELD(txnOffshoreFee) + VARINT_FIELD(txnOffshoreFee_usd) + if (type == RCTTypeCLSAGN) + { + VARINT_FIELD(txnOffshoreFee_xasset) + } + } else { + txnFee_usd = 0; + txnFee_xasset = 0; + txnOffshoreFee = 0; + txnOffshoreFee_usd = 0; + txnOffshoreFee_xasset = 0; + } + // 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 + if (type == RCTTypeSimple) // moved to prunable with bulletproofs { - FIELD(type) - if (type == RCTTypeNull) - return ar.stream().good(); - if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeCLSAGN) - return false; - VARINT_FIELD(txnFee) - if ((type == RCTTypeCLSAG) || (type == RCTTypeCLSAGN)) - { - VARINT_FIELD(txnFee_usd) - if (type == RCTTypeCLSAGN) - { - VARINT_FIELD(txnFee_xasset) - } - VARINT_FIELD(txnOffshoreFee) - VARINT_FIELD(txnOffshoreFee_usd) - if (type == RCTTypeCLSAGN) - { - VARINT_FIELD(txnOffshoreFee_xasset) - } - } else { - txnFee_usd = 0; - txnFee_xasset = 0; - txnOffshoreFee = 0; - txnOffshoreFee_usd = 0; - txnOffshoreFee_xasset = 0; - } - // 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 - if (type == RCTTypeSimple) // moved to prunable with bulletproofs - { - ar.tag("pseudoOuts"); - ar.begin_array(); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts); - if (pseudoOuts.size() != inputs) - return false; - for (size_t i = 0; i < inputs; ++i) - { - FIELDS(pseudoOuts[i]) - if (inputs - i > 1) - ar.delimit_array(); - } - ar.end_array(); - } - - ar.tag("ecdhInfo"); + ar.tag("pseudoOuts"); ar.begin_array(); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, ecdhInfo); - if (ecdhInfo.size() != outputs) + PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts); + if (pseudoOuts.size() != inputs) return false; - for (size_t i = 0; i < outputs; ++i) + for (size_t i = 0; i < inputs; ++i) { - if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN) - { - 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(); - } - else - { - FIELDS(ecdhInfo[i]) - } - if (outputs - i > 1) + FIELDS(pseudoOuts[i]) + if (inputs - 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) + 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) + { + if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2) { - FIELDS(outPk[i].mask) + 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(); + } + else + { + FIELDS(ecdhInfo[i]) + } + 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 (type == RCTTypeHaven2) { + + ar.tag("maskSums"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(2, maskSums); + if (maskSums.size() != 2) + return false; + FIELDS(maskSums[0]) + ar.delimit_array(); + FIELDS(maskSums[1]) ar.end_array(); + } else { + if ((type == RCTTypeCLSAG) || (type == RCTTypeCLSAGN)) { ar.tag("outPk_usd"); @@ -374,8 +395,8 @@ namespace rct { for (size_t i = 0; i < outputs; ++i) { FIELDS(outPk_usd[i].mask) - if (outputs - i > 1) - ar.delimit_array(); + if (outputs - i > 1) + ar.delimit_array(); } ar.end_array(); } @@ -389,14 +410,14 @@ namespace rct { for (size_t i = 0; i < outputs; ++i) { FIELDS(outPk_xasset[i].mask) - if (outputs - i > 1) - ar.delimit_array(); + if (outputs - i > 1) + ar.delimit_array(); } ar.end_array(); } - - return ar.stream().good(); - } + } + return ar.stream().good(); + } }; struct rctSigPrunable { std::vector rangeSigs; @@ -411,12 +432,12 @@ namespace rct { { if (type == RCTTypeNull) return ar.stream().good(); - if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeCLSAGN) + if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeCLSAGN && type != RCTTypeHaven2) return false; - if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN) + if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2) { uint32_t nbp = bulletproofs.size(); - if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN) + if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2) VARINT_FIELD(nbp) else FIELD(nbp) @@ -451,7 +472,7 @@ namespace rct { ar.end_array(); } - if ((type == RCTTypeCLSAG) || (type == RCTTypeCLSAGN)) + if ((type == RCTTypeCLSAG) || (type == RCTTypeCLSAGN) || (type == RCTTypeHaven2)) { ar.tag("CLSAGs"); ar.begin_array(); @@ -542,7 +563,7 @@ namespace rct { } ar.end_array(); } - if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN) + if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2) { ar.tag("pseudoOuts"); ar.begin_array(); @@ -566,12 +587,12 @@ namespace rct { keyV& get_pseudo_outs() { - return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN ? p.pseudoOuts : pseudoOuts; + return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 ? p.pseudoOuts : pseudoOuts; } keyV const& get_pseudo_outs() const { - return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN ? p.pseudoOuts : pseudoOuts; + return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 ? p.pseudoOuts : pseudoOuts; } };