From c83d0b3ee2776d42ad27845534fd82f43cf5c083 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 2 Dec 2017 21:17:42 +0000 Subject: [PATCH] add bulletproofs from v7 on testnet --- .../cryptonote_boost_serialization.h | 8 +- src/cryptonote_core/blockchain.cpp | 35 +++++-- src/cryptonote_core/cryptonote_core.cpp | 18 ++++ src/ringct/rctSigs.cpp | 91 ++++++++++++++----- src/ringct/rctTypes.h | 40 ++++---- src/serialization/json_object.cpp | 41 +++++++++ src/serialization/json_object.h | 3 + src/wallet/wallet2.cpp | 17 ++-- src/wallet/wallet2.h | 2 +- tests/core_tests/rct.cpp | 2 +- tests/performance_tests/check_tx_signature.h | 2 +- 11 files changed, 195 insertions(+), 64 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 2abc3122c..760edf9b9 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -280,11 +280,11 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeSimpleBulletproof) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - if (x.type == rct::RCTTypeSimple) + if (x.type == rct::RCTTypeSimple || x.type == rct::RCTTypeSimpleBulletproof) a & x.pseudoOuts; a & x.ecdhInfo; serializeOutPk(a, x.outPk, ver); @@ -306,11 +306,11 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeSimpleBulletproof) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - if (x.type == rct::RCTTypeSimple) + if (x.type == rct::RCTTypeSimple || x.type == rct::RCTTypeSimpleBulletproof) a & x.pseudoOuts; a & x.ecdhInfo; serializeOutPk(a, x.outPk, ver); diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 3d586a704..836856bae 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -127,6 +127,7 @@ static const struct { { 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6 { 6, 971400, 0, 1501709789 }, + { 7, 1057028, 0, 1512211236 }, }; static const uint64_t testnet_hard_fork_version_1_till = 624633; @@ -2387,8 +2388,10 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); + const uint8_t hf_version = m_hardfork->get_current_version(); + // from hard fork 2, we forbid dust and compound outputs - if (m_hardfork->get_current_version() >= 2) { + if (hf_version >= 2) { for (auto &o: tx.vout) { if (tx.version == 1) { @@ -2401,7 +2404,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } // in a v2 tx, all outputs must have 0 amount - if (m_hardfork->get_current_version() >= 3) { + if (hf_version >= 3) { if (tx.version >= 2) { for (auto &o: tx.vout) { if (o.amount != 0) { @@ -2413,7 +2416,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } // from v4, forbid invalid pubkeys - if (m_hardfork->get_current_version() >= 4) { + if (hf_version >= 4) { for (const auto &o: tx.vout) { if (o.target.type() == typeid(txout_to_key)) { const txout_to_key& out_to_key = boost::get(o.target); @@ -2425,6 +2428,16 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } } + // from v7, allow bulletproofs + if (hf_version < 7 || !m_testnet) { + if (!tx.rct_signatures.p.bulletproofs.empty()) + { + MERROR("Bulletproofs are not allowed before v7 or on mainnet"); + tvc.m_invalid_output = true; + return false; + } + } + return true; } //------------------------------------------------------------------ @@ -2450,7 +2463,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr rv.message = rct::hash2rct(tx_prefix_hash); // mixRing - full and simple store it in opposite ways - if (rv.type == rct::RCTTypeFull) + if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof) { rv.mixRing.resize(pubkeys[0].size()); for (size_t m = 0; m < pubkeys[0].size(); ++m) @@ -2464,7 +2477,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } } } - else if (rv.type == rct::RCTTypeSimple) + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof) { rv.mixRing.resize(pubkeys.size()); for (size_t n = 0; n < pubkeys.size(); ++n) @@ -2482,14 +2495,14 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } // II - if (rv.type == rct::RCTTypeFull) + if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof) { rv.p.MGs.resize(1); rv.p.MGs[0].II.resize(tx.vin.size()); for (size_t n = 0; n < tx.vin.size(); ++n) rv.p.MGs[0].II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); } - else if (rv.type == rct::RCTTypeSimple) + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof) { CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size"); for (size_t n = 0; n < tx.vin.size(); ++n) @@ -2753,7 +2766,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, MERROR_VER("Null rct signature on non-coinbase tx"); return false; } - case rct::RCTTypeSimple: { + case rct::RCTTypeSimple: + case rct::RCTTypeSimpleBulletproof: + { // check all this, either recontructed (so should really pass), or not { if (pubkeys.size() != rv.mixRing.size()) @@ -2809,7 +2824,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } break; } - case rct::RCTTypeFull: { + case rct::RCTTypeFull: + case rct::RCTTypeFullBulletproof: + { // check all this, either recontructed (so should really pass), or not { bool size_matches = true; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index edc2dfdaa..5cfa4b3e9 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -625,6 +625,22 @@ namespace cryptonote } for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n) rv.outPk[n].dest = rct::pk2rct(boost::get(tx.vout[n].target).key); + + const bool bulletproof = rv.type == rct::RCTTypeFullBulletproof || rv.type == rct::RCTTypeSimpleBulletproof; + if (bulletproof) + { + if (rv.p.bulletproofs.size() != tx.vout.size()) + { + LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad bulletproofs size in tx " << tx_hash << ", rejected"); + tvc.m_verifivation_failed = true; + return false; + } + for (size_t n = 0; n < rv.outPk.size(); ++n) + { + rv.p.bulletproofs[n].V.resize(1); + rv.p.bulletproofs[n].V[0] = rv.outPk[n].mask; + } + } } if (keeped_by_block && get_blockchain_storage().is_within_compiled_block_hash_area()) @@ -828,6 +844,7 @@ namespace cryptonote MERROR_VER("Unexpected Null rctSig type"); return false; case rct::RCTTypeSimple: + case rct::RCTTypeSimpleBulletproof: if (!rct::verRctSimple(rv, true)) { MERROR_VER("rct signature semantics check failed"); @@ -835,6 +852,7 @@ namespace cryptonote } break; case rct::RCTTypeFull: + case rct::RCTTypeFullBulletproof: if (!rct::verRct(rv, true)) { MERROR_VER("rct signature semantics check failed"); diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index ddd26e3ad..cfb4aaf97 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -47,7 +47,8 @@ namespace rct { { mask = rct::skGen(); Bulletproof proof = bulletproof_PROVE(amount, mask); - C = proof.V; + CHECK_AND_ASSERT_THROW_MES(proof.V.size() == 1, "V has not exactly one element"); + C = proof.V[0]; return proof; } @@ -344,16 +345,41 @@ namespace rct { hashes.push_back(hash2rct(h)); keyV kv; - kv.reserve((64*3+1) * rv.p.rangeSigs.size()); - for (auto r: rv.p.rangeSigs) + if (rv.type == RCTTypeSimpleBulletproof || rv.type == RCTTypeFullBulletproof) { - for (size_t n = 0; n < 64; ++n) - kv.push_back(r.asig.s0[n]); - for (size_t n = 0; n < 64; ++n) - kv.push_back(r.asig.s1[n]); - kv.push_back(r.asig.ee); - for (size_t n = 0; n < 64; ++n) - kv.push_back(r.Ci[n]); + kv.reserve((6*2+10) * rv.p.bulletproofs.size()); + for (const auto &p: rv.p.bulletproofs) + { + for (size_t n = 0; n < p.V.size(); ++n) + kv.push_back(p.V[n]); + kv.push_back(p.A); + kv.push_back(p.S); + kv.push_back(p.T1); + kv.push_back(p.T2); + kv.push_back(p.taux); + kv.push_back(p.mu); + for (size_t n = 0; n < p.L.size(); ++n) + kv.push_back(p.L[n]); + for (size_t n = 0; n < p.R.size(); ++n) + kv.push_back(p.R[n]); + kv.push_back(p.a); + kv.push_back(p.b); + kv.push_back(p.t); + } + } + else + { + kv.reserve((64*3+1) * rv.p.rangeSigs.size()); + for (const auto &r: rv.p.rangeSigs) + { + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.asig.s0[n]); + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.asig.s1[n]); + kv.push_back(r.asig.ee); + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.Ci[n]); + } } hashes.push_back(cn_fast_hash(kv)); return cn_fast_hash(hashes); @@ -581,10 +607,13 @@ namespace rct { } rctSig rv; - rv.type = RCTTypeFull; + rv.type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull; rv.message = message; rv.outPk.resize(destinations.size()); - rv.p.rangeSigs.resize(destinations.size()); + if (bulletproof) + rv.p.bulletproofs.resize(destinations.size()); + else + rv.p.rangeSigs.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size()); size_t i = 0; @@ -650,7 +679,7 @@ namespace rct { } rctSig rv; - rv.type = RCTTypeSimple; + rv.type = bulletproof ? RCTTypeSimpleBulletproof : RCTTypeSimple; rv.message = message; rv.outPk.resize(destinations.size()); if (bulletproof) @@ -738,10 +767,10 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number bool verRct(const rctSig & rv, bool semantics) { PERF_TIMER(verRct); - CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "verRct called on non-full rctSig"); if (semantics) { - if (rv.p.rangeSigs.empty()) + if (rv.type == RCTTypeFullBulletproof) CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs"); else CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); @@ -764,7 +793,7 @@ namespace rct { for (size_t i = 0; i < rv.outPk.size(); i++) { tpool.submit(&waiter, [&, i] { if (rv.p.rangeSigs.empty()) - results[i] = bulletproof_VERIFY(rv.p.bulletproofs[i]); // TODO + results[i] = bulletproof_VERIFY(rv.p.bulletproofs[i]); else results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); @@ -806,10 +835,10 @@ namespace rct { { PERF_TIMER(verRctSimple); - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "verRctSimple called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSimple called on non simple rctSig"); if (semantics) { - if (rv.p.rangeSigs.empty()) + if (rv.type == RCTTypeSimpleBulletproof) CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs"); else CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); @@ -905,9 +934,17 @@ namespace rct { // uses the attached ecdh info to find the amounts represented by each output commitment // must know the destination private key to find the correct amount, else will return a random number xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask) { - CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig"); - CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "decodeRct called on non-full rctSig"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); + if (rv.type == RCTTypeFullBulletproof) + { + CHECK_AND_ASSERT_THROW_MES(rv.p.bulletproofs.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.p.bulletproofs and rv.ecdhInfo"); + CHECK_AND_ASSERT_THROW_MES(rv.p.bulletproofs[i].V.size() == 1, "Unexpected sizes of rv.p.bulletproofs[i].V"); + } + else + { + CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); + } //mask amount and mask ecdhTuple ecdh_info = rv.ecdhInfo[i]; @@ -933,16 +970,24 @@ namespace rct { } xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask) { - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "decodeRct called on non simple rctSig"); - CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "decodeRct called on non simple rctSig"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); + if (rv.type == RCTTypeSimpleBulletproof) + { + CHECK_AND_ASSERT_THROW_MES(rv.p.bulletproofs.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.p.bulletproofs and rv.ecdhInfo"); + CHECK_AND_ASSERT_THROW_MES(rv.p.bulletproofs[i].V.size() == 1, "Unexpected sizes of rv.p.bulletproofs[i].V"); + } + else + { + CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); + } //mask amount and mask ecdhTuple ecdh_info = rv.ecdhInfo[i]; ecdhDecode(ecdh_info, sk); mask = ecdh_info.mask; key amount = ecdh_info.amount; - key C = rv.outPk[i].mask; + key C = (rv.type == RCTTypeSimpleBulletproof) ? rv.p.bulletproofs[i].V.front() : rv.outPk[i].mask; DP("C"); DP(C); key Ctmp; diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index aa906c0ef..50dfdb432 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -164,17 +164,19 @@ namespace rct { struct Bulletproof { - rct::key V, A, S, T1, T2; + rct::keyV V; + rct::key A, S, T1, T2; rct::key taux, mu; rct::keyV L, R; rct::key a, b, t; Bulletproof() {} Bulletproof(const rct::key &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t): - V(V), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {} + V({V}), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {} BEGIN_SERIALIZE_OBJECT() - FIELD(V) + // Commitments aren't saved, they're restored via outPk + // FIELD(V) FIELD(A) FIELD(S) FIELD(T1) @@ -203,6 +205,8 @@ namespace rct { RCTTypeNull = 0, RCTTypeFull = 1, RCTTypeSimple = 2, + RCTTypeFullBulletproof = 3, + RCTTypeSimpleBulletproof = 4, }; struct rctSigBase { uint8_t type; @@ -220,13 +224,13 @@ namespace rct { FIELD(type) if (type == RCTTypeNull) return true; - if (type != RCTTypeFull && type != RCTTypeSimple) + if (type != RCTTypeFull && type != RCTTypeFullBulletproof && type != RCTTypeSimple && type != RCTTypeSimpleBulletproof) return false; VARINT_FIELD(txnFee) // 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) + if (type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) { ar.tag("pseudoOuts"); ar.begin_array(); @@ -280,18 +284,18 @@ namespace rct { { if (type == RCTTypeNull) return true; - if (type != RCTTypeFull && type != RCTTypeSimple) + if (type != RCTTypeFull && type != RCTTypeFullBulletproof && type != RCTTypeSimple && type != RCTTypeSimpleBulletproof) return false; - ar.tag("rangeSigs"); - ar.begin_array(); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, rangeSigs); - if (!rangeSigs.empty()) + if (type == RCTTypeSimpleBulletproof || type == RCTTypeFullBulletproof) { - if (rangeSigs.size() != outputs) + ar.tag("bp"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs); + if (bulletproofs.size() != outputs) return false; for (size_t i = 0; i < outputs; ++i) { - FIELDS(rangeSigs[i]) + FIELDS(bulletproofs[i]) if (outputs - i > 1) ar.delimit_array(); } @@ -299,14 +303,14 @@ namespace rct { } else { - ar.tag("bp"); + ar.tag("rangeSigs"); ar.begin_array(); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs); - if (bulletproofs.size() != outputs) + PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, rangeSigs); + if (rangeSigs.size() != outputs) return false; for (size_t i = 0; i < outputs; ++i) { - FIELDS(bulletproofs[i]) + FIELDS(rangeSigs[i]) if (outputs - i > 1) ar.delimit_array(); } @@ -317,7 +321,7 @@ namespace rct { ar.begin_array(); // we keep a byte for size of MGs, because we don't know whether this is // a simple or full rct signature, and it's starting to annoy the hell out of me - size_t mg_elements = type == RCTTypeSimple ? inputs : 1; + size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) ? inputs : 1; PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs); if (MGs.size() != mg_elements) return false; @@ -335,7 +339,7 @@ namespace rct { for (size_t j = 0; j < mixin + 1; ++j) { ar.begin_array(); - size_t mg_ss2_elements = (type == RCTTypeSimple ? 1 : inputs) + 1; + size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) ? 1 : inputs) + 1; PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]); if (MGs[i].ss[j].size() != mg_ss2_elements) return false; diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index 6e6e51528..2c86d4054 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -1007,6 +1007,7 @@ void toJsonValue(rapidjson::Document& doc, const rct::rctSigPrunable& sig, rapid val.SetObject(); INSERT_INTO_JSON_OBJECT(val, doc, rangeSigs, sig.rangeSigs); + INSERT_INTO_JSON_OBJECT(val, doc, bulletproofs, sig.bulletproofs); INSERT_INTO_JSON_OBJECT(val, doc, MGs, sig.MGs); } @@ -1018,6 +1019,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig) } GET_FROM_JSON_OBJECT(val, sig.rangeSigs, rangeSigs); + GET_FROM_JSON_OBJECT(val, sig.bulletproofs, bulletproofs); GET_FROM_JSON_OBJECT(val, sig.MGs, MGs); } @@ -1052,6 +1054,45 @@ void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig) } } +void toJsonValue(rapidjson::Document& doc, const rct::Bulletproof& p, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, V, p.V); + INSERT_INTO_JSON_OBJECT(val, doc, A, p.A); + INSERT_INTO_JSON_OBJECT(val, doc, S, p.S); + INSERT_INTO_JSON_OBJECT(val, doc, T1, p.T1); + INSERT_INTO_JSON_OBJECT(val, doc, T2, p.T2); + INSERT_INTO_JSON_OBJECT(val, doc, taux, p.taux); + INSERT_INTO_JSON_OBJECT(val, doc, mu, p.mu); + INSERT_INTO_JSON_OBJECT(val, doc, L, p.L); + INSERT_INTO_JSON_OBJECT(val, doc, R, p.R); + INSERT_INTO_JSON_OBJECT(val, doc, a, p.a); + INSERT_INTO_JSON_OBJECT(val, doc, b, p.b); + INSERT_INTO_JSON_OBJECT(val, doc, t, p.t); +} + +void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, p.V, V); + GET_FROM_JSON_OBJECT(val, p.A, A); + GET_FROM_JSON_OBJECT(val, p.S, S); + GET_FROM_JSON_OBJECT(val, p.T1, T1); + GET_FROM_JSON_OBJECT(val, p.T2, T2); + GET_FROM_JSON_OBJECT(val, p.taux, taux); + GET_FROM_JSON_OBJECT(val, p.mu, mu); + GET_FROM_JSON_OBJECT(val, p.L, L); + GET_FROM_JSON_OBJECT(val, p.R, R); + GET_FROM_JSON_OBJECT(val, p.a, a); + GET_FROM_JSON_OBJECT(val, p.b, b); + GET_FROM_JSON_OBJECT(val, p.t, t); +} + void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val) { val.SetObject(); diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h index 7b9519c48..5dca7b249 100644 --- a/src/serialization/json_object.h +++ b/src/serialization/json_object.h @@ -274,6 +274,9 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig); void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val); void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig); +void toJsonValue(rapidjson::Document& doc, const rct::Bulletproof& p, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p); + void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val); void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 5be57d33c..e29e1051c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -823,8 +823,10 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation & switch (rv.type) { case rct::RCTTypeSimple: + case rct::RCTTypeSimpleBulletproof: return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask); case rct::RCTTypeFull: + case rct::RCTTypeFullBulletproof: return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask); default: LOG_ERROR("Unsupported rct type: " << rv.type); @@ -3774,9 +3776,10 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size()); signed_txes.ptx.push_back(pending_tx()); tools::wallet2::pending_tx &ptx = signed_txes.ptx.back(); + bool bulletproof = sd.use_rct && !ptx.tx.rct_signatures.p.bulletproofs.empty(); crypto::secret_key tx_key; std::vector additional_tx_keys; - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct); + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, bulletproof); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_testnet); // we don't test tx size, because we don't know the current limit, due to not having a blockchain, // and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway, @@ -4659,7 +4662,7 @@ void wallet2::transfer_selected(const std::vector dsts, const std::vector& selected_transfers, size_t fake_outputs_count, std::vector> &outs, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx &ptx) + uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof) { using namespace cryptonote; // throw if attempting a transaction with no destinations @@ -4775,7 +4778,7 @@ void wallet2::transfer_selected_rct(std::vector additional_tx_keys; LOG_PRINT_L2("constructing tx"); - bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true); + bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, bulletproof); LOG_PRINT_L2("constructed tx, r="< wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector test_ptx.fee) { if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, - test_tx, test_ptx); + test_tx, test_ptx, bulletproof); else transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); @@ -5984,7 +5987,7 @@ std::vector wallet2::create_transactions_from(const crypton tx.selected_transfers.size() << " outputs"); if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, - test_tx, test_ptx); + test_tx, test_ptx, bulletproof); else transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); @@ -6001,7 +6004,7 @@ std::vector wallet2::create_transactions_from(const crypton tx.dsts[0].amount = available_for_fee - needed_fee; if (use_rct) transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, - test_tx, test_ptx); + test_tx, test_ptx, bulletproof); else transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index eeec7c338..866b95853 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -536,7 +536,7 @@ namespace tools uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx); void transfer_selected_rct(std::vector dsts, const std::vector& selected_transfers, size_t fake_outputs_count, std::vector> &outs, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx &ptx); + uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof); void commit_tx(pending_tx& ptx_vector); void commit_tx(std::vector& ptx_vector); diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index 50f65cc67..e5047baf2 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -132,7 +132,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation"); crypto::secret_key amount_key; crypto::derivation_to_scalar(derivation, o, amount_key); - if (rct_txes[n].rct_signatures.type == rct::RCTTypeSimple) + if (rct_txes[n].rct_signatures.type == rct::RCTTypeSimple || rct_txes[n].rct_signatures.type == rct::RCTTypeSimpleBulletproof) rct::decodeRctSimple(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4]); else rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4]); diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h index 02555fae8..afc2bdc45 100644 --- a/tests/performance_tests/check_tx_signature.h +++ b/tests/performance_tests/check_tx_signature.h @@ -80,7 +80,7 @@ public: { if (rct) { - if (m_tx.rct_signatures.type == rct::RCTTypeFull) + if (m_tx.rct_signatures.type == rct::RCTTypeFull || m_tx.rct_signatures.type == rct::RCTTypeFullBulletproof) return rct::verRct(m_tx.rct_signatures); else return rct::verRctSimple(m_tx.rct_signatures);