aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cryptonote_basic/cryptonote_boost_serialization.h32
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp47
-rw-r--r--src/cryptonote_config.h2
-rw-r--r--src/cryptonote_core/blockchain.cpp35
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp23
-rw-r--r--src/hardforks/hardforks.cpp3
-rw-r--r--src/ringct/bulletproofs_plus.cc14
-rw-r--r--src/ringct/rctSigs.cpp200
-rw-r--r--src/ringct/rctTypes.cpp87
-rw-r--r--src/ringct/rctTypes.h51
-rw-r--r--src/serialization/json_object.cpp42
-rw-r--r--src/serialization/json_object.h3
-rw-r--r--src/wallet/api/wallet.cpp1
-rw-r--r--src/wallet/wallet2.cpp85
-rw-r--r--src/wallet/wallet2.h2
-rw-r--r--tests/core_tests/CMakeLists.txt2
-rw-r--r--tests/core_tests/bulletproof_plus.cpp373
-rw-r--r--tests/core_tests/bulletproof_plus.h206
-rw-r--r--tests/core_tests/bulletproofs.cpp41
-rw-r--r--tests/core_tests/bulletproofs.h6
-rw-r--r--tests/core_tests/chaingen_main.cpp18
-rw-r--r--tests/core_tests/chaingen_tests_list.h1
-rw-r--r--tests/core_tests/multisig.cpp13
-rw-r--r--tests/core_tests/multisig.h2
-rw-r--r--tests/core_tests/rct.cpp2
-rw-r--r--tests/core_tests/rct2.cpp2
-rw-r--r--tests/performance_tests/bulletproof_plus.h1
-rw-r--r--tests/unit_tests/bulletproofs.cpp2
28 files changed, 1137 insertions, 159 deletions
diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h
index c6b81b094..24d452083 100644
--- a/src/cryptonote_basic/cryptonote_boost_serialization.h
+++ b/src/cryptonote_basic/cryptonote_boost_serialization.h
@@ -228,6 +228,20 @@ namespace boost
}
template <class Archive>
+ inline void serialize(Archive &a, rct::BulletproofPlus &x, const boost::serialization::version_type ver)
+ {
+ a & x.V;
+ a & x.A;
+ a & x.A1;
+ a & x.B;
+ a & x.r1;
+ a & x.s1;
+ a & x.d1;
+ a & x.L;
+ a & x.R;
+ }
+
+ template <class Archive>
inline void serialize(Archive &a, rct::boroSig &x, const boost::serialization::version_type ver)
{
a & x.s0;
@@ -305,7 +319,7 @@ namespace boost
a & x.type;
if (x.type == rct::RCTTypeNull)
return;
- if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG)
+ if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG && x.type != rct::RCTTypeBulletproofPlus)
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
@@ -321,7 +335,11 @@ namespace boost
{
a & x.rangeSigs;
if (x.rangeSigs.empty())
+ {
a & x.bulletproofs;
+ if (ver >= 2u)
+ a & x.bulletproofs_plus;
+ }
a & x.MGs;
if (ver >= 1u)
a & x.CLSAGs;
@@ -335,7 +353,7 @@ namespace boost
a & x.type;
if (x.type == rct::RCTTypeNull)
return;
- if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG)
+ if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG && x.type != rct::RCTTypeBulletproofPlus)
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
@@ -347,11 +365,15 @@ namespace boost
//--------------
a & x.p.rangeSigs;
if (x.p.rangeSigs.empty())
+ {
a & x.p.bulletproofs;
+ if (ver >= 2u)
+ a & x.p.bulletproofs_plus;
+ }
a & x.p.MGs;
if (ver >= 1u)
a & x.p.CLSAGs;
- if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2 || x.type == rct::RCTTypeCLSAG)
+ if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2 || x.type == rct::RCTTypeCLSAG || x.type == rct::RCTTypeBulletproofPlus)
a & x.p.pseudoOuts;
}
@@ -392,6 +414,6 @@ namespace boost
}
}
-BOOST_CLASS_VERSION(rct::rctSigPrunable, 1)
-BOOST_CLASS_VERSION(rct::rctSig, 1)
+BOOST_CLASS_VERSION(rct::rctSigPrunable, 2)
+BOOST_CLASS_VERSION(rct::rctSig, 2)
BOOST_CLASS_VERSION(rct::multisig_out, 1)
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 835f59d69..8c10fad1f 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -105,7 +105,9 @@ namespace cryptonote
uint64_t get_transaction_weight_clawback(const transaction &tx, size_t n_padded_outputs)
{
- const uint64_t bp_base = 368;
+ const rct::rctSig &rv = tx.rct_signatures;
+ const bool plus = rv.type == rct::RCTTypeBulletproofPlus;
+ const uint64_t bp_base = (32 * ((plus ? 6 : 9) + 7 * 2)) / 2; // notional size of a 2 output proof, normalized to 1 proof (ie, divided by 2)
const size_t n_outputs = tx.vout.size();
if (n_padded_outputs <= 2)
return 0;
@@ -113,7 +115,7 @@ namespace cryptonote
while ((1u << nlr) < n_padded_outputs)
++nlr;
nlr += 6;
- const size_t bp_size = 32 * (9 + 2 * nlr);
+ const size_t bp_size = 32 * ((plus ? 6 : 9) + 2 * nlr);
CHECK_AND_ASSERT_THROW_MES_L1(n_outputs <= BULLETPROOF_MAX_OUTPUTS, "maximum number of outputs is " + std::to_string(BULLETPROOF_MAX_OUTPUTS) + " per transaction");
CHECK_AND_ASSERT_THROW_MES_L1(bp_base * n_padded_outputs >= bp_size, "Invalid bulletproof clawback: bp_base " + std::to_string(bp_base) + ", n_padded_outputs "
+ std::to_string(n_padded_outputs) + ", bp_size " + std::to_string(bp_size));
@@ -164,7 +166,32 @@ namespace cryptonote
if (!base_only)
{
const bool bulletproof = rct::is_rct_bulletproof(rv.type);
- if (bulletproof)
+ const bool bulletproof_plus = rct::is_rct_bulletproof_plus(rv.type);
+ if (bulletproof_plus)
+ {
+ if (rv.p.bulletproofs_plus.size() != 1)
+ {
+ LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs_plus size in tx " << get_transaction_hash(tx));
+ return false;
+ }
+ if (rv.p.bulletproofs_plus[0].L.size() < 6)
+ {
+ LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs_plus L size in tx " << get_transaction_hash(tx));
+ return false;
+ }
+ const size_t max_outputs = rct::n_bulletproof_plus_max_amounts(rv.p.bulletproofs_plus[0]);
+ if (max_outputs < tx.vout.size())
+ {
+ LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs_plus max outputs in tx " << get_transaction_hash(tx));
+ return false;
+ }
+ const size_t n_amounts = tx.vout.size();
+ CHECK_AND_ASSERT_MES(n_amounts == rv.outPk.size(), false, "Internal error filling out V");
+ rv.p.bulletproofs_plus[0].V.resize(n_amounts);
+ for (size_t i = 0; i < n_amounts; ++i)
+ rv.p.bulletproofs_plus[0].V[i] = rct::scalarmultKey(rv.outPk[i].mask, rct::INV_EIGHT);
+ }
+ else if (bulletproof)
{
if (rv.p.bulletproofs.size() != 1)
{
@@ -419,9 +446,11 @@ namespace cryptonote
if (tx.version < 2)
return blob_size;
const rct::rctSig &rv = tx.rct_signatures;
- if (!rct::is_rct_bulletproof(rv.type))
+ const bool bulletproof = rct::is_rct_bulletproof(rv.type);
+ const bool bulletproof_plus = rct::is_rct_bulletproof_plus(rv.type);
+ if (!bulletproof && !bulletproof_plus)
return blob_size;
- const size_t n_padded_outputs = rct::n_bulletproof_max_amounts(rv.p.bulletproofs);
+ const size_t n_padded_outputs = bulletproof_plus ? rct::n_bulletproof_plus_max_amounts(rv.p.bulletproofs_plus) : rct::n_bulletproof_max_amounts(rv.p.bulletproofs);
uint64_t bp_clawback = get_transaction_weight_clawback(tx, n_padded_outputs);
CHECK_AND_ASSERT_THROW_MES_L1(bp_clawback <= std::numeric_limits<uint64_t>::max() - blob_size, "Weight overflow");
return blob_size + bp_clawback;
@@ -431,8 +460,8 @@ namespace cryptonote
{
CHECK_AND_ASSERT_MES(tx.pruned, std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support non pruned txes");
CHECK_AND_ASSERT_MES(tx.version >= 2, std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support v1 txes");
- CHECK_AND_ASSERT_MES(tx.rct_signatures.type >= rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG,
- std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support older range proof types");
+ CHECK_AND_ASSERT_MES(tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus,
+ std::numeric_limits<uint64_t>::max(), "Unsupported rct_signatures type in get_pruned_transaction_weight");
CHECK_AND_ASSERT_MES(!tx.vin.empty(), std::numeric_limits<uint64_t>::max(), "empty vin");
CHECK_AND_ASSERT_MES(tx.vin[0].type() == typeid(cryptonote::txin_to_key), std::numeric_limits<uint64_t>::max(), "empty vin");
@@ -450,12 +479,12 @@ namespace cryptonote
while ((n_padded_outputs = (1u << nrl)) < tx.vout.size())
++nrl;
nrl += 6;
- extra = 32 * (9 + 2 * nrl) + 2;
+ extra = 32 * ((rct::is_rct_bulletproof_plus(tx.rct_signatures.type) ? 6 : 9) + 2 * nrl) + 2;
weight += extra;
// calculate deterministic CLSAG/MLSAG data size
const size_t ring_size = boost::get<cryptonote::txin_to_key>(tx.vin[0]).key_offsets.size();
- if (tx.rct_signatures.type == rct::RCTTypeCLSAG)
+ if (rct::is_rct_clsag(tx.rct_signatures.type))
extra = tx.vin.size() * (ring_size + 2) * 32;
else
extra = tx.vin.size() * (ring_size * (1 + 1) * 32 + 32 /* cc */);
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index b2919ac20..ff61fc036 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -182,6 +182,7 @@
#define HF_VERSION_EXACT_COINBASE 13
#define HF_VERSION_CLSAG 13
#define HF_VERSION_DETERMINISTIC_UNLOCK_TIME 13
+#define HF_VERSION_BULLETPROOF_PLUS 15
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
@@ -190,6 +191,7 @@
#define DEFAULT_TXPOOL_MAX_WEIGHT 648000000ull // 3 days at 300000, in bytes
#define BULLETPROOF_MAX_OUTPUTS 16
+#define BULLETPROOF_PLUS_MAX_OUTPUTS 16
#define CRYPTONOTE_PRUNING_STRIPE_SIZE 4096 // the smaller, the smoother the increase
#define CRYPTONOTE_PRUNING_LOG_STRIPES 3 // the higher, the more space saved
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 34031fb7c..cd9972d1e 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -3143,6 +3143,32 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
}
}
+ // from v15, allow bulletproofs plus
+ if (hf_version < HF_VERSION_BULLETPROOF_PLUS) {
+ if (tx.version >= 2) {
+ const bool bulletproof_plus = rct::is_rct_bulletproof_plus(tx.rct_signatures.type);
+ if (bulletproof_plus || !tx.rct_signatures.p.bulletproofs_plus.empty())
+ {
+ MERROR_VER("Bulletproofs plus are not allowed before v" << std::to_string(HF_VERSION_BULLETPROOF_PLUS));
+ tvc.m_invalid_output = true;
+ return false;
+ }
+ }
+ }
+
+ // from v16, forbid bulletproofs
+ if (hf_version > HF_VERSION_BULLETPROOF_PLUS) {
+ if (tx.version >= 2) {
+ const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type);
+ if (bulletproof)
+ {
+ MERROR_VER("Bulletproof range proofs are not allowed after v" + std::to_string(HF_VERSION_BULLETPROOF_PLUS));
+ tvc.m_invalid_output = true;
+ return false;
+ }
+ }
+ }
+
return true;
}
//------------------------------------------------------------------
@@ -3183,7 +3209,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
}
}
- else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeCLSAG)
+ else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus)
{
CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys.size());
@@ -3224,7 +3250,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
}
}
- else if (rv.type == rct::RCTTypeCLSAG)
+ else if (rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus)
{
if (!tx.pruned)
{
@@ -3516,6 +3542,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
case rct::RCTTypeBulletproof:
case rct::RCTTypeBulletproof2:
case rct::RCTTypeCLSAG:
+ case rct::RCTTypeBulletproofPlus:
{
// check all this, either reconstructed (so should really pass), or not
{
@@ -3551,7 +3578,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
}
- const size_t n_sigs = rv.type == rct::RCTTypeCLSAG ? rv.p.CLSAGs.size() : rv.p.MGs.size();
+ const size_t n_sigs = rct::is_rct_clsag(rv.type) ? rv.p.CLSAGs.size() : rv.p.MGs.size();
if (n_sigs != tx.vin.size())
{
MERROR_VER("Failed to check ringct signatures: mismatched MGs/vin sizes");
@@ -3560,7 +3587,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
for (size_t n = 0; n < tx.vin.size(); ++n)
{
bool error;
- if (rv.type == rct::RCTTypeCLSAG)
+ if (rct::is_rct_clsag(rv.type))
error = memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.CLSAGs[n].I, 32);
else
error = rv.p.MGs[n].II.empty() || memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32);
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 4c6536318..1da4e2d41 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -879,6 +879,16 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ static bool is_canonical_bulletproof_plus_layout(const std::vector<rct::BulletproofPlus> &proofs)
+ {
+ if (proofs.size() != 1)
+ return false;
+ const size_t sz = proofs[0].V.size();
+ if (sz == 0 || sz > BULLETPROOF_PLUS_MAX_OUTPUTS)
+ return false;
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::handle_incoming_tx_accumulated_batch(std::vector<tx_verification_batch_info> &tx_info, bool keeped_by_block)
{
bool ret = true;
@@ -943,6 +953,17 @@ namespace cryptonote
}
rvv.push_back(&rv); // delayed batch verification
break;
+ case rct::RCTTypeBulletproofPlus:
+ if (!is_canonical_bulletproof_plus_layout(rv.p.bulletproofs_plus))
+ {
+ MERROR_VER("Bulletproof_plus does not have canonical form");
+ set_semantics_failed(tx_info[n].tx_hash);
+ tx_info[n].tvc.m_verifivation_failed = true;
+ tx_info[n].result = false;
+ break;
+ }
+ rvv.push_back(&rv); // delayed batch verification
+ break;
default:
MERROR_VER("Unknown rct type: " << rv.type);
set_semantics_failed(tx_info[n].tx_hash);
@@ -960,7 +981,7 @@ namespace cryptonote
{
if (!tx_info[n].result)
continue;
- if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2 && tx_info[n].tx->rct_signatures.type != rct::RCTTypeCLSAG)
+ if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2 && tx_info[n].tx->rct_signatures.type != rct::RCTTypeCLSAG && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproofPlus)
continue;
if (assumed_bad || !rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures))
{
diff --git a/src/hardforks/hardforks.cpp b/src/hardforks/hardforks.cpp
index 9055b92e3..45db59a67 100644
--- a/src/hardforks/hardforks.cpp
+++ b/src/hardforks/hardforks.cpp
@@ -70,6 +70,9 @@ const hardfork_t mainnet_hard_forks[] = {
{ 13, 2210000, 0, 1598180817 },
{ 14, 2210720, 0, 1598180818 },
+
+ { 15, 8000000, 0, 1608223241 }, // temp so tests test with these consensus rules
+ { 16, 8000001, 0, 1608223242 }, // temp so tests test with these consensus rules
};
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
const uint64_t mainnet_hard_fork_version_1_till = 1009826;
diff --git a/src/ringct/bulletproofs_plus.cc b/src/ringct/bulletproofs_plus.cc
index fd2ebfce0..589e58525 100644
--- a/src/ringct/bulletproofs_plus.cc
+++ b/src/ringct/bulletproofs_plus.cc
@@ -65,7 +65,7 @@ namespace rct
// Proof bounds
static constexpr size_t maxN = 64; // maximum number of bits in range
- static constexpr size_t maxM = BULLETPROOF_MAX_OUTPUTS; // maximum number of outputs to aggregate into a single proof
+ static constexpr size_t maxM = BULLETPROOF_PLUS_MAX_OUTPUTS; // maximum number of outputs to aggregate into a single proof
// Cached public generators
static rct::key Hi[maxN*maxM], Gi[maxN*maxM];
@@ -796,15 +796,7 @@ try_again:
rct::keyV sv(v.size());
for (size_t i = 0; i < v.size(); ++i)
{
- sv[i] = rct::zero();
- sv[i].bytes[0] = v[i] & 255;
- sv[i].bytes[1] = (v[i] >> 8) & 255;
- sv[i].bytes[2] = (v[i] >> 16) & 255;
- sv[i].bytes[3] = (v[i] >> 24) & 255;
- sv[i].bytes[4] = (v[i] >> 32) & 255;
- sv[i].bytes[5] = (v[i] >> 40) & 255;
- sv[i].bytes[6] = (v[i] >> 48) & 255;
- sv[i].bytes[7] = (v[i] >> 56) & 255;
+ sv[i] = rct::d2h(v[i]);
}
return bulletproof_plus_PROVE(sv, gamma);
}
@@ -836,7 +828,7 @@ try_again:
// We'll perform only a single batch inversion across all proofs in the batch,
// since batch inversion requires only one scalar inversion operation.
std::vector<rct::key> to_invert;
- to_invert.reserve(11 * sizeof(proofs)); // maximal size, given the aggregation limit
+ to_invert.reserve(11 * proofs.size()); // maximal size, given the aggregation limit
for (const BulletproofPlus *p: proofs)
{
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index f5950c53c..961d83a9e 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -35,6 +35,7 @@
#include "common/util.h"
#include "rctSigs.h"
#include "bulletproofs.h"
+#include "bulletproofs_plus.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_config.h"
@@ -78,6 +79,36 @@ namespace
return rct::Bulletproof{rct::keyV(n_outs, I), I, I, I, I, I, I, rct::keyV(nrl, I), rct::keyV(nrl, I), I, I, I};
}
+ rct::BulletproofPlus make_dummy_bulletproof_plus(const std::vector<uint64_t> &outamounts, rct::keyV &C, rct::keyV &masks)
+ {
+ const size_t n_outs = outamounts.size();
+ const rct::key I = rct::identity();
+ size_t nrl = 0;
+ while ((1u << nrl) < n_outs)
+ ++nrl;
+ nrl += 6;
+
+ C.resize(n_outs);
+ masks.resize(n_outs);
+ for (size_t i = 0; i < n_outs; ++i)
+ {
+ masks[i] = I;
+ rct::key sv8, sv;
+ sv = rct::zero();
+ sv.bytes[0] = outamounts[i] & 255;
+ sv.bytes[1] = (outamounts[i] >> 8) & 255;
+ sv.bytes[2] = (outamounts[i] >> 16) & 255;
+ sv.bytes[3] = (outamounts[i] >> 24) & 255;
+ sv.bytes[4] = (outamounts[i] >> 32) & 255;
+ sv.bytes[5] = (outamounts[i] >> 40) & 255;
+ sv.bytes[6] = (outamounts[i] >> 48) & 255;
+ sv.bytes[7] = (outamounts[i] >> 56) & 255;
+ sc_mul(sv8.bytes, sv.bytes, rct::INV_EIGHT.bytes);
+ rct::addKeys2(C[i], rct::INV_EIGHT, sv8, rct::H);
+ }
+
+ return rct::BulletproofPlus{rct::keyV(n_outs, I), I, I, I, I, I, I, rct::keyV(nrl, I), rct::keyV(nrl, I)};
+ }
}
namespace rct {
@@ -107,6 +138,32 @@ namespace rct {
catch (...) { return false; }
}
+ BulletproofPlus proveRangeBulletproofPlus(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts, epee::span<const key> sk, hw::device &hwdev)
+ {
+ CHECK_AND_ASSERT_THROW_MES(amounts.size() == sk.size(), "Invalid amounts/sk sizes");
+ masks.resize(amounts.size());
+ for (size_t i = 0; i < masks.size(); ++i)
+ masks[i] = hwdev.genCommitmentMask(sk[i]);
+ BulletproofPlus proof = bulletproof_plus_PROVE(amounts, masks);
+ CHECK_AND_ASSERT_THROW_MES(proof.V.size() == amounts.size(), "V does not have the expected size");
+ C = proof.V;
+ return proof;
+ }
+
+ bool verBulletproofPlus(const BulletproofPlus &proof)
+ {
+ try { return bulletproof_plus_VERIFY(proof); }
+ // we can get deep throws from ge_frombytes_vartime if input isn't valid
+ catch (...) { return false; }
+ }
+
+ bool verBulletproofPlus(const std::vector<const BulletproofPlus*> &proofs)
+ {
+ try { return bulletproof_plus_VERIFY(proofs); }
+ // we can get deep throws from ge_frombytes_vartime if input isn't valid
+ catch (...) { return false; }
+ }
+
//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;
@@ -611,6 +668,25 @@ namespace rct {
kv.push_back(p.t);
}
}
+ else if (rv.type == RCTTypeBulletproofPlus)
+ {
+ kv.reserve((6*2+6) * rv.p.bulletproofs_plus.size());
+ for (const auto &p: rv.p.bulletproofs_plus)
+ {
+ // V are not hashed as they're expanded from outPk.mask
+ // (and thus hashed as part of rctSigBase above)
+ kv.push_back(p.A);
+ kv.push_back(p.A1);
+ kv.push_back(p.B);
+ kv.push_back(p.r1);
+ kv.push_back(p.s1);
+ kv.push_back(p.d1);
+ 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]);
+ }
+ }
else
{
kv.reserve((64*3+1) * rv.p.rangeSigs.size());
@@ -1031,7 +1107,7 @@ namespace rct {
//mask amount and mask
rv.ecdhInfo[i].mask = copy(outSk[i].mask);
rv.ecdhInfo[i].amount = d2h(amounts[i]);
- hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
+ hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus);
}
//set txn fee
@@ -1063,7 +1139,7 @@ namespace rct {
//RCT simple
//for post-rct only
rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev) {
- const bool bulletproof = rct_config.range_proof_type != RangeProofBorromean;
+ const bool bulletproof_or_plus = rct_config.range_proof_type > RangeProofBorromean;
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");
@@ -1079,11 +1155,14 @@ namespace rct {
}
rctSig rv;
- if (bulletproof)
+ if (bulletproof_or_plus)
{
switch (rct_config.bp_version)
{
case 0:
+ case 4:
+ rv.type = RCTTypeBulletproofPlus;
+ break;
case 3:
rv.type = RCTTypeCLSAG;
break;
@@ -1102,7 +1181,7 @@ namespace rct {
rv.message = message;
rv.outPk.resize(destinations.size());
- if (!bulletproof)
+ if (!bulletproof_or_plus)
rv.p.rangeSigs.resize(destinations.size());
rv.ecdhInfo.resize(destinations.size());
@@ -1114,17 +1193,19 @@ namespace rct {
//add destination to sig
rv.outPk[i].dest = copy(destinations[i]);
//compute range proof
- if (!bulletproof)
+ if (!bulletproof_or_plus)
rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]);
#ifdef DBG
- if (!bulletproof)
+ if (!bulletproof_or_plus)
CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof");
#endif
}
rv.p.bulletproofs.clear();
- if (bulletproof)
+ rv.p.bulletproofs_plus.clear();
+ if (bulletproof_or_plus)
{
+ const bool plus = rv.type == RCTTypeBulletproofPlus;
size_t n_amounts = outamounts.size();
size_t amounts_proved = 0;
if (rct_config.range_proof_type == RangeProofPaddedBulletproof)
@@ -1133,14 +1214,23 @@ namespace rct {
if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE)
{
// use a fake bulletproof for speed
- rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts, C, masks));
+ if (plus)
+ rv.p.bulletproofs_plus.push_back(make_dummy_bulletproof_plus(outamounts, C, masks));
+ else
+ rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts, C, masks));
}
else
{
const epee::span<const key> keys{&amount_keys[0], amount_keys.size()};
- rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts, keys, hwdev));
+ if (plus)
+ rv.p.bulletproofs_plus.push_back(proveRangeBulletproofPlus(C, masks, outamounts, keys, hwdev));
+ else
+ rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts, keys, hwdev));
#ifdef DBG
- CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
+ if (plus)
+ CHECK_AND_ASSERT_THROW_MES(verBulletproofPlus(rv.p.bulletproofs_plus.back()), "verBulletproofPlus failed on newly created proof");
+ else
+ CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
#endif
}
for (i = 0; i < outamounts.size(); ++i)
@@ -1153,7 +1243,7 @@ namespace rct {
{
size_t batch_size = 1;
if (rct_config.range_proof_type == RangeProofMultiOutputBulletproof)
- while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS)
+ while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= (plus ? BULLETPROOF_PLUS_MAX_OUTPUTS : BULLETPROOF_MAX_OUTPUTS))
batch_size *= 2;
rct::keyV C, masks;
std::vector<uint64_t> batch_amounts(batch_size);
@@ -1162,14 +1252,23 @@ namespace rct {
if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE)
{
// use a fake bulletproof for speed
- rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts, C, masks));
+ if (plus)
+ rv.p.bulletproofs_plus.push_back(make_dummy_bulletproof_plus(batch_amounts, C, masks));
+ else
+ rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts, C, masks));
}
else
{
const epee::span<const key> keys{&amount_keys[amounts_proved], batch_size};
- rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts, keys, hwdev));
+ if (plus)
+ rv.p.bulletproofs_plus.push_back(proveRangeBulletproofPlus(C, masks, batch_amounts, keys, hwdev));
+ else
+ rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts, keys, hwdev));
#ifdef DBG
- CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
+ if (plus)
+ CHECK_AND_ASSERT_THROW_MES(verBulletproofPlus(rv.p.bulletproofs_plus.back()), "verBulletproofPlus failed on newly created proof");
+ else
+ CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
#endif
}
for (i = 0; i < batch_size; ++i)
@@ -1189,7 +1288,7 @@ namespace rct {
//mask amount and mask
rv.ecdhInfo[i].mask = copy(outSk[i].mask);
rv.ecdhInfo[i].amount = d2h(outamounts[i]);
- hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
+ hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus);
}
//set txn fee
@@ -1197,9 +1296,9 @@ namespace rct {
// TODO: unused ??
// key txnFeeKey = scalarmultH(d2h(rv.txnFee));
rv.mixRing = mixRing;
- keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
+ keyV &pseudoOuts = bulletproof_or_plus ? rv.p.pseudoOuts : rv.pseudoOuts;
pseudoOuts.resize(inamounts.size());
- if (rv.type == RCTTypeCLSAG)
+ if (is_rct_clsag(rv.type))
rv.p.CLSAGs.resize(inamounts.size());
else
rv.p.MGs.resize(inamounts.size());
@@ -1218,11 +1317,11 @@ namespace rct {
if (msout)
{
msout->c.resize(inamounts.size());
- msout->mu_p.resize(rv.type == RCTTypeCLSAG ? inamounts.size() : 0);
+ msout->mu_p.resize(is_rct_clsag(rv.type) ? inamounts.size() : 0);
}
for (i = 0 ; i < inamounts.size(); i++)
{
- if (rv.type == RCTTypeCLSAG)
+ if (is_rct_clsag(rv.type))
{
rv.p.CLSAGs[i] = proveRctCLSAGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, msout ? &msout->mu_p[i] : NULL, index[i], hwdev);
}
@@ -1328,20 +1427,25 @@ namespace rct {
tools::threadpool& tpool = tools::threadpool::getInstance();
tools::threadpool::waiter waiter(tpool);
std::deque<bool> results;
- std::vector<const Bulletproof*> proofs;
+ std::vector<const Bulletproof*> bp_proofs;
+ std::vector<const BulletproofPlus*> bpp_proofs;
size_t max_non_bp_proofs = 0, offset = 0;
for (const rctSig *rvp: rvv)
{
CHECK_AND_ASSERT_MES(rvp, false, "rctSig pointer is NULL");
const rctSig &rv = *rvp;
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG,
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus,
false, "verRctSemanticsSimple called on non simple rctSig");
const bool bulletproof = is_rct_bulletproof(rv.type);
- if (bulletproof)
+ const bool bulletproof_plus = is_rct_bulletproof_plus(rv.type);
+ if (bulletproof || bulletproof_plus)
{
- CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs");
- if (rv.type == RCTTypeCLSAG)
+ if (bulletproof_plus)
+ CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_plus_amounts(rv.p.bulletproofs_plus), false, "Mismatched sizes of outPk and bulletproofs_plus");
+ else
+ CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs");
+ if (is_rct_clsag(rv.type))
{
CHECK_AND_ASSERT_MES(rv.p.MGs.empty(), false, "MGs are not empty for CLSAG");
CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.CLSAGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.CLSAGs");
@@ -1361,7 +1465,7 @@ namespace rct {
}
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
- if (!bulletproof)
+ if (!bulletproof && !bulletproof_plus)
max_non_bp_proofs += rv.p.rangeSigs.size();
}
@@ -1371,7 +1475,8 @@ namespace rct {
const rctSig &rv = *rvp;
const bool bulletproof = is_rct_bulletproof(rv.type);
- const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
+ const bool bulletproof_plus = is_rct_bulletproof_plus(rv.type);
+ const keyV &pseudoOuts = bulletproof || bulletproof_plus ? rv.p.pseudoOuts : rv.pseudoOuts;
rct::keyV masks(rv.outPk.size());
for (size_t i = 0; i < rv.outPk.size(); i++) {
@@ -1391,10 +1496,15 @@ namespace rct {
return false;
}
- if (bulletproof)
+ if (bulletproof_plus)
+ {
+ for (size_t i = 0; i < rv.p.bulletproofs_plus.size(); i++)
+ bpp_proofs.push_back(&rv.p.bulletproofs_plus[i]);
+ }
+ else if (bulletproof)
{
for (size_t i = 0; i < rv.p.bulletproofs.size(); i++)
- proofs.push_back(&rv.p.bulletproofs[i]);
+ bp_proofs.push_back(&rv.p.bulletproofs[i]);
}
else
{
@@ -1403,9 +1513,18 @@ namespace rct {
offset += rv.p.rangeSigs.size();
}
}
- if (!proofs.empty() && !verBulletproof(proofs))
+ if (!bpp_proofs.empty() && !verBulletproofPlus(bpp_proofs))
+ {
+ LOG_PRINT_L1("Aggregate range proof verified failed");
+ if (!waiter.wait())
+ return false;
+ return false;
+ }
+ if (!bp_proofs.empty() && !verBulletproof(bp_proofs))
{
LOG_PRINT_L1("Aggregate range proof verified failed");
+ if (!waiter.wait())
+ return false;
return false;
}
@@ -1445,11 +1564,12 @@ namespace rct {
{
PERF_TIMER(verRctNonSemanticsSimple);
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG,
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus,
false, "verRctNonSemanticsSimple called on non simple rctSig");
const bool bulletproof = is_rct_bulletproof(rv.type);
+ const bool bulletproof_plus = is_rct_bulletproof_plus(rv.type);
// semantics check is early, and mixRing/MGs aren't resolved yet
- if (bulletproof)
+ if (bulletproof || bulletproof_plus)
CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.p.pseudoOuts and mixRing");
else
CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing");
@@ -1460,7 +1580,7 @@ namespace rct {
tools::threadpool& tpool = tools::threadpool::getInstance();
tools::threadpool::waiter waiter(tpool);
- const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
+ const keyV &pseudoOuts = bulletproof || bulletproof_plus ? rv.p.pseudoOuts : rv.pseudoOuts;
const key message = get_pre_mlsag_hash(rv, hw::get_device("default"));
@@ -1468,10 +1588,8 @@ namespace rct {
results.resize(rv.mixRing.size());
for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
tpool.submit(&waiter, [&, i] {
- if (rv.type == RCTTypeCLSAG)
- {
+ if (is_rct_clsag(rv.type))
results[i] = verRctCLSAGSimple(message, rv.p.CLSAGs[i], rv.mixRing[i], pseudoOuts[i]);
- }
else
results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]);
});
@@ -1518,7 +1636,7 @@ namespace rct {
//mask amount and mask
ecdhTuple ecdh_info = rv.ecdhInfo[i];
- hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
+ hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus);
mask = ecdh_info.mask;
key amount = ecdh_info.amount;
key C = rv.outPk[i].mask;
@@ -1542,13 +1660,14 @@ namespace rct {
}
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask, hw::device &hwdev) {
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG, false, "decodeRct called on non simple rctSig");
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus,
+ false, "decodeRct called on non simple rctSig");
CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
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];
- hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
+ hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG || rv.type == RCTTypeBulletproofPlus);
mask = ecdh_info.mask;
key amount = ecdh_info.amount;
key C = rv.outPk[i].mask;
@@ -1574,6 +1693,7 @@ namespace rct {
bool signMultisigMLSAG(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2,
false, "unsupported rct type");
+ CHECK_AND_ASSERT_MES(!is_rct_clsag(rv.type), false, "CLSAG signature type in MLSAG signature function");
CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes");
CHECK_AND_ASSERT_MES(k.size() == rv.p.MGs.size(), false, "Mismatched k/MGs size");
CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size");
@@ -1598,7 +1718,7 @@ namespace rct {
}
bool signMultisigCLSAG(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeCLSAG, false, "unsupported rct type");
+ CHECK_AND_ASSERT_MES(is_rct_clsag(rv.type), false, "unsupported rct type");
CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes");
CHECK_AND_ASSERT_MES(k.size() == rv.p.CLSAGs.size(), false, "Mismatched k/CLSAGs size");
CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size");
@@ -1620,7 +1740,7 @@ namespace rct {
}
bool signMultisig(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
- if (rv.type == RCTTypeCLSAG)
+ if (is_rct_clsag(rv.type))
return signMultisigCLSAG(rv, indices, k, msout, secret_key);
else
return signMultisigMLSAG(rv, indices, k, msout, secret_key);
diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp
index 1f674056d..c22b0524f 100644
--- a/src/ringct/rctTypes.cpp
+++ b/src/ringct/rctTypes.cpp
@@ -196,6 +196,7 @@ namespace rct {
case RCTTypeBulletproof:
case RCTTypeBulletproof2:
case RCTTypeCLSAG:
+ case RCTTypeBulletproofPlus:
return true;
default:
return false;
@@ -215,6 +216,17 @@ namespace rct {
}
}
+ bool is_rct_bulletproof_plus(int type)
+ {
+ switch (type)
+ {
+ case RCTTypeBulletproofPlus:
+ return true;
+ default:
+ return false;
+ }
+ }
+
bool is_rct_borromean(int type)
{
switch (type)
@@ -227,19 +239,34 @@ namespace rct {
}
}
- size_t n_bulletproof_amounts(const Bulletproof &proof)
+ bool is_rct_clsag(int type)
{
- CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size");
- CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), 0, "Mismatched bulletproof L/R size");
+ switch (type)
+ {
+ case RCTTypeCLSAG:
+ case RCTTypeBulletproofPlus:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static size_t n_bulletproof_amounts_base(const size_t L_size, const size_t R_size, const size_t V_size, const size_t max_outputs)
+ {
+ CHECK_AND_ASSERT_MES(L_size >= 6, 0, "Invalid bulletproof L size");
+ CHECK_AND_ASSERT_MES(L_size == R_size, 0, "Mismatched bulletproof L/R size");
static const size_t extra_bits = 4;
- static_assert((1 << extra_bits) == BULLETPROOF_MAX_OUTPUTS, "log2(BULLETPROOF_MAX_OUTPUTS) is out of date");
- CHECK_AND_ASSERT_MES(proof.L.size() <= 6 + extra_bits, 0, "Invalid bulletproof L size");
- CHECK_AND_ASSERT_MES(proof.V.size() <= (1u<<(proof.L.size()-6)), 0, "Invalid bulletproof V/L");
- CHECK_AND_ASSERT_MES(proof.V.size() * 2 > (1u<<(proof.L.size()-6)), 0, "Invalid bulletproof V/L");
- CHECK_AND_ASSERT_MES(proof.V.size() > 0, 0, "Empty bulletproof");
- return proof.V.size();
+ CHECK_AND_ASSERT_MES((1 << extra_bits) == max_outputs, 0, "log2(max_outputs) is out of date");
+ CHECK_AND_ASSERT_MES(L_size <= 6 + extra_bits, 0, "Invalid bulletproof L size");
+ CHECK_AND_ASSERT_MES(V_size <= (1u<<(L_size-6)), 0, "Invalid bulletproof V/L");
+ CHECK_AND_ASSERT_MES(V_size * 2 > (1u<<(L_size-6)), 0, "Invalid bulletproof V/L");
+ CHECK_AND_ASSERT_MES(V_size > 0, 0, "Empty bulletproof");
+ return V_size;
}
+ size_t n_bulletproof_amounts(const Bulletproof &proof) { return n_bulletproof_amounts_base(proof.L.size(), proof.R.size(), proof.V.size(), BULLETPROOF_MAX_OUTPUTS); }
+ size_t n_bulletproof_plus_amounts(const BulletproofPlus &proof) { return n_bulletproof_amounts_base(proof.L.size(), proof.R.size(), proof.V.size(), BULLETPROOF_PLUS_MAX_OUTPUTS); }
+
size_t n_bulletproof_amounts(const std::vector<Bulletproof> &proofs)
{
size_t n = 0;
@@ -254,15 +281,31 @@ namespace rct {
return n;
}
- size_t n_bulletproof_max_amounts(const Bulletproof &proof)
+ size_t n_bulletproof_plus_amounts(const std::vector<BulletproofPlus> &proofs)
+ {
+ size_t n = 0;
+ for (const BulletproofPlus &proof: proofs)
+ {
+ size_t n2 = n_bulletproof_plus_amounts(proof);
+ CHECK_AND_ASSERT_MES(n2 < std::numeric_limits<uint32_t>::max() - n, 0, "Invalid number of bulletproofs");
+ if (n2 == 0)
+ return 0;
+ n += n2;
+ }
+ return n;
+ }
+
+ static size_t n_bulletproof_max_amounts_base(size_t L_size, size_t R_size, size_t max_outputs)
{
- CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size");
- CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), 0, "Mismatched bulletproof L/R size");
+ CHECK_AND_ASSERT_MES(L_size >= 6, 0, "Invalid bulletproof L size");
+ CHECK_AND_ASSERT_MES(L_size == R_size, 0, "Mismatched bulletproof L/R size");
static const size_t extra_bits = 4;
- static_assert((1 << extra_bits) == BULLETPROOF_MAX_OUTPUTS, "log2(BULLETPROOF_MAX_OUTPUTS) is out of date");
- CHECK_AND_ASSERT_MES(proof.L.size() <= 6 + extra_bits, 0, "Invalid bulletproof L size");
- return 1 << (proof.L.size() - 6);
+ CHECK_AND_ASSERT_MES((1 << extra_bits) == max_outputs, 0, "log2(max_outputs) is out of date");
+ CHECK_AND_ASSERT_MES(L_size <= 6 + extra_bits, 0, "Invalid bulletproof L size");
+ return 1 << (L_size - 6);
}
+ size_t n_bulletproof_max_amounts(const Bulletproof &proof) { return n_bulletproof_max_amounts_base(proof.L.size(), proof.R.size(), BULLETPROOF_MAX_OUTPUTS); }
+ size_t n_bulletproof_plus_max_amounts(const BulletproofPlus &proof) { return n_bulletproof_max_amounts_base(proof.L.size(), proof.R.size(), BULLETPROOF_PLUS_MAX_OUTPUTS); }
size_t n_bulletproof_max_amounts(const std::vector<Bulletproof> &proofs)
{
@@ -278,4 +321,18 @@ namespace rct {
return n;
}
+ size_t n_bulletproof_plus_max_amounts(const std::vector<BulletproofPlus> &proofs)
+ {
+ size_t n = 0;
+ for (const BulletproofPlus &proof: proofs)
+ {
+ size_t n2 = n_bulletproof_plus_max_amounts(proof);
+ CHECK_AND_ASSERT_MES(n2 < std::numeric_limits<uint32_t>::max() - n, 0, "Invalid number of bulletproofs");
+ if (n2 == 0)
+ return 0;
+ n += n2;
+ }
+ return n;
+ }
+
}
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index dc69990f0..59ed4d6a6 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -245,8 +245,7 @@ namespace rct {
rct::key r1, s1, d1;
rct::keyV L, R;
- BulletproofPlus():
- A({}), A1({}), B({}), r1({}), s1({}), d1({}) {}
+ 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):
@@ -276,6 +275,11 @@ namespace rct {
size_t n_bulletproof_amounts(const std::vector<Bulletproof> &proofs);
size_t n_bulletproof_max_amounts(const std::vector<Bulletproof> &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<BulletproofPlus> &proofs);
+ size_t n_bulletproof_plus_max_amounts(const std::vector<BulletproofPlus> &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
@@ -290,6 +294,7 @@ namespace rct {
RCTTypeBulletproof = 3,
RCTTypeBulletproof2 = 4,
RCTTypeCLSAG = 5,
+ RCTTypeBulletproofPlus = 6,
};
enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof };
struct RCTConfig {
@@ -318,7 +323,7 @@ namespace rct {
FIELD(type)
if (type == RCTTypeNull)
return ar.good();
- if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG)
+ if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeBulletproofPlus)
return false;
VARINT_FIELD(txnFee)
// inputs/outputs not saved, only here for serialization help
@@ -347,7 +352,7 @@ namespace rct {
return false;
for (size_t i = 0; i < outputs; ++i)
{
- if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG)
+ if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus)
{
ar.begin_object();
if (!typename Archive<W>::is_saving())
@@ -393,6 +398,7 @@ namespace rct {
struct rctSigPrunable {
std::vector<rangeSig> rangeSigs;
std::vector<Bulletproof> bulletproofs;
+ std::vector<BulletproofPlus> bulletproofs_plus;
std::vector<mgSig> MGs; // simple rct has N, full has 1
std::vector<clsag> CLSAGs;
keyV pseudoOuts; //C - for simple rct
@@ -409,9 +415,28 @@ namespace rct {
return false;
if (type == RCTTypeNull)
return ar.good();
- if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG)
+ if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeBulletproofPlus)
return false;
- if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG)
+ 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)
{
uint32_t nbp = bulletproofs.size();
if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG)
@@ -449,7 +474,7 @@ namespace rct {
ar.end_array();
}
- if (type == RCTTypeCLSAG)
+ if (type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus)
{
ar.tag("CLSAGs");
ar.begin_array();
@@ -540,7 +565,7 @@ namespace rct {
}
ar.end_array();
}
- if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG)
+ if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus)
{
ar.tag("pseudoOuts");
ar.begin_array();
@@ -561,6 +586,7 @@ namespace rct {
BEGIN_SERIALIZE_OBJECT()
FIELD(rangeSigs)
FIELD(bulletproofs)
+ FIELD(bulletproofs_plus)
FIELD(MGs)
FIELD(CLSAGs)
FIELD(pseudoOuts)
@@ -571,12 +597,12 @@ namespace rct {
keyV& get_pseudo_outs()
{
- return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG ? p.pseudoOuts : pseudoOuts;
+ return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus ? p.pseudoOuts : pseudoOuts;
}
keyV const& get_pseudo_outs() const
{
- return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG ? p.pseudoOuts : pseudoOuts;
+ return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus ? p.pseudoOuts : pseudoOuts;
}
BEGIN_SERIALIZE_OBJECT()
@@ -688,7 +714,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; }
@@ -744,6 +772,7 @@ VARIANT_TAG(debug_archive, rct::Bulletproof, "rct::bulletproof");
VARIANT_TAG(debug_archive, rct::multisig_kLRki, "rct::multisig_kLRki");
VARIANT_TAG(debug_archive, rct::multisig_out, "rct::multisig_out");
VARIANT_TAG(debug_archive, rct::clsag, "rct::clsag");
+VARIANT_TAG(debug_archive, rct::BulletproofPlus, "rct::bulletproof_plus");
VARIANT_TAG(binary_archive, rct::key, 0x90);
VARIANT_TAG(binary_archive, rct::key64, 0x91);
@@ -761,6 +790,7 @@ 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);
VARIANT_TAG(json_archive, rct::key, "rct_key");
VARIANT_TAG(json_archive, rct::key64, "rct_key64");
@@ -778,5 +808,6 @@ VARIANT_TAG(json_archive, rct::Bulletproof, "rct_bulletproof");
VARIANT_TAG(json_archive, rct::multisig_kLRki, "rct_multisig_kLR");
VARIANT_TAG(json_archive, rct::multisig_out, "rct_multisig_out");
VARIANT_TAG(json_archive, rct::clsag, "rct_clsag");
+VARIANT_TAG(json_archive, rct::BulletproofPlus, "rct_bulletproof_plus");
#endif /* RCTTYPES_H */
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
index b03da1edc..bd715dcfd 100644
--- a/src/serialization/json_object.cpp
+++ b/src/serialization/json_object.cpp
@@ -300,7 +300,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx)
}
const auto& rsig = tx.rct_signatures;
- if (!cryptonote::is_coinbase(tx) && rsig.p.bulletproofs.empty() && rsig.p.rangeSigs.empty() && rsig.p.MGs.empty() && rsig.get_pseudo_outs().empty() && sigs == val.MemberEnd())
+ if (!cryptonote::is_coinbase(tx) && rsig.p.bulletproofs.empty() && rsig.p.bulletproofs_plus.empty() && rsig.p.rangeSigs.empty() && rsig.p.MGs.empty() && rsig.get_pseudo_outs().empty() && sigs == val.MemberEnd())
tx.pruned = true;
}
@@ -1100,13 +1100,14 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig&
}
// prunable
- if (!sig.p.bulletproofs.empty() || !sig.p.rangeSigs.empty() || !sig.p.MGs.empty() || !sig.get_pseudo_outs().empty())
+ if (!sig.p.bulletproofs.empty() || !sig.p.bulletproofs_plus.empty() || !sig.p.rangeSigs.empty() || !sig.p.MGs.empty() || !sig.get_pseudo_outs().empty())
{
dest.Key("prunable");
dest.StartObject();
INSERT_INTO_JSON_OBJECT(dest, range_proofs, sig.p.rangeSigs);
INSERT_INTO_JSON_OBJECT(dest, bulletproofs, sig.p.bulletproofs);
+ INSERT_INTO_JSON_OBJECT(dest, bulletproofs_plus, sig.p.bulletproofs_plus);
INSERT_INTO_JSON_OBJECT(dest, mlsags, sig.p.MGs);
INSERT_INTO_JSON_OBJECT(dest, pseudo_outs, sig.get_pseudo_outs());
@@ -1141,6 +1142,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig)
GET_FROM_JSON_OBJECT(prunable->value, sig.p.rangeSigs, range_proofs);
GET_FROM_JSON_OBJECT(prunable->value, sig.p.bulletproofs, bulletproofs);
+ GET_FROM_JSON_OBJECT(prunable->value, sig.p.bulletproofs_plus, bulletproofs_plus);
GET_FROM_JSON_OBJECT(prunable->value, sig.p.MGs, mlsags);
GET_FROM_JSON_OBJECT(prunable->value, pseudo_outs, pseudo_outs);
@@ -1150,6 +1152,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig)
{
sig.p.rangeSigs.clear();
sig.p.bulletproofs.clear();
+ sig.p.bulletproofs_plus.clear();
sig.p.MGs.clear();
sig.get_pseudo_outs().clear();
}
@@ -1258,6 +1261,41 @@ void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p)
GET_FROM_JSON_OBJECT(val, p.t, t);
}
+void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::BulletproofPlus& p)
+{
+ dest.StartObject();
+
+ INSERT_INTO_JSON_OBJECT(dest, V, p.V);
+ INSERT_INTO_JSON_OBJECT(dest, A, p.A);
+ INSERT_INTO_JSON_OBJECT(dest, A1, p.A1);
+ INSERT_INTO_JSON_OBJECT(dest, B, p.B);
+ INSERT_INTO_JSON_OBJECT(dest, r1, p.r1);
+ INSERT_INTO_JSON_OBJECT(dest, s1, p.s1);
+ INSERT_INTO_JSON_OBJECT(dest, d1, p.d1);
+ INSERT_INTO_JSON_OBJECT(dest, L, p.L);
+ INSERT_INTO_JSON_OBJECT(dest, R, p.R);
+
+ dest.EndObject();
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::BulletproofPlus& 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.A1, A1);
+ GET_FROM_JSON_OBJECT(val, p.B, B);
+ GET_FROM_JSON_OBJECT(val, p.r1, r1);
+ GET_FROM_JSON_OBJECT(val, p.s1, s1);
+ GET_FROM_JSON_OBJECT(val, p.d1, d1);
+ GET_FROM_JSON_OBJECT(val, p.L, L);
+ GET_FROM_JSON_OBJECT(val, p.R, R);
+}
+
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::boroSig& sig)
{
dest.StartObject();
diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h
index c858faf5a..4514ad568 100644
--- a/src/serialization/json_object.h
+++ b/src/serialization/json_object.h
@@ -292,6 +292,9 @@ void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig);
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::Bulletproof& p);
void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p);
+void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::BulletproofPlus& p);
+void fromJsonValue(const rapidjson::Value& val, rct::BulletproofPlus& p);
+
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::boroSig& sig);
void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig);
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index b058619a3..87242b79c 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -1742,6 +1742,7 @@ uint64_t WalletImpl::estimateTransactionFee(const std::vector<std::pair<std::str
extra_size,
m_wallet->use_fork_rules(8, 0),
m_wallet->use_fork_rules(HF_VERSION_CLSAG, 0),
+ m_wallet->use_fork_rules(HF_VERSION_BULLETPROOF_PLUS, 0),
m_wallet->get_base_fee(),
m_wallet->get_fee_multiplier(m_wallet->adjust_priority(static_cast<uint32_t>(priority))),
m_wallet->get_fee_quantization_mask());
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 9e95a26c1..c13e8edbf 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -781,7 +781,7 @@ void drop_from_short_history(std::list<crypto::hash> &short_chain_history, size_
}
}
-size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
+size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus)
{
size_t size = 0;
@@ -805,12 +805,12 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
size += 1;
// rangeSigs
- if (bulletproof)
+ if (bulletproof || bulletproof_plus)
{
size_t log_padded_outputs = 0;
while ((1<<log_padded_outputs) < n_outputs)
++log_padded_outputs;
- size += (2 * (6 + log_padded_outputs) + 4 + 5) * 32 + 3;
+ size += (2 * (6 + log_padded_outputs) + (bulletproof_plus ? 6 : (4 + 5))) * 32 + 3;
}
else
size += (2*64*32+32+64*32) * n_outputs;
@@ -833,29 +833,29 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
// txnFee
size += 4;
- LOG_PRINT_L2("estimated " << (bulletproof ? "bulletproof" : "borromean") << " rct tx size for " << n_inputs << " inputs with ring size " << (mixin+1) << " and " << n_outputs << " outputs: " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
+ LOG_PRINT_L2("estimated " << (bulletproof_plus ? "bulletproof plus" : bulletproof ? "bulletproof" : "borromean") << " rct tx size for " << n_inputs << " inputs with ring size " << (mixin+1) << " and " << n_outputs << " outputs: " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
return size;
}
-size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
+size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus)
{
if (use_rct)
- return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
+ return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
else
return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
}
-uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
+uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus)
{
- size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
- if (use_rct && bulletproof && n_outputs > 2)
+ size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
+ if (use_rct && (bulletproof || bulletproof_plus) && n_outputs > 2)
{
- const uint64_t bp_base = 368;
+ const uint64_t bp_base = (32 * ((bulletproof_plus ? 6 : 9) + 7 * 2)) / 2; // notional size of a 2 output proof, normalized to 1 proof (ie, divided by 2)
size_t log_padded_outputs = 2;
while ((1<<log_padded_outputs) < n_outputs)
++log_padded_outputs;
uint64_t nlr = 2 * (6 + log_padded_outputs);
- const uint64_t bp_size = 32 * (9 + nlr);
+ const uint64_t bp_size = 32 * ((bulletproof_plus ? 6 : 9) + nlr);
const uint64_t bp_clawback = (bp_base * (1<<log_padded_outputs) - bp_size) * 4 / 5;
MDEBUG("clawback on size " << size << ": " << bp_clawback);
size += bp_clawback;
@@ -868,6 +868,11 @@ uint8_t get_bulletproof_fork()
return 8;
}
+uint8_t get_bulletproof_plus_fork()
+{
+ return HF_VERSION_BULLETPROOF_PLUS;
+}
+
uint8_t get_clsag_fork()
{
return HF_VERSION_CLSAG;
@@ -1816,6 +1821,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
case rct::RCTTypeBulletproof:
case rct::RCTTypeBulletproof2:
case rct::RCTTypeCLSAG:
+ case rct::RCTTypeBulletproofPlus:
return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev);
case rct::RCTTypeFull:
return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev);
@@ -7216,16 +7222,16 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
return sign_multisig_tx_to_file(exported_txs, filename, txids);
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const
+uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const
{
if (use_per_byte_fee)
{
- const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
+ const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
}
else
{
- const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
+ const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
}
}
@@ -8946,8 +8952,8 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
ptx.construction_data.unlock_time = unlock_time;
ptx.construction_data.use_rct = true;
ptx.construction_data.rct_config = {
- tx.rct_signatures.p.bulletproofs.empty() ? rct::RangeProofBorromean : rct::RangeProofPaddedBulletproof,
- use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1
+ rct::RangeProofPaddedBulletproof,
+ use_fork_rules(HF_VERSION_BULLETPROOF_PLUS, -10) ? 4 : 3
};
ptx.construction_data.dests = dsts;
// record which subaddress indices are being used as inputs
@@ -9642,10 +9648,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
const bool use_rct = use_fork_rules(4, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
const rct::RCTConfig rct_config {
- bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean,
- bulletproof ? (use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0
+ rct::RangeProofPaddedBulletproof,
+ bulletproof_plus ? 4 : 3
};
const uint64_t base_fee = get_base_fee();
@@ -9681,7 +9688,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// early out if we know we can't make it anyway
// we could also check for being within FEE_PER_KB, but if the fee calculation
// ever changes, this might be missed, so let this go through
- const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof, clsag));
+ const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus));
uint64_t balance_subtotal = 0;
uint64_t unlocked_balance_subtotal = 0;
for (uint32_t index_minor : subaddr_indices)
@@ -9699,8 +9706,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
LOG_PRINT_L2("Candidate subaddress index for spending: " << i);
// determine threshold for fractional amount
- const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag);
- const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag);
+ const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
+ const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
@@ -9797,7 +9804,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
// will get us a known fee.
- uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, base_fee, fee_multiplier, fee_quantization_mask);
+ uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_multiplier, fee_quantization_mask);
preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices);
if (!preferred_inputs.empty())
{
@@ -9910,7 +9917,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
+ while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
{
// we can fully pay that destination
LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
@@ -9927,7 +9934,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
++original_output_index;
}
- if (!out_slots_exhausted && available_amount > 0 && !dsts.empty() && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
+ if (!out_slots_exhausted && available_amount > 0 && !dsts.empty() &&
+ estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
// we can partially fill that destination
LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
" for " << print_money(available_amount) << "/" << print_money(dsts[0].amount));
@@ -9965,7 +9973,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag);
+ const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus);
try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
THROW_WALLET_EXCEPTION_IF(try_tx && tx.dsts.empty(), error::tx_too_big, estimated_rct_tx_weight, upper_transaction_weight_limit);
}
@@ -9976,7 +9984,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
pending_tx test_ptx;
const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers);
- needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, base_fee, fee_multiplier, fee_quantization_mask);
+ needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_multiplier, fee_quantization_mask);
auto try_carving_from_partial_payment = [&](uint64_t needed_fee, uint64_t available_for_fee)
{
@@ -10236,11 +10244,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
// determine threshold for fractional amount
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
- const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag);
- const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag);
+ const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
+ const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag, bulletproof_plus);
THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
@@ -10346,10 +10355,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE);
const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
const rct::RCTConfig rct_config {
- bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean,
- bulletproof ? (use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0,
+ rct::RangeProofPaddedBulletproof,
+ bulletproof_plus ? 4 : 3
};
const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
@@ -10378,7 +10388,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
uint64_t fee_dust_threshold;
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
{
- const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag);
+ const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus);
fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_multiplier, fee_quantization_mask);
}
else
@@ -10409,7 +10419,7 @@ std::vector<wallet2::pending_tx> 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_weight_limit);
- const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof, clsag);
+ const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof, clsag, bulletproof_plus);
bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
if (try_tx) {
@@ -10417,7 +10427,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
pending_tx test_ptx;
const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers);
- needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, base_fee, fee_multiplier, fee_quantization_mask);
+ needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, bulletproof_plus, base_fee, fee_multiplier, fee_quantization_mask);
// add N - 1 outputs for correct initial fee estimation
for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i)
@@ -11279,7 +11289,7 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
crypto::secret_key scalar1;
crypto::derivation_to_scalar(found_derivation, n, scalar1);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
- rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
+ rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
const rct::key C = tx.rct_signatures.outPk[n].mask;
rct::key Ctmp;
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask");
@@ -11932,7 +11942,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
crypto::secret_key shared_secret;
crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx];
- rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
+ rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
amount = rct::h2d(ecdh_info.amount);
}
total += amount;
@@ -14066,9 +14076,10 @@ std::pair<size_t, uint64_t> wallet2::estimate_tx_size_and_weight(bool use_rct, i
n_outputs = 2; // extra dummy output
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ const bool bulletproof_plus = use_fork_rules(get_bulletproof_plus_fork(), 0);
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
- size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag);
- uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag);
+ size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
+ uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus);
return std::make_pair(size, weight);
}
//----------------------------------------------------------------------------------------------------
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index d64832b13..04d5277ce 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -1386,7 +1386,7 @@ private:
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels);
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees);
- uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const;
+ uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const;
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
uint64_t get_base_fee();
uint64_t get_fee_quantization_mask();
diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt
index 7455639ca..c8583c476 100644
--- a/tests/core_tests/CMakeLists.txt
+++ b/tests/core_tests/CMakeLists.txt
@@ -44,6 +44,7 @@ set(core_tests_sources
v2_tests.cpp
rct.cpp
bulletproofs.cpp
+ bulletproof_plus.cpp
rct2.cpp
wallet_tools.cpp)
@@ -65,6 +66,7 @@ set(core_tests_headers
v2_tests.h
rct.h
bulletproofs.h
+ bulletproof_plus.h
rct2.h
wallet_tools.h)
diff --git a/tests/core_tests/bulletproof_plus.cpp b/tests/core_tests/bulletproof_plus.cpp
new file mode 100644
index 000000000..c3879e646
--- /dev/null
+++ b/tests/core_tests/bulletproof_plus.cpp
@@ -0,0 +1,373 @@
+// Copyright (c) 2014-2020, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#include "ringct/rctSigs.h"
+#include "ringct/bulletproofs_plus.h"
+#include "chaingen.h"
+#include "bulletproof_plus.h"
+#include "device/device.hpp"
+
+using namespace epee;
+using namespace crypto;
+using namespace cryptonote;
+
+//----------------------------------------------------------------------------------------------------------------------
+// Tests
+
+bool gen_bpp_tx_validation_base::generate_with(std::vector<test_event_entry>& events,
+ size_t mixin, size_t n_txes, const uint64_t *amounts_paid, bool valid, const rct::RCTConfig *rct_config, uint8_t hf_version,
+ const std::function<bool(std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations, size_t tx_idx)> &pre_tx,
+ const std::function<bool(transaction &tx, size_t tx_idx)> &post_tx) const
+{
+ uint64_t ts_start = 1338224400;
+
+ GENERATE_ACCOUNT(miner_account);
+ MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
+
+ // create 12 miner accounts, and have them mine the next 12 blocks
+ cryptonote::account_base miner_accounts[12];
+ const cryptonote::block *prev_block = &blk_0;
+ cryptonote::block blocks[12 + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW];
+ for (size_t n = 0; n < 12; ++n) {
+ miner_accounts[n].generate();
+ CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, miner_accounts[n],
+ test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version,
+ 2, 2, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
+ crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 0, 2),
+ false, "Failed to generate block");
+ events.push_back(blocks[n]);
+ prev_block = blocks + n;
+ }
+
+ // rewind
+ cryptonote::block blk_r, blk_last;
+ {
+ blk_last = blocks[11];
+ for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i)
+ {
+ CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[12+i], blk_last, miner_account,
+ test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version,
+ 2, 2, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
+ crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 0, 2),
+ false, "Failed to generate block");
+ events.push_back(blocks[12+i]);
+ blk_last = blocks[12+i];
+ }
+ blk_r = blk_last;
+ }
+
+ // create 4 txes from these miners in another block, to generate some rct outputs
+ std::vector<transaction> rct_txes;
+ cryptonote::block blk_txes;
+ std::vector<crypto::hash> starting_rct_tx_hashes;
+ uint64_t fees = 0;
+ static const uint64_t input_amounts_available[] = {5000000000000, 30000000000000, 100000000000, 80000000000};
+ for (size_t n = 0; n < n_txes; ++n)
+ {
+ std::vector<tx_source_entry> sources;
+
+ sources.resize(1);
+ tx_source_entry& src = sources.back();
+
+ const uint64_t needed_amount = input_amounts_available[n];
+ src.amount = input_amounts_available[n];
+ size_t real_index_in_tx = 0;
+ for (size_t m = 0; m <= mixin; ++m) {
+ size_t index_in_tx = 0;
+ for (size_t i = 0; i < blocks[m].miner_tx.vout.size(); ++i)
+ if (blocks[m].miner_tx.vout[i].amount == needed_amount)
+ index_in_tx = i;
+ CHECK_AND_ASSERT_MES(blocks[m].miner_tx.vout[index_in_tx].amount == needed_amount, false, "Expected amount not found");
+ src.push_output(m, boost::get<txout_to_key>(blocks[m].miner_tx.vout[index_in_tx].target).key, src.amount);
+ if (m == n)
+ real_index_in_tx = index_in_tx;
+ }
+ src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[n].miner_tx);
+ src.real_output = n;
+ src.real_output_in_tx_index = real_index_in_tx;
+ src.mask = rct::identity();
+ src.rct = false;
+
+ //fill outputs entry
+ tx_destination_entry td;
+ td.addr = miner_accounts[n].get_keys().m_account_address;
+ std::vector<tx_destination_entry> destinations;
+ for (int o = 0; amounts_paid[o] != (uint64_t)-1; ++o)
+ {
+ td.amount = amounts_paid[o];
+ destinations.push_back(td);
+ }
+
+ if (pre_tx && !pre_tx(sources, destinations, n))
+ {
+ MDEBUG("pre_tx returned failure");
+ return false;
+ }
+
+ crypto::secret_key tx_key;
+ std::vector<crypto::secret_key> additional_tx_keys;
+ std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
+ subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0};
+ rct_txes.resize(rct_txes.size() + 1);
+ bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), 0, tx_key, additional_tx_keys, true, rct_config[n]);
+ CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
+
+ if (post_tx && !post_tx(rct_txes.back(), n))
+ {
+ MDEBUG("post_tx returned failure");
+ return false;
+ }
+
+ //events.push_back(rct_txes.back());
+ starting_rct_tx_hashes.push_back(get_transaction_hash(rct_txes.back()));
+ LOG_PRINT_L0("Test tx: " << obj_to_json_str(rct_txes.back()));
+
+ for (int o = 0; amounts_paid[o] != (uint64_t)-1; ++o)
+ {
+ crypto::key_derivation derivation;
+ bool r = crypto::generate_key_derivation(destinations[o].addr.m_view_public_key, tx_key, derivation);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
+ crypto::secret_key amount_key;
+ crypto::derivation_to_scalar(derivation, o, amount_key);
+ rct::key rct_tx_mask;
+ const uint8_t type = rct_txes.back().rct_signatures.type;
+ if (rct::is_rct_simple(type))
+ rct::decodeRctSimple(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default"));
+ else
+ rct::decodeRct(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default"));
+ }
+
+ while (amounts_paid[0] != (size_t)-1)
+ ++amounts_paid;
+ ++amounts_paid;
+
+ uint64_t fee = 0;
+ get_tx_fee(rct_txes.back(), fee);
+ fees += fee;
+ }
+ if (!valid)
+ DO_CALLBACK(events, "mark_invalid_tx");
+ events.push_back(rct_txes);
+
+ CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk_txes, blk_last, miner_account,
+ test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_tx_hashes | test_generator::bf_hf_version | test_generator::bf_max_outs | test_generator::bf_tx_fees,
+ hf_version, hf_version, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
+ crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, 6, hf_version, fees),
+ false, "Failed to generate block");
+ if (!valid)
+ DO_CALLBACK(events, "mark_invalid_block");
+ events.push_back(blk_txes);
+ blk_last = blk_txes;
+
+ return true;
+}
+
+bool gen_bpp_tx_validation_base::check_bpp(const cryptonote::transaction &tx, size_t tx_idx, const size_t *sizes, const char *context) const
+{
+ DEFINE_TESTS_ERROR_CONTEXT(context);
+ CHECK_TEST_CONDITION(tx.version >= 2);
+ CHECK_TEST_CONDITION(rct::is_rct_bulletproof_plus(tx.rct_signatures.type));
+ size_t n_sizes = 0, n_amounts = 0;
+ for (size_t n = 0; n < tx_idx; ++n)
+ {
+ while (sizes[0] != (size_t)-1)
+ ++sizes;
+ ++sizes;
+ }
+ while (sizes[n_sizes] != (size_t)-1)
+ n_amounts += sizes[n_sizes++];
+ CHECK_TEST_CONDITION(tx.rct_signatures.p.bulletproofs_plus.size() == n_sizes);
+ CHECK_TEST_CONDITION(rct::n_bulletproof_plus_max_amounts(tx.rct_signatures.p.bulletproofs_plus) == n_amounts);
+ for (size_t n = 0; n < n_sizes; ++n)
+ CHECK_TEST_CONDITION(rct::n_bulletproof_plus_max_amounts(tx.rct_signatures.p.bulletproofs_plus[n]) == sizes[n]);
+ return true;
+}
+
+bool gen_bpp_tx_invalid_before_fork::generate(std::vector<test_event_entry>& events) const
+{
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
+ const size_t bp_sizes[] = {2, (size_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 4 } };
+ return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_BULLETPROOF_PLUS - 1, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bpp(tx, tx_idx, bp_sizes, "gen_bpp_tx_invalid_before_fork"); });
+}
+
+bool gen_bpp_tx_valid_at_fork::generate(std::vector<test_event_entry>& events) const
+{
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
+ const size_t bp_sizes[] = {2, (size_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 4 } };
+ return generate_with(events, mixin, 1, amounts_paid, true, rct_config, HF_VERSION_BULLETPROOF_PLUS, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bpp(tx, tx_idx, bp_sizes, "gen_bpp_tx_valid_at_fork"); });
+}
+
+bool gen_bpp_tx_invalid_1_1::generate(std::vector<test_event_entry>& events) const
+{
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof , 4 } };
+ return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_BULLETPROOF_PLUS, NULL, NULL);
+}
+
+bool gen_bpp_tx_valid_2::generate(std::vector<test_event_entry>& events) const
+{
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
+ const size_t bp_sizes[] = {2, (size_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 4 } };
+ return generate_with(events, mixin, 1, amounts_paid, true, rct_config, HF_VERSION_BULLETPROOF_PLUS, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bpp(tx, tx_idx, bp_sizes, "gen_bpp_tx_valid_2"); });
+}
+
+bool gen_bpp_tx_valid_3::generate(std::vector<test_event_entry>& events) const
+{
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {5000, 5000, 5000, (uint64_t)-1};
+ const size_t bp_sizes[] = {4, (size_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof , 4 } };
+ return generate_with(events, mixin, 1, amounts_paid, true, rct_config, HF_VERSION_BULLETPROOF_PLUS, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bpp(tx, tx_idx, bp_sizes, "gen_bpp_tx_valid_3"); });
+}
+
+bool gen_bpp_tx_valid_16::generate(std::vector<test_event_entry>& events) const
+{
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, (uint64_t)-1};
+ const size_t bp_sizes[] = {16, (size_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof , 4 } };
+ return generate_with(events, mixin, 1, amounts_paid, true, rct_config, HF_VERSION_BULLETPROOF_PLUS, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bpp(tx, tx_idx, bp_sizes, "gen_bpp_tx_valid_16"); });
+}
+
+bool gen_bpp_tx_invalid_4_2_1::generate(std::vector<test_event_entry>& events) const
+{
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofMultiOutputBulletproof , 4 } };
+ return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_BULLETPROOF_PLUS, NULL, NULL);
+}
+
+bool gen_bpp_tx_invalid_16_16::generate(std::vector<test_event_entry>& events) const
+{
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofMultiOutputBulletproof , 4 } };
+ return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_BULLETPROOF_PLUS, NULL, NULL);
+}
+
+bool gen_bpp_txs_valid_2_and_2::generate(std::vector<test_event_entry>& events) const
+{
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {1000, 1000, (size_t)-1, 1000, 1000, (uint64_t)-1};
+ const size_t bp_sizes[] = {2, (size_t)-1, 2, (size_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 4 }, {rct::RangeProofPaddedBulletproof, 4 } };
+ return generate_with(events, mixin, 2, amounts_paid, true, rct_config, HF_VERSION_BULLETPROOF_PLUS, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bpp(tx, tx_idx, bp_sizes, "gen_bpp_txs_valid_2_and_2"); });
+}
+
+bool gen_bpp_txs_invalid_2_and_8_2_and_16_16_1::generate(std::vector<test_event_entry>& events) const
+{
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1};
+ const rct::RCTConfig rct_config[] = {{rct::RangeProofMultiOutputBulletproof, 4}, {rct::RangeProofMultiOutputBulletproof, 4}, {rct::RangeProofMultiOutputBulletproof, 4}};
+ return generate_with(events, mixin, 3, amounts_paid, false, rct_config, HF_VERSION_BULLETPROOF_PLUS, NULL, NULL);
+}
+
+bool gen_bpp_txs_valid_2_and_3_and_2_and_4::generate(std::vector<test_event_entry>& events) const
+{
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {11111115000, 11111115000, (uint64_t)-1, 11111115000, 11111115000, 11111115001, (uint64_t)-1, 11111115000, 11111115002, (uint64_t)-1, 11111115000, 11111115000, 11111115000, 11111115003, (uint64_t)-1};
+ const rct::RCTConfig rct_config[] = {{rct::RangeProofPaddedBulletproof, 4}, {rct::RangeProofPaddedBulletproof, 4}, {rct::RangeProofPaddedBulletproof, 4}, {rct::RangeProofPaddedBulletproof, 4}};
+ const size_t bp_sizes[] = {2, (size_t)-1, 4, (size_t)-1, 2, (size_t)-1, 4, (size_t)-1};
+ return generate_with(events, mixin, 4, amounts_paid, true, rct_config, HF_VERSION_BULLETPROOF_PLUS, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx) { return check_bpp(tx, tx_idx, bp_sizes, "gen_bpp_txs_valid_2_and_3_and_2_and_4"); });
+}
+
+bool gen_bpp_tx_invalid_not_enough_proofs::generate(std::vector<test_event_entry>& events) const
+{
+ DEFINE_TESTS_ERROR_CONTEXT("gen_bpp_tx_invalid_not_enough_proofs");
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 4 } };
+ return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_BULLETPROOF_PLUS, NULL, [&](cryptonote::transaction &tx, size_t idx){
+ CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
+ CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs_plus.empty());
+ tx.rct_signatures.p.bulletproofs_plus.pop_back();
+ CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs_plus.empty());
+ return true;
+ });
+}
+
+bool gen_bpp_tx_invalid_empty_proofs::generate(std::vector<test_event_entry>& events) const
+{
+ DEFINE_TESTS_ERROR_CONTEXT("gen_bpp_tx_invalid_empty_proofs");
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {50000, 50000, (uint64_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 4 } };
+ return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_BULLETPROOF_PLUS, NULL, [&](cryptonote::transaction &tx, size_t idx){
+ CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
+ tx.rct_signatures.p.bulletproofs_plus.clear();
+ return true;
+ });
+}
+
+bool gen_bpp_tx_invalid_too_many_proofs::generate(std::vector<test_event_entry>& events) const
+{
+ DEFINE_TESTS_ERROR_CONTEXT("gen_bpp_tx_invalid_too_many_proofs");
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {10000, (uint64_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 4 } };
+ return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_BULLETPROOF_PLUS, NULL, [&](cryptonote::transaction &tx, size_t idx){
+ CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
+ CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs_plus.empty());
+ tx.rct_signatures.p.bulletproofs_plus.push_back(tx.rct_signatures.p.bulletproofs_plus.back());
+ return true;
+ });
+}
+
+bool gen_bpp_tx_invalid_wrong_amount::generate(std::vector<test_event_entry>& events) const
+{
+ DEFINE_TESTS_ERROR_CONTEXT("gen_bpp_tx_invalid_wrong_amount");
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {10000, (uint64_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 4 } };
+ return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_BULLETPROOF_PLUS, NULL, [&](cryptonote::transaction &tx, size_t idx){
+ CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
+ CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs_plus.empty());
+ tx.rct_signatures.p.bulletproofs_plus.back() = rct::bulletproof_plus_PROVE(1000, rct::skGen());
+ return true;
+ });
+}
+
+bool gen_bpp_tx_invalid_clsag_type::generate(std::vector<test_event_entry>& events) const
+{
+ DEFINE_TESTS_ERROR_CONTEXT("gen_bpp_tx_invalid_clsag_type");
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 3 } };
+ return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_BULLETPROOF_PLUS + 1, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){
+ return true;
+ });
+}
diff --git a/tests/core_tests/bulletproof_plus.h b/tests/core_tests/bulletproof_plus.h
new file mode 100644
index 000000000..481044fd4
--- /dev/null
+++ b/tests/core_tests/bulletproof_plus.h
@@ -0,0 +1,206 @@
+// Copyright (c) 2014-2020, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#pragma once
+#include "chaingen.h"
+
+struct gen_bpp_tx_validation_base : public test_chain_unit_base
+{
+ gen_bpp_tx_validation_base()
+ : m_invalid_tx_index(0)
+ , m_invalid_block_index(0)
+ {
+ REGISTER_CALLBACK_METHOD(gen_bpp_tx_validation_base, mark_invalid_tx);
+ REGISTER_CALLBACK_METHOD(gen_bpp_tx_validation_base, mark_invalid_block);
+ }
+
+ bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& /*tx*/)
+ {
+ if (m_invalid_tx_index == event_idx)
+ return tvc.m_verifivation_failed;
+ else
+ return !tvc.m_verifivation_failed && tx_added;
+ }
+
+ bool check_tx_verification_context_array(const std::vector<cryptonote::tx_verification_context>& tvcs, size_t tx_added, size_t event_idx, const std::vector<cryptonote::transaction>& /*txs*/)
+ {
+ size_t failed = 0;
+ for (const cryptonote::tx_verification_context &tvc: tvcs)
+ if (tvc.m_verifivation_failed)
+ ++failed;
+ if (m_invalid_tx_index == event_idx)
+ return failed > 0;
+ else
+ return failed == 0 && tx_added == tvcs.size();
+ }
+
+ bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*block*/)
+ {
+ if (m_invalid_block_index == event_idx)
+ return bvc.m_verifivation_failed;
+ else
+ return !bvc.m_verifivation_failed;
+ }
+
+ bool mark_invalid_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
+ {
+ m_invalid_block_index = ev_index + 1;
+ return true;
+ }
+
+ bool mark_invalid_tx(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
+ {
+ m_invalid_tx_index = ev_index + 1;
+ return true;
+ }
+
+ bool generate_with(std::vector<test_event_entry>& events, size_t mixin,
+ size_t n_txes, const uint64_t *amounts_paid, bool valid, const rct::RCTConfig *rct_config, uint8_t hf_version,
+ const std::function<bool(std::vector<cryptonote::tx_source_entry> &sources, std::vector<cryptonote::tx_destination_entry> &destinations, size_t)> &pre_tx,
+ const std::function<bool(cryptonote::transaction &tx, size_t)> &post_tx) const;
+
+ bool check_bpp(const cryptonote::transaction &tx, size_t tx_idx, const size_t *sizes, const char *context) const;
+
+private:
+ size_t m_invalid_tx_index;
+ size_t m_invalid_block_index;
+};
+
+template<>
+struct get_test_options<gen_bpp_tx_validation_base> {
+ const std::pair<uint8_t, uint64_t> hard_forks[4] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(HF_VERSION_BULLETPROOF_PLUS, 73), std::make_pair(0, 0)};
+ const cryptonote::test_options test_options = {
+ hard_forks, 0
+ };
+};
+
+template<uint8_t test_version = 1>
+struct get_bpp_versioned_test_options: public get_test_options<gen_bpp_tx_validation_base> {
+ const std::pair<uint8_t, uint64_t> hard_forks[4] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(test_version, 73), std::make_pair(0, 0)};
+ const cryptonote::test_options test_options = {
+ hard_forks, 0
+ };
+};
+
+struct gen_bpp_tx_invalid_before_fork : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_tx_invalid_before_fork>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS - 1> {};
+
+struct gen_bpp_tx_valid_at_fork : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_tx_valid_at_fork>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS> {};
+
+struct gen_bpp_tx_invalid_1_1 : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_tx_invalid_1_1>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS> {};
+
+struct gen_bpp_tx_valid_2 : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_tx_valid_2>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS> {};
+
+struct gen_bpp_tx_valid_3 : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_tx_valid_3>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS> {};
+
+struct gen_bpp_tx_valid_16 : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_tx_valid_16>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS> {};
+
+struct gen_bpp_tx_invalid_4_2_1 : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_tx_invalid_4_2_1>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS> {};
+
+struct gen_bpp_tx_invalid_16_16 : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_tx_invalid_16_16>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS> {};
+
+struct gen_bpp_txs_valid_2_and_2 : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_txs_valid_2_and_2>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS> {};
+
+struct gen_bpp_txs_invalid_2_and_8_2_and_16_16_1 : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_txs_invalid_2_and_8_2_and_16_16_1>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS> {};
+
+struct gen_bpp_txs_valid_2_and_3_and_2_and_4 : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_txs_valid_2_and_3_and_2_and_4>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS> {};
+
+struct gen_bpp_tx_invalid_not_enough_proofs : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_tx_invalid_not_enough_proofs>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS> {};
+
+struct gen_bpp_tx_invalid_empty_proofs : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_tx_invalid_empty_proofs>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS> {};
+
+struct gen_bpp_tx_invalid_too_many_proofs : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_tx_invalid_too_many_proofs>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS> {};
+
+struct gen_bpp_tx_invalid_wrong_amount : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_tx_invalid_wrong_amount>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS> {};
+
+struct gen_bpp_tx_invalid_clsag_type : public gen_bpp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bpp_tx_invalid_clsag_type>: public get_bpp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS + 1> {};
diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp
index c46b5e657..e7b42405b 100644
--- a/tests/core_tests/bulletproofs.cpp
+++ b/tests/core_tests/bulletproofs.cpp
@@ -158,7 +158,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
crypto::derivation_to_scalar(derivation, o, amount_key);
rct::key rct_tx_mask;
const uint8_t type = rct_txes.back().rct_signatures.type;
- if (type == rct::RCTTypeSimple || type == rct::RCTTypeBulletproof || type == rct::RCTTypeBulletproof2 || type == rct::RCTTypeCLSAG)
+ if (rct::is_rct_simple(type))
rct::decodeRctSimple(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default"));
else
rct::decodeRct(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default"));
@@ -232,7 +232,7 @@ bool gen_bp_tx_invalid_1_1::generate(std::vector<test_event_entry>& events) cons
{
const size_t mixin = 10;
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
- const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof , 0 } };
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof , 3 } };
return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_CLSAG, NULL, NULL);
}
@@ -241,7 +241,7 @@ bool gen_bp_tx_valid_2::generate(std::vector<test_event_entry>& events) const
const size_t mixin = 10;
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
const size_t bp_sizes[] = {2, (size_t)-1};
- const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 0 } };
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 3 } };
return generate_with(events, mixin, 1, amounts_paid, true, rct_config, HF_VERSION_CLSAG, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_2"); });
}
@@ -250,7 +250,7 @@ bool gen_bp_tx_valid_3::generate(std::vector<test_event_entry>& events) const
const size_t mixin = 10;
const uint64_t amounts_paid[] = {5000, 5000, 5000, (uint64_t)-1};
const size_t bp_sizes[] = {4, (size_t)-1};
- const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof , 0 } };
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 3 } };
return generate_with(events, mixin, 1, amounts_paid, true, rct_config, HF_VERSION_CLSAG, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_3"); });
}
@@ -259,7 +259,7 @@ bool gen_bp_tx_valid_16::generate(std::vector<test_event_entry>& events) const
const size_t mixin = 10;
const uint64_t amounts_paid[] = {500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, (uint64_t)-1};
const size_t bp_sizes[] = {16, (size_t)-1};
- const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof , 0 } };
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 3 } };
return generate_with(events, mixin, 1, amounts_paid, true, rct_config, HF_VERSION_CLSAG, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_16"); });
}
@@ -267,7 +267,7 @@ bool gen_bp_tx_invalid_4_2_1::generate(std::vector<test_event_entry>& events) co
{
const size_t mixin = 10;
const uint64_t amounts_paid[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1};
- const rct::RCTConfig rct_config[] = { { rct::RangeProofMultiOutputBulletproof , 0 } };
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofMultiOutputBulletproof, 3 } };
return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_CLSAG, NULL, NULL);
}
@@ -275,7 +275,7 @@ bool gen_bp_tx_invalid_16_16::generate(std::vector<test_event_entry>& events) co
{
const size_t mixin = 10;
const uint64_t amounts_paid[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1};
- const rct::RCTConfig rct_config[] = { { rct::RangeProofMultiOutputBulletproof , 0 } };
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofMultiOutputBulletproof, 3 } };
return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_CLSAG, NULL, NULL);
}
@@ -284,7 +284,7 @@ bool gen_bp_txs_valid_2_and_2::generate(std::vector<test_event_entry>& events) c
const size_t mixin = 10;
const uint64_t amounts_paid[] = {1000, 1000, (size_t)-1, 1000, 1000, (uint64_t)-1};
const size_t bp_sizes[] = {2, (size_t)-1, 2, (size_t)-1};
- const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 0 }, {rct::RangeProofPaddedBulletproof, 0 } };
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 3 }, {rct::RangeProofPaddedBulletproof, 3 } };
return generate_with(events, mixin, 2, amounts_paid, true, rct_config, HF_VERSION_CLSAG, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_and_2"); });
}
@@ -292,7 +292,7 @@ bool gen_bp_txs_invalid_2_and_8_2_and_16_16_1::generate(std::vector<test_event_e
{
const size_t mixin = 10;
const uint64_t amounts_paid[] = {1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1};
- const rct::RCTConfig rct_config[] = {{rct::RangeProofMultiOutputBulletproof, 0}, {rct::RangeProofMultiOutputBulletproof, 0}, {rct::RangeProofMultiOutputBulletproof, 0}};
+ const rct::RCTConfig rct_config[] = {{rct::RangeProofMultiOutputBulletproof, 3}, {rct::RangeProofMultiOutputBulletproof, 3}, {rct::RangeProofMultiOutputBulletproof, 3}};
return generate_with(events, mixin, 3, amounts_paid, false, rct_config, HF_VERSION_CLSAG, NULL, NULL);
}
@@ -300,7 +300,7 @@ bool gen_bp_txs_valid_2_and_3_and_2_and_4::generate(std::vector<test_event_entry
{
const size_t mixin = 10;
const uint64_t amounts_paid[] = {11111115000, 11111115000, (uint64_t)-1, 11111115000, 11111115000, 11111115001, (uint64_t)-1, 11111115000, 11111115002, (uint64_t)-1, 11111115000, 11111115000, 11111115000, 11111115003, (uint64_t)-1};
- const rct::RCTConfig rct_config[] = {{rct::RangeProofPaddedBulletproof, 0}, {rct::RangeProofPaddedBulletproof, 0}, {rct::RangeProofPaddedBulletproof, 0}, {rct::RangeProofPaddedBulletproof, 0}};
+ const rct::RCTConfig rct_config[] = {{rct::RangeProofPaddedBulletproof, 3}, {rct::RangeProofPaddedBulletproof, 3}, {rct::RangeProofPaddedBulletproof, 3}, {rct::RangeProofPaddedBulletproof, 3}};
const size_t bp_sizes[] = {2, (size_t)-1, 4, (size_t)-1, 2, (size_t)-1, 4, (size_t)-1};
return generate_with(events, mixin, 4, amounts_paid, true, rct_config, HF_VERSION_CLSAG, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx) { return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_and_3_and_2_and_4"); });
}
@@ -310,7 +310,7 @@ bool gen_bp_tx_invalid_not_enough_proofs::generate(std::vector<test_event_entry>
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_not_enough_proofs");
const size_t mixin = 10;
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
- const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 0 } };
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 3 } };
return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_CLSAG, NULL, [&](cryptonote::transaction &tx, size_t idx){
CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof || tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty());
@@ -325,7 +325,7 @@ bool gen_bp_tx_invalid_empty_proofs::generate(std::vector<test_event_entry>& eve
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_empty_proofs");
const size_t mixin = 10;
const uint64_t amounts_paid[] = {50000, 50000, (uint64_t)-1};
- const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 0 } };
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 3 } };
return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_CLSAG, NULL, [&](cryptonote::transaction &tx, size_t idx){
CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof || tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
tx.rct_signatures.p.bulletproofs.clear();
@@ -338,7 +338,7 @@ bool gen_bp_tx_invalid_too_many_proofs::generate(std::vector<test_event_entry>&
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_too_many_proofs");
const size_t mixin = 10;
const uint64_t amounts_paid[] = {10000, (uint64_t)-1};
- const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 0 } };
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 3 } };
return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_CLSAG, NULL, [&](cryptonote::transaction &tx, size_t idx){
CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof || tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty());
@@ -352,7 +352,7 @@ bool gen_bp_tx_invalid_wrong_amount::generate(std::vector<test_event_entry>& eve
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_wrong_amount");
const size_t mixin = 10;
const uint64_t amounts_paid[] = {10000, (uint64_t)-1};
- const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 0 } };
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof, 3 } };
return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_CLSAG, NULL, [&](cryptonote::transaction &tx, size_t idx){
CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof || tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty());
@@ -366,7 +366,7 @@ bool gen_bp_tx_invalid_borromean_type::generate(std::vector<test_event_entry>& e
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_borromean_type");
const size_t mixin = 10;
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
- const rct::RCTConfig rct_config[] = { { rct::RangeProofBorromean, 0 } };
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofBorromean, 3 } };
return generate_with(events, mixin, 1, amounts_paid, false, rct_config, 11, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){
return true;
});
@@ -382,3 +382,14 @@ bool gen_bp_tx_invalid_bulletproof2_type::generate(std::vector<test_event_entry>
return true;
});
}
+
+bool gen_bp_tx_invalid_clsag_type::generate(std::vector<test_event_entry>& events) const
+{
+ DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_clsag_type");
+ const size_t mixin = 10;
+ const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
+ const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 3 } };
+ return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_BULLETPROOF_PLUS + 1, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){
+ return true;
+ });
+}
diff --git a/tests/core_tests/bulletproofs.h b/tests/core_tests/bulletproofs.h
index b30d82e68..115ad49b5 100644
--- a/tests/core_tests/bulletproofs.h
+++ b/tests/core_tests/bulletproofs.h
@@ -211,3 +211,9 @@ struct gen_bp_tx_invalid_bulletproof2_type : public gen_bp_tx_validation_base
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_invalid_bulletproof2_type>: public get_bp_versioned_test_options<HF_VERSION_CLSAG + 1> {};
+
+struct gen_bp_tx_invalid_clsag_type : public gen_bp_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_bp_tx_invalid_clsag_type>: public get_bp_versioned_test_options<HF_VERSION_BULLETPROOF_PLUS + 1> {};
diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp
index c55154917..c573eb995 100644
--- a/tests/core_tests/chaingen_main.cpp
+++ b/tests/core_tests/chaingen_main.cpp
@@ -265,6 +265,24 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_bp_tx_invalid_wrong_amount);
GENERATE_AND_PLAY(gen_bp_tx_invalid_borromean_type);
GENERATE_AND_PLAY(gen_bp_tx_invalid_bulletproof2_type);
+ GENERATE_AND_PLAY(gen_bp_tx_invalid_clsag_type);
+
+ GENERATE_AND_PLAY(gen_bpp_tx_invalid_before_fork);
+ GENERATE_AND_PLAY(gen_bpp_tx_valid_at_fork);
+ GENERATE_AND_PLAY(gen_bpp_tx_invalid_1_1);
+ GENERATE_AND_PLAY(gen_bpp_tx_valid_2);
+ GENERATE_AND_PLAY(gen_bpp_tx_valid_3);
+ GENERATE_AND_PLAY(gen_bpp_tx_valid_16);
+ GENERATE_AND_PLAY(gen_bpp_tx_invalid_4_2_1);
+ GENERATE_AND_PLAY(gen_bpp_tx_invalid_16_16);
+ GENERATE_AND_PLAY(gen_bpp_txs_valid_2_and_2);
+ GENERATE_AND_PLAY(gen_bpp_txs_invalid_2_and_8_2_and_16_16_1);
+ GENERATE_AND_PLAY(gen_bpp_txs_valid_2_and_3_and_2_and_4);
+ GENERATE_AND_PLAY(gen_bpp_tx_invalid_not_enough_proofs);
+ GENERATE_AND_PLAY(gen_bpp_tx_invalid_empty_proofs);
+ GENERATE_AND_PLAY(gen_bpp_tx_invalid_too_many_proofs);
+ GENERATE_AND_PLAY(gen_bpp_tx_invalid_wrong_amount);
+ GENERATE_AND_PLAY(gen_bpp_tx_invalid_clsag_type);
GENERATE_AND_PLAY(gen_rct2_tx_clsag_malleability);
diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h
index db78c3e41..1a5489b9e 100644
--- a/tests/core_tests/chaingen_tests_list.h
+++ b/tests/core_tests/chaingen_tests_list.h
@@ -43,6 +43,7 @@
#include "rct.h"
#include "multisig.h"
#include "bulletproofs.h"
+#include "bulletproof_plus.h"
#include "rct2.h"
/************************************************************************/
/* */
diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp
index 73bc1f104..f8b4f25c1 100644
--- a/tests/core_tests/multisig.cpp
+++ b/tests/core_tests/multisig.cpp
@@ -165,7 +165,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
account_base &account = n < inputs ? miner_account[creator] : miner_accounts[n];
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, account,
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version | test_generator::bf_max_outs,
- 10, 10, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
+ HF_VERSION_BULLETPROOF_PLUS, HF_VERSION_BULLETPROOF_PLUS, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 1, 4),
false, "Failed to generate block");
events.push_back(blocks[n]);
@@ -181,7 +181,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
cryptonote::block blk;
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_accounts[0],
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version | test_generator::bf_max_outs,
- 10, 10, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
+ HF_VERSION_BULLETPROOF_PLUS, HF_VERSION_BULLETPROOF_PLUS, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 1, 4),
false, "Failed to generate block");
events.push_back(blk);
@@ -339,6 +339,11 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
td.amount = amount_paid;
std::vector<tx_destination_entry> destinations;
destinations.push_back(td);
+ cryptonote::account_base dummy;
+ dummy.generate();
+ td.addr = dummy.get_keys().m_account_address;
+ td.amount = 0;
+ destinations.push_back(td);
if (pre_tx)
pre_tx(sources, destinations);
@@ -353,7 +358,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
#endif
std::vector<crypto::secret_key> additional_tx_secret_keys;
auto sources_copy = sources;
- r = construct_tx_and_get_tx_key(miner_account[creator].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_secret_keys, true, { rct::RangeProofPaddedBulletproof, 2 }, msoutp);
+ r = construct_tx_and_get_tx_key(miner_account[creator].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_secret_keys, true, { rct::RangeProofPaddedBulletproof, 0 }, msoutp);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
#ifndef NO_MULTISIG
@@ -443,7 +448,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
crypto::secret_key scalar1;
crypto::derivation_to_scalar(derivation, n, scalar1);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
- rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
+ rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
rct::key C = tx.rct_signatures.outPk[n].mask;
rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
CHECK_AND_ASSERT_MES(rct::equalKeys(C, Ctmp), false, "Failed to decode amount");
diff --git a/tests/core_tests/multisig.h b/tests/core_tests/multisig.h
index 333c4fe38..c7376bf63 100644
--- a/tests/core_tests/multisig.h
+++ b/tests/core_tests/multisig.h
@@ -82,7 +82,7 @@ private:
template<>
struct get_test_options<gen_multisig_tx_validation_base> {
- const std::pair<uint8_t, uint64_t> hard_forks[3] = {std::make_pair(1, 0), std::make_pair(10, 1), std::make_pair(0, 0)};
+ const std::pair<uint8_t, uint64_t> hard_forks[3] = {std::make_pair(1, 0), std::make_pair(HF_VERSION_BULLETPROOF_PLUS, 1), std::make_pair(0, 0)};
const cryptonote::test_options test_options = {
hard_forks, 0
};
diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp
index 6ce99e76e..035128177 100644
--- a/tests/core_tests/rct.cpp
+++ b/tests/core_tests/rct.cpp
@@ -133,7 +133,7 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry
crypto::secret_key amount_key;
crypto::derivation_to_scalar(derivation, o, amount_key);
const uint8_t type = rct_txes[n].rct_signatures.type;
- if (type == rct::RCTTypeSimple || type == rct::RCTTypeBulletproof || type == rct::RCTTypeBulletproof2 || type == rct::RCTTypeCLSAG)
+ if (rct::is_rct_simple(type))
rct::decodeRctSimple(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default"));
else
rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default"));
diff --git a/tests/core_tests/rct2.cpp b/tests/core_tests/rct2.cpp
index 8d7c4b3eb..4772be38c 100644
--- a/tests/core_tests/rct2.cpp
+++ b/tests/core_tests/rct2.cpp
@@ -158,7 +158,7 @@ bool gen_rct2_tx_validation_base::generate_with(std::vector<test_event_entry>& e
crypto::derivation_to_scalar(derivation, o, amount_key);
rct::key rct_tx_mask;
const uint8_t type = rct_txes.back().rct_signatures.type;
- if (type == rct::RCTTypeSimple || type == rct::RCTTypeBulletproof || type == rct::RCTTypeBulletproof2 || type == rct::RCTTypeCLSAG)
+ if (rct::is_rct_simple(type))
rct::decodeRctSimple(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default"));
else
rct::decodeRct(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default"));
diff --git a/tests/performance_tests/bulletproof_plus.h b/tests/performance_tests/bulletproof_plus.h
index b436e5e12..9aad61065 100644
--- a/tests/performance_tests/bulletproof_plus.h
+++ b/tests/performance_tests/bulletproof_plus.h
@@ -72,7 +72,6 @@ public:
size_t o = start;
for (size_t n = 0; n < N; ++n)
{
- //printf("adding %zu times %zu\n", repeat, o);
for (size_t i = 0; i < repeat; ++i)
proofs.push_back(rct::bulletproof_plus_PROVE(std::vector<uint64_t>(o, 749327532984), rct::skvGen(o)));
o = o * mul + add;
diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp
index ee617938c..43a359a59 100644
--- a/tests/unit_tests/bulletproofs.cpp
+++ b/tests/unit_tests/bulletproofs.cpp
@@ -131,7 +131,7 @@ TEST(bulletproofs, multi_splitting)
}
rct::ctkeyV outSk;
- rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 0 };
+ rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 4 };
rct::rctSig s = rct::genRctSimple(rct::zero(), sc, destinations, inamounts, outamounts, available, mixRing, amount_keys, NULL, NULL, index, outSk, rct_config, hw::get_device("default"));
ASSERT_TRUE(rct::verRctSimple(s));
for (size_t i = 0; i < n_outputs; ++i)