From d58835b2f611872fffa3a6551ad63c9c239ae37e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 2 Dec 2017 08:32:39 +0000 Subject: [PATCH] integrate bulletproofs into monero --- .../cryptonote_boost_serialization.h | 21 ++++++ src/cryptonote_core/cryptonote_tx_utils.cpp | 6 +- src/cryptonote_core/cryptonote_tx_utils.h | 2 +- src/ringct/bulletproofs.h | 33 +-------- src/ringct/rctSigs.cpp | 64 ++++++++++++++---- src/ringct/rctSigs.h | 4 +- src/ringct/rctTypes.h | 67 +++++++++++++++++-- src/wallet/wallet2.cpp | 37 ++++++---- tests/unit_tests/bulletproofs.cpp | 1 + 9 files changed, 164 insertions(+), 71 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index a67fa0ae7..2abc3122c 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -211,6 +211,23 @@ namespace boost a & x.Ci; } + template + inline void serialize(Archive &a, rct::Bulletproof &x, const boost::serialization::version_type ver) + { + a & x.V; + a & x.A; + a & x.S; + a & x.T1; + a & x.T2; + a & x.taux; + a & x.mu; + a & x.L; + a & x.R; + a & x.a; + a & x.b; + a & x.t; + } + template inline void serialize(Archive &a, rct::boroSig &x, const boost::serialization::version_type ver) { @@ -278,6 +295,8 @@ namespace boost inline void serialize(Archive &a, rct::rctSigPrunable &x, const boost::serialization::version_type ver) { a & x.rangeSigs; + if (x.rangeSigs.empty()) + a & x.bulletproofs; a & x.MGs; } @@ -298,6 +317,8 @@ namespace boost a & x.txnFee; //-------------- a & x.p.rangeSigs; + if (x.p.rangeSigs.empty()) + a & x.p.bulletproofs; a & x.p.MGs; } } diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index feefc1592..20697520f 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -160,7 +160,7 @@ namespace cryptonote return destinations[0].addr.m_view_public_key; } //--------------------------------------------------------------- - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, const std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &additional_tx_keys, bool rct) + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, const std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &additional_tx_keys, bool rct, bool bulletproof) { std::vector amount_keys; tx.set_null(); @@ -552,9 +552,9 @@ namespace cryptonote get_transaction_prefix_hash(tx, tx_prefix_hash); rct::ctkeyV outSk; if (use_simple_rct) - tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk); + tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk, bulletproof); else - tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk); // same index assumption + tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk, bulletproof); // same index assumption CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index 8d9a1e332..d72f5d13b 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -88,7 +88,7 @@ namespace cryptonote //--------------------------------------------------------------- crypto::public_key get_destination_view_key_pub(const std::vector &destinations, const account_keys &sender_keys); bool construct_tx(const account_keys& sender_account_keys, std::vector& sources, const std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time); - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, const std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &additional_tx_keys, bool rct = false); + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map& subaddresses, std::vector& sources, const std::vector& destinations, const boost::optional& change_addr, std::vector extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector &additional_tx_keys, bool rct = false, bool bulletproof = false); bool generate_genesis_block( block& bl diff --git a/src/ringct/bulletproofs.h b/src/ringct/bulletproofs.h index a1e7df7ea..aca470f47 100644 --- a/src/ringct/bulletproofs.h +++ b/src/ringct/bulletproofs.h @@ -33,42 +33,11 @@ #ifndef BULLETPROOFS_H #define BULLETPROOFS_H -#include "serialization/serialization.h" -#include "ringct/rctOps.h" +#include "rctTypes.h" namespace rct { -struct Bulletproof -{ - rct::key V, 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) {} - - BEGIN_SERIALIZE_OBJECT() - FIELD(V) - FIELD(A) - FIELD(S) - FIELD(T1) - FIELD(T2) - FIELD(taux) - FIELD(mu) - FIELD(L) - FIELD(R) - FIELD(a) - FIELD(b) - FIELD(t) - - if (L.empty() || L.size() != R.size()) - return false; - END_SERIALIZE() -}; - Bulletproof bulletproof_PROVE(const rct::key &v, const rct::key &gamma); Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma); bool bulletproof_VERIFY(const Bulletproof &proof); diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 946325367..ddd26e3ad 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -33,6 +33,7 @@ #include "common/threadpool.h" #include "common/util.h" #include "rctSigs.h" +#include "bulletproofs.h" #include "cryptonote_basic/cryptonote_format_utils.h" using namespace crypto; @@ -42,6 +43,14 @@ using namespace std; #define MONERO_DEFAULT_LOG_CATEGORY "ringct" namespace rct { + Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount) + { + mask = rct::skGen(); + Bulletproof proof = bulletproof_PROVE(amount, mask); + C = proof.V; + return proof; + } + //Borromean (c.f. gmax/andytoshi's paper) boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) { key64 L[2], alpha; @@ -563,7 +572,7 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number // Note: For txn fees, the last index in the amounts vector should contain that // Thus the amounts vector will be "one" longer than the destinations vectort - rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk) { + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk, bool bulletproof) { CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations"); CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations"); CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing"); @@ -585,8 +594,14 @@ namespace rct { //add destination to sig rv.outPk[i].dest = copy(destinations[i]); //compute range proof - rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); + if (bulletproof) + rv.p.bulletproofs[i] = proveRangeBulletproof(rv.outPk[i].mask, outSk[i].mask, amounts[i]); + else + rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); #ifdef DBG + if (bulletproof) + CHECK_AND_ASSERT_THROW_MES(bulletproof_VERIFY(rv.p.bulletproofs[i]), "bulletproof_VERIFY failed on newly created proof"); + else CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); #endif @@ -618,12 +633,12 @@ namespace rct { ctkeyM mixRing; ctkeyV outSk; tie(mixRing, index) = populateFromBlockchain(inPk, mixin); - return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, index, outSk); + return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, index, outSk, false); } //RCT simple //for post-rct only - rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector &inamounts, const vector &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector & index, ctkeyV &outSk) { + rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector &inamounts, const vector &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector & index, ctkeyV &outSk, bool bulletproof) { CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk"); CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations"); @@ -638,7 +653,10 @@ namespace rct { rv.type = RCTTypeSimple; 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; @@ -650,10 +668,16 @@ namespace rct { //add destination to sig rv.outPk[i].dest = copy(destinations[i]); //compute range proof - rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); - #ifdef DBG - verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); - #endif + if (bulletproof) + rv.p.bulletproofs[i] = proveRangeBulletproof(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); + else + rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); + #ifdef DBG + if (bulletproof) + CHECK_AND_ASSERT_THROW_MES(bulletproof_VERIFY(rv.p.bulletproofs[i]), "bulletproof_VERIFY failed on newly created proof"); + else + CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); + #endif sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes); @@ -699,7 +723,7 @@ namespace rct { mixRing[i].resize(mixin+1); index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin); } - return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk); + return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk, false); } //RingCT protocol @@ -717,7 +741,10 @@ namespace rct { CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig"); if (semantics) { - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + if (rv.p.rangeSigs.empty()) + 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"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG"); } @@ -736,7 +763,10 @@ namespace rct { DP("range proofs verified?"); for (size_t i = 0; i < rv.outPk.size(); i++) { tpool.submit(&waiter, [&, i] { - results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); + if (rv.p.rangeSigs.empty()) + results[i] = bulletproof_VERIFY(rv.p.bulletproofs[i]); // TODO + else + results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); } waiter.wait(); @@ -779,7 +809,10 @@ namespace rct { CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "verRctSimple called on non simple rctSig"); if (semantics) { - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + if (rv.p.rangeSigs.empty()) + 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"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs"); } @@ -820,7 +853,10 @@ namespace rct { results.resize(rv.outPk.size()); for (size_t i = 0; i < rv.outPk.size(); i++) { tpool.submit(&waiter, [&, i] { - results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); + if (rv.p.rangeSigs.empty()) + results[i] = bulletproof_VERIFY(rv.p.bulletproofs[i]); + else + results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); } waiter.wait(); diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index d158f06f0..46c9cb2df 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -118,10 +118,10 @@ namespace rct { //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) // 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 - rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk); + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk, bool bulletproof); rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector & amounts, const keyV &amount_keys, const int mixin); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin); - rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector & index, ctkeyV &outSk); + rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector & index, ctkeyV &outSk, bool bulletproof); bool verRct(const rctSig & rv, bool semantics); static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); } bool verRctSimple(const rctSig & rv, bool semantics); diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 8147cb602..aa906c0ef 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -161,6 +161,37 @@ namespace rct { FIELD(Ci) END_SERIALIZE() }; + + struct Bulletproof + { + rct::key V, 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) {} + + BEGIN_SERIALIZE_OBJECT() + FIELD(V) + FIELD(A) + FIELD(S) + FIELD(T1) + FIELD(T2) + FIELD(taux) + FIELD(mu) + FIELD(L) + FIELD(R) + FIELD(a) + FIELD(b) + FIELD(t) + + if (L.empty() || L.size() != R.size()) + return false; + END_SERIALIZE() + }; + //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 @@ -241,6 +272,7 @@ namespace rct { }; struct rctSigPrunable { std::vector rangeSigs; + std::vector bulletproofs; std::vector MGs; // simple rct has N, full has 1 template class Archive> @@ -253,15 +285,33 @@ namespace rct { ar.tag("rangeSigs"); ar.begin_array(); PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, rangeSigs); - if (rangeSigs.size() != outputs) - return false; - for (size_t i = 0; i < outputs; ++i) + if (!rangeSigs.empty()) { - FIELDS(rangeSigs[i]) - if (outputs - i > 1) - ar.delimit_array(); + if (rangeSigs.size() != outputs) + return false; + for (size_t i = 0; i < outputs; ++i) + { + FIELDS(rangeSigs[i]) + if (outputs - i > 1) + ar.delimit_array(); + } + ar.end_array(); + } + else + { + 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(bulletproofs[i]) + if (outputs - i > 1) + ar.delimit_array(); + } + ar.end_array(); } - ar.end_array(); ar.tag("MGs"); ar.begin_array(); @@ -464,6 +514,7 @@ VARIANT_TAG(debug_archive, rct::mgSig, "rct::mgSig"); VARIANT_TAG(debug_archive, rct::rangeSig, "rct::rangeSig"); VARIANT_TAG(debug_archive, rct::boroSig, "rct::boroSig"); VARIANT_TAG(debug_archive, rct::rctSig, "rct::rctSig"); +VARIANT_TAG(debug_archive, rct::Bulletproof, "rct::bulletproof"); VARIANT_TAG(binary_archive, rct::key, 0x90); VARIANT_TAG(binary_archive, rct::key64, 0x91); @@ -477,6 +528,7 @@ VARIANT_TAG(binary_archive, rct::mgSig, 0x98); VARIANT_TAG(binary_archive, rct::rangeSig, 0x99); VARIANT_TAG(binary_archive, rct::boroSig, 0x9a); VARIANT_TAG(binary_archive, rct::rctSig, 0x9b); +VARIANT_TAG(binary_archive, rct::Bulletproof, 0x9c); VARIANT_TAG(json_archive, rct::key, "rct_key"); VARIANT_TAG(json_archive, rct::key64, "rct_key64"); @@ -490,5 +542,6 @@ VARIANT_TAG(json_archive, rct::mgSig, "rct_mgSig"); VARIANT_TAG(json_archive, rct::rangeSig, "rct_rangeSig"); VARIANT_TAG(json_archive, rct::boroSig, "rct_boroSig"); VARIANT_TAG(json_archive, rct::rctSig, "rct_rctSig"); +VARIANT_TAG(json_archive, rct::Bulletproof, "rct_bulletproof"); #endif /* RCTTYPES_H */ diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 181bb1852..5be57d33c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -456,7 +456,7 @@ void drop_from_short_history(std::list &short_chain_history, size_ } } -size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size) +size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof) { size_t size = 0; @@ -480,7 +480,10 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra size += 1; // rangeSigs - size += (2*64*32+32+64*32) * n_outputs; + if (bulletproof) + size += ((2*6 + 4 + 5)*32 + 3) * n_outputs; + else + size += (2*64*32+32+64*32) * n_outputs; // MGs size += n_inputs * (64 * (mixin+1) + 32); @@ -501,14 +504,22 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra return size; } -size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size) +size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof) { if (use_rct) - return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1, extra_size); + return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1, extra_size, bulletproof); else return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size; } +uint8_t get_bulletproof_fork(bool testnet) +{ + if (testnet) + return 7; + else + return 255; // TODO +} + } //namespace namespace tools @@ -4066,7 +4077,7 @@ std::vector wallet2::create_transactions(std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()) < TX_SIZE_TARGET(upper_transaction_size_limit)) { + if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit)) { // we can partially fill that destination LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_testnet, dsts[0].is_subaddress, dsts[0].addr) << " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount)); @@ -5706,7 +5718,7 @@ std::vector wallet2::create_transactions_2(std::vector= TX_SIZE_TARGET(upper_transaction_size_limit)); } } @@ -5715,7 +5727,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_from(const crypton std::vector> outs; const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0); + const bool bulletproof = use_fork_rules(get_bulletproof_fork(m_testnet), 0); const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); @@ -5955,14 +5968,14 @@ std::vector wallet2::create_transactions_from(const crypton // here, check if we need to sent tx and start a new one LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit " << upper_transaction_size_limit); - const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1, extra.size()); + const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1, extra.size(), bulletproof); bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); if (try_tx) { cryptonote::transaction test_tx; pending_tx test_ptx; - const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()); + const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof); needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier); tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress)); diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index 543cc8190..3d3dba5e6 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -30,6 +30,7 @@ #include "gtest/gtest.h" +#include "ringct/rctOps.h" #include "ringct/bulletproofs.h" TEST(bulletproofs, valid_zero)