support old BP and BP+

pull/436/head
wowario 1 year ago
parent e72451bdb3
commit a04b346103
No known key found for this signature in database
GPG Key ID: 24DCBE762DE9C111

@ -241,8 +241,15 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair
} }
else else
{ {
rct::key commitment;
if (tx.version > 1)
{
commitment = tx.rct_signatures.outPk[i].mask;
if (rct::is_rct_bulletproof_plus(tx.rct_signatures.type))
commitment = rct::scalarmult8(commitment);
}
amount_output_indices[i] = add_output(tx_hash, tx.vout[i], i, tx.unlock_time, amount_output_indices[i] = add_output(tx_hash, tx.vout[i], i, tx.unlock_time,
tx.version > 1 ? &tx.rct_signatures.outPk[i].mask : NULL); tx.version > 1 ? &commitment : NULL);
} }
} }
add_tx_amount_output_indices(tx_id, amount_output_indices); add_tx_amount_output_indices(tx_id, amount_output_indices);

@ -335,7 +335,7 @@ namespace boost
a & x.type; a & x.type;
if (x.type == rct::RCTTypeNull) if (x.type == rct::RCTTypeNull)
return; return;
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) if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimpleBulletproof && x.type != rct::RCTTypeCLSAG && x.type != rct::RCTTypeBulletproofPlus)
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); 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.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 // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
@ -369,7 +369,7 @@ namespace boost
a & x.type; a & x.type;
if (x.type == rct::RCTTypeNull) if (x.type == rct::RCTTypeNull)
return; return;
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) if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimpleBulletproof && x.type != rct::RCTTypeCLSAG && x.type != rct::RCTTypeBulletproofPlus)
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); 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.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 // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
@ -389,7 +389,7 @@ namespace boost
a & x.p.MGs; a & x.p.MGs;
if (ver >= 1u) if (ver >= 1u)
a & x.p.CLSAGs; a & x.p.CLSAGs;
if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2 || x.type == rct::RCTTypeCLSAG || x.type == rct::RCTTypeBulletproofPlus) if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2 || x.type == rct::RCTTypeSimpleBulletproof || x.type == rct::RCTTypeCLSAG || x.type == rct::RCTTypeBulletproofPlus)
a & x.p.pseudoOuts; a & x.p.pseudoOuts;
} }

@ -166,7 +166,6 @@ namespace cryptonote
if (!base_only) if (!base_only)
{ {
const bool bulletproof = rct::is_rct_bulletproof(rv.type);
const bool bulletproof_plus = rct::is_rct_bulletproof_plus(rv.type); const bool bulletproof_plus = rct::is_rct_bulletproof_plus(rv.type);
if (bulletproof_plus) if (bulletproof_plus)
{ {
@ -180,7 +179,7 @@ namespace cryptonote
LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs_plus L size in tx " << get_transaction_hash(tx)); LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs_plus L size in tx " << get_transaction_hash(tx));
return false; return false;
} }
const size_t max_outputs = rct::n_bulletproof_plus_max_amounts(rv.p.bulletproofs_plus[0]); const size_t max_outputs = 1 << (rv.p.bulletproofs_plus[0].L.size() - 6);
if (max_outputs < tx.vout.size()) 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)); LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs_plus max outputs in tx " << get_transaction_hash(tx));
@ -190,9 +189,10 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(n_amounts == rv.outPk.size(), false, "Internal error filling out V"); CHECK_AND_ASSERT_MES(n_amounts == rv.outPk.size(), false, "Internal error filling out V");
rv.p.bulletproofs_plus[0].V.resize(n_amounts); rv.p.bulletproofs_plus[0].V.resize(n_amounts);
for (size_t i = 0; i < n_amounts; ++i) 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); rv.p.bulletproofs_plus[0].V[i] = rv.outPk[i].mask;
} }
else if (bulletproof) const bool bulletproof = rct::is_rct_bulletproof(rv.type);
if (rct::is_rct_new_bulletproof(rv.type))
{ {
if (rv.p.bulletproofs.size() != 1) if (rv.p.bulletproofs.size() != 1)
{ {
@ -205,7 +205,7 @@ namespace cryptonote
return false; return false;
} }
const size_t max_outputs = 1 << (rv.p.bulletproofs[0].L.size() - 6); const size_t max_outputs = 1 << (rv.p.bulletproofs[0].L.size() - 6);
if (max_outputs < tx.vout.size()) if (max_outputs < tx.vout.size() && rv.type == rct::RCTTypeBulletproof)
{ {
LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs max outputs in tx " << get_transaction_hash(tx)); LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs max outputs in tx " << get_transaction_hash(tx));
return false; return false;
@ -216,6 +216,26 @@ namespace cryptonote
for (size_t i = 0; i < n_amounts; ++i) for (size_t i = 0; i < n_amounts; ++i)
rv.p.bulletproofs[0].V[i] = rct::scalarmultKey(rv.outPk[i].mask, rct::INV_EIGHT); rv.p.bulletproofs[0].V[i] = rct::scalarmultKey(rv.outPk[i].mask, rct::INV_EIGHT);
} }
else if (bulletproof)
{
if (rct::n_bulletproof_v1_amounts(rv.p.bulletproofs) != tx.vout.size())
{
LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs size in tx " << get_transaction_hash(tx));
return false;
}
size_t idx = 0;
for (size_t n = 0; n < rv.outPk.size(); ++n)
{
//rv.p.bulletproofs[n].V.resize(1);
//rv.p.bulletproofs[n].V[0] = rv.outPk[n].mask;
CHECK_AND_ASSERT_MES(rv.p.bulletproofs[n].L.size() >= 6, false, "Bad bulletproofs L size"); // at least 64 bits
const size_t n_amounts = rct::n_bulletproof_v1_amounts(rv.p.bulletproofs[n]);
CHECK_AND_ASSERT_MES(idx + n_amounts <= rv.outPk.size(), false, "Internal error filling out V");
rv.p.bulletproofs[n].V.resize(n_amounts);
for (size_t i = 0; i < n_amounts; ++i)
rv.p.bulletproofs[n].V[i] = rv.outPk[idx++].mask;
}
}
} }
} }
return true; return true;
@ -451,6 +471,11 @@ namespace cryptonote
const bool bulletproof_plus = rct::is_rct_bulletproof_plus(rv.type); const bool bulletproof_plus = rct::is_rct_bulletproof_plus(rv.type);
if (!bulletproof && !bulletproof_plus) if (!bulletproof && !bulletproof_plus)
return blob_size; return blob_size;
const size_t n_outputs = tx.vout.size();
if (n_outputs <= 2)
return blob_size;
if (rct::is_rct_old_bulletproof(rv.type))
return blob_size;
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); 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); 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"); CHECK_AND_ASSERT_THROW_MES_L1(bp_clawback <= std::numeric_limits<uint64_t>::max() - blob_size, "Weight overflow");
@ -462,7 +487,7 @@ 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.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.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 || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus, 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"); std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support older range proof types");
CHECK_AND_ASSERT_MES(!tx.vin.empty(), std::numeric_limits<uint64_t>::max(), "empty vin"); 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"); CHECK_AND_ASSERT_MES(tx.vin[0].type() == typeid(cryptonote::txin_to_key), std::numeric_limits<uint64_t>::max(), "empty vin");
@ -485,7 +510,7 @@ namespace cryptonote
// calculate deterministic CLSAG/MLSAG data size // calculate deterministic CLSAG/MLSAG data size
const size_t ring_size = boost::get<cryptonote::txin_to_key>(tx.vin[0]).key_offsets.size(); const size_t ring_size = boost::get<cryptonote::txin_to_key>(tx.vin[0]).key_offsets.size();
if (rct::is_rct_clsag(tx.rct_signatures.type)) if (tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus)
extra = tx.vin.size() * (ring_size + 2) * 32; extra = tx.vin.size() * (ring_size + 2) * 32;
else else
extra = tx.vin.size() * (ring_size * (1 + 1) * 32 + 32 /* cc */); extra = tx.vin.size() * (ring_size * (1 + 1) * 32 + 32 /* cc */);

@ -3166,13 +3166,13 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
} }
} }
// from v11, allow bulletproofs // from v8, allow bulletproofs
if (hf_version < 11) { if (hf_version < 8) {
if (tx.version >= 2) { if (tx.version >= 2) {
const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type); const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type);
if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty()) if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty())
{ {
MERROR_VER("New Bulletproofs are not allowed before v11"); MERROR_VER("Bulletproofs are not allowed before v8");
tvc.m_invalid_output = true; tvc.m_invalid_output = true;
return false; return false;
} }
@ -3192,7 +3192,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
} }
} }
// from v10, allow bulletproofs v2 // from v13, allow bulletproofs v2
if (hf_version < HF_VERSION_SMALLER_BP) { if (hf_version < HF_VERSION_SMALLER_BP) {
if (tx.version >= 2) { if (tx.version >= 2) {
if (tx.rct_signatures.type == rct::RCTTypeBulletproof2) if (tx.rct_signatures.type == rct::RCTTypeBulletproof2)
@ -3204,7 +3204,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
} }
} }
// from v11, allow only bulletproofs v2 // from v14, allow only bulletproofs v2
if (hf_version > HF_VERSION_SMALLER_BP) { if (hf_version > HF_VERSION_SMALLER_BP) {
if (tx.version >= 2) { if (tx.version >= 2) {
if (tx.rct_signatures.type == rct::RCTTypeBulletproof) if (tx.rct_signatures.type == rct::RCTTypeBulletproof)
@ -3216,7 +3216,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
} }
} }
// from v13, allow CLSAGs // from v16, allow CLSAGs
if (hf_version < HF_VERSION_CLSAG) { if (hf_version < HF_VERSION_CLSAG) {
if (tx.version >= 2) { if (tx.version >= 2) {
if (tx.rct_signatures.type == rct::RCTTypeCLSAG) if (tx.rct_signatures.type == rct::RCTTypeCLSAG)
@ -3228,7 +3228,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
} }
} }
// from v14, allow only CLSAGs // from v17, allow only CLSAGs
if (hf_version > HF_VERSION_CLSAG) { if (hf_version > HF_VERSION_CLSAG) {
if (tx.version >= 2) { if (tx.version >= 2) {
if (tx.rct_signatures.type <= rct::RCTTypeBulletproof2) if (tx.rct_signatures.type <= rct::RCTTypeBulletproof2)
@ -3240,7 +3240,27 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
} }
} }
// from v15, allow bulletproofs plus // from v15, require view tags on outputs
if (!check_output_types(tx, hf_version))
{
tvc.m_invalid_output = true;
return false;
}
// from v12, forbid old bulletproofs
if (hf_version > 11) {
if (tx.version >= 2) {
const bool old_bulletproof = rct::is_rct_old_bulletproof(tx.rct_signatures.type);
if (old_bulletproof)
{
MERROR_VER("Old Bulletproofs are not allowed after v11");
tvc.m_invalid_output = true;
return false;
}
}
}
// from v18, allow bulletproofs plus
if (hf_version < HF_VERSION_BULLETPROOF_PLUS) { if (hf_version < HF_VERSION_BULLETPROOF_PLUS) {
if (tx.version >= 2) { if (tx.version >= 2) {
const bool bulletproof_plus = rct::is_rct_bulletproof_plus(tx.rct_signatures.type); const bool bulletproof_plus = rct::is_rct_bulletproof_plus(tx.rct_signatures.type);
@ -3253,7 +3273,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
} }
} }
// from v16, forbid bulletproofs // from v19, forbid bulletproofs
if (hf_version > HF_VERSION_BULLETPROOF_PLUS) { if (hf_version > HF_VERSION_BULLETPROOF_PLUS) {
if (tx.version >= 2) { if (tx.version >= 2) {
const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type); const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type);
@ -3266,7 +3286,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
} }
} }
// from v15, require view tags on outputs // from v20, require view tags on outputs
if (!check_output_types(tx, hf_version)) if (!check_output_types(tx, hf_version))
{ {
tvc.m_invalid_output = true; tvc.m_invalid_output = true;
@ -3298,7 +3318,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
rv.message = rct::hash2rct(tx_prefix_hash); rv.message = rct::hash2rct(tx_prefix_hash);
// mixRing - full and simple store it in opposite ways // mixRing - full and simple store it in opposite ways
if (rv.type == rct::RCTTypeFull) if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof)
{ {
CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys"); CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys[0].size()); rv.mixRing.resize(pubkeys[0].size());
@ -3313,7 +3333,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 || rv.type == rct::RCTTypeBulletproofPlus) else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeSimpleBulletproof || rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus)
{ {
CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys"); CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys.size()); rv.mixRing.resize(pubkeys.size());
@ -3332,7 +3352,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
} }
// II // II
if (rv.type == rct::RCTTypeFull) if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof)
{ {
if (!tx.pruned) if (!tx.pruned)
{ {
@ -3342,7 +3362,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image); rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
} }
} }
else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2) else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeSimpleBulletproof)
{ {
if (!tx.pruned) if (!tx.pruned)
{ {
@ -3649,6 +3669,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
return false; return false;
} }
case rct::RCTTypeSimple: case rct::RCTTypeSimple:
case rct::RCTTypeSimpleBulletproof:
case rct::RCTTypeBulletproof: case rct::RCTTypeBulletproof:
case rct::RCTTypeBulletproof2: case rct::RCTTypeBulletproof2:
case rct::RCTTypeCLSAG: case rct::RCTTypeCLSAG:
@ -3688,7 +3709,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
} }
} }
const size_t n_sigs = rct::is_rct_clsag(rv.type) ? rv.p.CLSAGs.size() : rv.p.MGs.size(); const size_t n_sigs = rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus ? rv.p.CLSAGs.size() : rv.p.MGs.size();
if (n_sigs != tx.vin.size()) if (n_sigs != tx.vin.size())
{ {
MERROR_VER("Failed to check ringct signatures: mismatched MGs/vin sizes"); MERROR_VER("Failed to check ringct signatures: mismatched MGs/vin sizes");
@ -3697,7 +3718,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
for (size_t n = 0; n < tx.vin.size(); ++n) for (size_t n = 0; n < tx.vin.size(); ++n)
{ {
bool error; bool error;
if (rct::is_rct_clsag(rv.type)) if (rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus)
error = memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.CLSAGs[n].I, 32); error = memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.CLSAGs[n].I, 32);
else 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); 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);
@ -3716,6 +3737,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
break; break;
} }
case rct::RCTTypeFull: case rct::RCTTypeFull:
case rct::RCTTypeFullBulletproof:
{ {
// check all this, either reconstructed (so should really pass), or not // check all this, either reconstructed (so should really pass), or not
{ {
@ -3782,13 +3804,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
// for bulletproofs, check they're only multi-output after v8 // for bulletproofs, check they're only multi-output after v8
if (rct::is_rct_bulletproof(rv.type)) if (rct::is_rct_bulletproof(rv.type))
{ {
if (hf_version < 11) if (hf_version < 8)
{ {
for (const rct::Bulletproof &proof: rv.p.bulletproofs) for (const rct::Bulletproof &proof: rv.p.bulletproofs)
{ {
if (proof.V.size() > 1) if (proof.V.size() > 1)
{ {
MERROR_VER("Multi output bulletproofs are invalid before v11"); MERROR_VER("Multi output bulletproofs are invalid before v8");
return false; return false;
} }
} }

@ -860,6 +860,44 @@ namespace cryptonote
return false; return false;
} }
// resolve outPk references in rct txes
// outPk aren't the only thing that need resolving for a fully resolved tx,
// but outPk (1) are needed now to check range proof semantics, and
// (2) do not need access to the blockchain to find data
if (tx.version >= 2)
{
rct::rctSig &rv = tx.rct_signatures;
if (!rct::is_rct_new_bulletproof(rv.type)){
if (rv.outPk.size() != tx.vout.size())
{
LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad outPk size in tx " << tx_hash << ", rejected");
tvc.m_verifivation_failed = true;
return false;
}
for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n)
rv.outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key);
const bool bulletproof = rct::is_rct_bulletproof(rv.type);
if (bulletproof)
{
if (rct::n_bulletproof_v1_amounts(rv.p.bulletproofs) != tx.vout.size())
{
LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad bulletproofs size in tx " << tx_hash << ", rejected");
tvc.m_verifivation_failed = true;
return false;
}
size_t idx = 0;
for (size_t n = 0; n < rv.p.bulletproofs.size(); ++n)
{
CHECK_AND_ASSERT_MES(rv.p.bulletproofs[n].L.size() >= 6, false, "Bad bulletproofs L size"); // at least 64 bits
const size_t n_amounts = rct::n_bulletproof_v1_amounts(rv.p.bulletproofs[n]);
CHECK_AND_ASSERT_MES(idx + n_amounts <= rv.outPk.size(), false, "Internal error filling out V");
rv.p.bulletproofs[n].V.clear();
for (size_t i = 0; i < n_amounts; ++i)
rv.p.bulletproofs[n].V.push_back(rv.outPk[idx++].mask);
}
}
}
}
return true; return true;
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
@ -928,6 +966,7 @@ namespace cryptonote
tx_info[n].result = false; tx_info[n].result = false;
break; break;
case rct::RCTTypeSimple: case rct::RCTTypeSimple:
case rct::RCTTypeSimpleBulletproof:
if (!rct::verRctSemanticsSimple(rv)) if (!rct::verRctSemanticsSimple(rv))
{ {
MERROR_VER("rct signature semantics check failed"); MERROR_VER("rct signature semantics check failed");
@ -938,6 +977,7 @@ namespace cryptonote
} }
break; break;
case rct::RCTTypeFull: case rct::RCTTypeFull:
case rct::RCTTypeFullBulletproof:
if (!rct::verRct(rv, true)) if (!rct::verRct(rv, true))
{ {
MERROR_VER("rct signature semantics check failed"); MERROR_VER("rct signature semantics check failed");

@ -601,7 +601,9 @@ namespace cryptonote
crypto::hash tx_prefix_hash; crypto::hash tx_prefix_hash;
get_transaction_prefix_hash(tx, tx_prefix_hash, hwdev); get_transaction_prefix_hash(tx, tx_prefix_hash, hwdev);
rct::ctkeyV outSk; rct::ctkeyV outSk;
if (use_simple_rct) if (rct_config.range_proof_type != rct::RangeProofPaddedBulletproof && use_simple_rct)
tx.rct_signatures = rct::genRctSimple_old(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk, rct_config, hwdev);
else if (use_simple_rct && rct_config.range_proof_type == rct::RangeProofPaddedBulletproof)
tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk, rct_config, hwdev); tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk, rct_config, hwdev);
else else
tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk, rct_config, hwdev); // same index assumption tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk, rct_config, hwdev); // same index assumption

@ -112,8 +112,8 @@ namespace cryptonote
uint64_t get_transaction_weight_limit(uint8_t version) uint64_t get_transaction_weight_limit(uint8_t version)
{ {
// from v8, limit a tx to 50% of the minimum block weight // from v12, limit a tx to 50% of the minimum block weight
if (version >= 8) if (version >= 12)
return get_min_block_weight(version) / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; return get_min_block_weight(version) / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
else else
return get_min_block_weight(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; return get_min_block_weight(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;

@ -1931,7 +1931,7 @@ namespace hw {
// ====== Aout, Bout, AKout, C, v, k ====== // ====== Aout, Bout, AKout, C, v, k ======
kv_offset = data_offset; kv_offset = data_offset;
if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus) { if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG) {
C_offset = kv_offset+ (8)*outputs_size; C_offset = kv_offset+ (8)*outputs_size;
} else { } else {
C_offset = kv_offset+ (32+32)*outputs_size; C_offset = kv_offset+ (32+32)*outputs_size;
@ -1948,7 +1948,7 @@ namespace hw {
offset = set_command_header(INS_VALIDATE, 0x02, i+1); offset = set_command_header(INS_VALIDATE, 0x02, i+1);
//options //options
this->buffer_send[offset] = (i==outputs_size-1)? 0x00:0x80 ; this->buffer_send[offset] = (i==outputs_size-1)? 0x00:0x80 ;
this->buffer_send[offset] |= (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus)?0x02:0x00; this->buffer_send[offset] |= (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG)?0x02:0x00;
offset += 1; offset += 1;
//is_subaddress //is_subaddress
this->buffer_send[offset] = outKeys.is_subaddress; this->buffer_send[offset] = outKeys.is_subaddress;
@ -1969,7 +1969,7 @@ namespace hw {
memmove(this->buffer_send+offset, data+C_offset,32); memmove(this->buffer_send+offset, data+C_offset,32);
offset += 32; offset += 32;
C_offset += 32; C_offset += 32;
if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus) { if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG) {
//k //k
memset(this->buffer_send+offset, 0, 32); memset(this->buffer_send+offset, 0, 32);
offset += 32; offset += 32;

@ -32,6 +32,7 @@ set(ringct_basic_sources
rctCryptoOps.c rctCryptoOps.c
multiexp.cc multiexp.cc
bulletproofs.cc bulletproofs.cc
bulletproofs2.cc
bulletproofs_plus.cc) bulletproofs_plus.cc)
monero_find_all_headers(ringct_basic_private_headers "${CMAKE_CURRENT_SOURCE_DIR}") monero_find_all_headers(ringct_basic_private_headers "${CMAKE_CURRENT_SOURCE_DIR}")

@ -70,12 +70,13 @@ static rct::key inner_product(const rct::keyV &a, const rct::keyV &b);
static constexpr size_t maxN = 64; static constexpr size_t maxN = 64;
static constexpr size_t maxM = BULLETPROOF_MAX_OUTPUTS; static constexpr size_t maxM = BULLETPROOF_MAX_OUTPUTS;
static rct::key Hi[maxN*maxM], Gi[maxN*maxM];
static ge_p3 Hi_p3[maxN*maxM], Gi_p3[maxN*maxM]; static ge_p3 Hi_p3[maxN*maxM], Gi_p3[maxN*maxM];
static std::shared_ptr<straus_cached_data> straus_HiGi_cache; static std::shared_ptr<straus_cached_data> straus_HiGi_cache;
static std::shared_ptr<pippenger_cached_data> pippenger_HiGi_cache; static std::shared_ptr<pippenger_cached_data> pippenger_HiGi_cache;
static const constexpr rct::key TWO = { {0x02, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; static const rct::key TWO = { {0x02, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } };
static const constexpr rct::key MINUS_ONE = { { 0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } }; static const rct::key MINUS_ONE = { { 0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } };
static const constexpr rct::key MINUS_INV_EIGHT = { { 0x74, 0xa4, 0x19, 0x7a, 0xf0, 0x7d, 0x0b, 0xf7, 0x05, 0xc2, 0xda, 0x25, 0x2b, 0x5c, 0x0b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a } }; static const rct::key MINUS_INV_EIGHT = { { 0x74, 0xa4, 0x19, 0x7a, 0xf0, 0x7d, 0x0b, 0xf7, 0x05, 0xc2, 0xda, 0x25, 0x2b, 0x5c, 0x0b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a } };
static const rct::keyV oneN = vector_dup(rct::identity(), maxN); static const rct::keyV oneN = vector_dup(rct::identity(), maxN);
static const rct::keyV twoN = vector_powers(TWO, maxN); static const rct::keyV twoN = vector_powers(TWO, maxN);
static const rct::key ip12 = inner_product(oneN, twoN); static const rct::key ip12 = inner_product(oneN, twoN);
@ -99,7 +100,8 @@ static inline bool is_reduced(const rct::key &scalar)
static rct::key get_exponent(const rct::key &base, size_t idx) static rct::key get_exponent(const rct::key &base, size_t idx)
{ {
std::string hashed = std::string((const char*)base.bytes, sizeof(base)) + config::HASH_KEY_BULLETPROOF_EXPONENT + tools::get_varint_data(idx); static const std::string domain_separator(config::HASH_KEY_BULLETPROOF_EXPONENT);
std::string hashed = std::string((const char*)base.bytes, sizeof(base)) + domain_separator + tools::get_varint_data(idx);
rct::key e; rct::key e;
ge_p3 e_p3; ge_p3 e_p3;
rct::hash_to_p3(e_p3, rct::hash2rct(crypto::cn_fast_hash(hashed.data(), hashed.size()))); rct::hash_to_p3(e_p3, rct::hash2rct(crypto::cn_fast_hash(hashed.data(), hashed.size())));
@ -119,10 +121,10 @@ static void init_exponents()
data.reserve(maxN*maxM*2); data.reserve(maxN*maxM*2);
for (size_t i = 0; i < maxN*maxM; ++i) for (size_t i = 0; i < maxN*maxM; ++i)
{ {
const rct::key Hi = get_exponent(rct::H, i * 2); Hi[i] = get_exponent(rct::H, i * 2);
CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Hi_p3[i], Hi.bytes) == 0, "ge_frombytes_vartime failed"); CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Hi_p3[i], Hi[i].bytes) == 0, "ge_frombytes_vartime failed");
const rct::key Gi = get_exponent(rct::H, i * 2 + 1); Gi[i] = get_exponent(rct::H, i * 2 + 1);
CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Gi_p3[i], Gi.bytes) == 0, "ge_frombytes_vartime failed"); CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Gi_p3[i], Gi[i].bytes) == 0, "ge_frombytes_vartime failed");
data.push_back({rct::zero(), Gi_p3[i]}); data.push_back({rct::zero(), Gi_p3[i]});
data.push_back({rct::zero(), Hi_p3[i]}); data.push_back({rct::zero(), Hi_p3[i]});
@ -131,10 +133,11 @@ static void init_exponents()
straus_HiGi_cache = straus_init_cache(data, STRAUS_SIZE_LIMIT); straus_HiGi_cache = straus_init_cache(data, STRAUS_SIZE_LIMIT);
pippenger_HiGi_cache = pippenger_init_cache(data, 0, PIPPENGER_SIZE_LIMIT); pippenger_HiGi_cache = pippenger_init_cache(data, 0, PIPPENGER_SIZE_LIMIT);
MINFO("Hi/Gi cache size: " << (sizeof(Hi)+sizeof(Gi))/1024 << " kB");
MINFO("Hi_p3/Gi_p3 cache size: " << (sizeof(Hi_p3)+sizeof(Gi_p3))/1024 << " kB"); MINFO("Hi_p3/Gi_p3 cache size: " << (sizeof(Hi_p3)+sizeof(Gi_p3))/1024 << " kB");
MINFO("Straus cache size: " << straus_get_cache_size(straus_HiGi_cache)/1024 << " kB"); MINFO("Straus cache size: " << straus_get_cache_size(straus_HiGi_cache)/1024 << " kB");
MINFO("Pippenger cache size: " << pippenger_get_cache_size(pippenger_HiGi_cache)/1024 << " kB"); MINFO("Pippenger cache size: " << pippenger_get_cache_size(pippenger_HiGi_cache)/1024 << " kB");
size_t cache_size = straus_get_cache_size(straus_HiGi_cache) + pippenger_get_cache_size(pippenger_HiGi_cache); size_t cache_size = (sizeof(Hi)+sizeof(Hi_p3))*2 + straus_get_cache_size(straus_HiGi_cache) + pippenger_get_cache_size(pippenger_HiGi_cache);
MINFO("Total cache size: " << cache_size/1024 << "kB"); MINFO("Total cache size: " << cache_size/1024 << "kB");
init_done = true; init_done = true;
} }
@ -892,8 +895,7 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
multiexp_data.resize(2 * maxMN); multiexp_data.resize(2 * maxMN);
PERF_TIMER_START_BP(VERIFY_line_24_25_invert); PERF_TIMER_START_BP(VERIFY_line_24_25_invert);
const std::vector<rct::key> inverses = invert(std::move(to_invert)); const std::vector<rct::key> inverses = invert(to_invert);
to_invert.clear();
PERF_TIMER_STOP_BP(VERIFY_line_24_25_invert); PERF_TIMER_STOP_BP(VERIFY_line_24_25_invert);
// setup weighted aggregates // setup weighted aggregates

@ -42,9 +42,16 @@ Bulletproof bulletproof_PROVE(const rct::key &v, const rct::key &gamma);
Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma); Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma);
Bulletproof bulletproof_PROVE(const rct::keyV &v, const rct::keyV &gamma); Bulletproof bulletproof_PROVE(const rct::keyV &v, const rct::keyV &gamma);
Bulletproof bulletproof_PROVE(const std::vector<uint64_t> &v, const rct::keyV &gamma); Bulletproof bulletproof_PROVE(const std::vector<uint64_t> &v, const rct::keyV &gamma);
Bulletproof bulletproof_PROVE_old(const rct::key &v, const rct::key &gamma);
Bulletproof bulletproof_PROVE_old(uint64_t v, const rct::key &gamma);
Bulletproof bulletproof_PROVE_old(const rct::keyV &v, const rct::keyV &gamma);
Bulletproof bulletproof_PROVE_old(const std::vector<uint64_t> &v, const rct::keyV &gamma);
bool bulletproof_VERIFY(const Bulletproof &proof); bool bulletproof_VERIFY(const Bulletproof &proof);
bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs); bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs);
bool bulletproof_VERIFY(const std::vector<Bulletproof> &proofs); bool bulletproof_VERIFY(const std::vector<Bulletproof> &proofs);
bool bulletproof_VERIFY_old(const Bulletproof &proof);
bool bulletproof_VERIFY_old(const std::vector<const Bulletproof*> &proofs);
bool bulletproof_VERIFY_old(const std::vector<Bulletproof> &proofs);
} }

File diff suppressed because it is too large Load Diff

@ -265,7 +265,7 @@ namespace rct
rct::key res = ONE; rct::key res = ONE;
if (n == 1) if (n == 1)
return x; return res;
n += 1; n += 1;
rct::key x1 = copy(x); rct::key x1 = copy(x);
@ -317,7 +317,16 @@ namespace rct
static rct::key weighted_inner_product(const rct::keyV &a, const epee::span<const rct::key> &b, const rct::key &y) static rct::key weighted_inner_product(const rct::keyV &a, const epee::span<const rct::key> &b, const rct::key &y)
{ {
CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b");
return weighted_inner_product(epee::to_span(a), b, y); rct::key res = rct::zero();
rct::key y_power = ONE;
rct::key temp;
for (size_t i = 0; i < a.size(); ++i)
{
sc_mul(temp.bytes, a[i].bytes, b[i].bytes);
sc_mul(y_power.bytes, y_power.bytes, y.bytes);
sc_muladd(res.bytes, temp.bytes, y_power.bytes, res.bytes);
}
return res;
} }
// Fold inner-product point vectors // Fold inner-product point vectors
@ -644,7 +653,8 @@ try_again:
{ {
sc_mul(temp.bytes, temp.bytes, z_squared.bytes); sc_mul(temp.bytes, temp.bytes, z_squared.bytes);
sc_mul(temp2.bytes, y_powers[MN+1].bytes, temp.bytes); sc_mul(temp2.bytes, y_powers[MN+1].bytes, temp.bytes);
sc_muladd(alpha1.bytes, temp2.bytes, gamma[j].bytes, alpha1.bytes); sc_mul(temp2.bytes, temp2.bytes, gamma[j].bytes);
sc_add(alpha1.bytes, alpha1.bytes, temp2.bytes);
} }
// These are used in the inner product rounds // These are used in the inner product rounds
@ -705,8 +715,7 @@ try_again:
rct::key challenge_squared; rct::key challenge_squared;
sc_mul(challenge_squared.bytes, challenge.bytes, challenge.bytes); sc_mul(challenge_squared.bytes, challenge.bytes, challenge.bytes);
rct::key challenge_squared_inv; rct::key challenge_squared_inv = invert(challenge_squared);
sc_mul(challenge_squared_inv.bytes, challenge_inv.bytes, challenge_inv.bytes);
sc_muladd(alpha1.bytes, dL.bytes, challenge_squared.bytes, alpha1.bytes); sc_muladd(alpha1.bytes, dL.bytes, challenge_squared.bytes, alpha1.bytes);
sc_muladd(alpha1.bytes, dR.bytes, challenge_squared_inv.bytes, alpha1.bytes); sc_muladd(alpha1.bytes, dR.bytes, challenge_squared_inv.bytes, alpha1.bytes);
@ -783,7 +792,15 @@ try_again:
rct::keyV sv(v.size()); rct::keyV sv(v.size());
for (size_t i = 0; i < v.size(); ++i) for (size_t i = 0; i < v.size(); ++i)
{ {
sv[i] = rct::d2h(v[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;
} }
return bulletproof_plus_PROVE(sv, gamma); return bulletproof_plus_PROVE(sv, gamma);
} }

@ -110,13 +110,6 @@ namespace
return rct::BulletproofPlus{rct::keyV(n_outs, I), I, I, I, I, I, I, rct::keyV(nrl, I), rct::keyV(nrl, I)}; return rct::BulletproofPlus{rct::keyV(n_outs, I), I, I, I, I, I, I, rct::keyV(nrl, I), rct::keyV(nrl, I)};
} }
rct::clsag make_dummy_clsag(size_t ring_size)
{
const rct::key I = rct::identity();
const size_t n_scalars = ring_size;
return rct::clsag{rct::keyV(n_scalars, I), I, I, I};
}
} }
namespace rct { namespace rct {
@ -132,6 +125,24 @@ namespace rct {
return proof; return proof;
} }
Bulletproof proveRangeBulletproof_old(key &C, key &mask, uint64_t amount)
{
mask = rct::skGen();
Bulletproof proof = bulletproof_PROVE_old(amount, mask);
CHECK_AND_ASSERT_THROW_MES(proof.V.size() == 1, "V has not exactly one element");
C = proof.V[0];
return proof;
}
Bulletproof proveRangeBulletproof_old(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts)
{
masks = rct::skvGen(amounts.size());
Bulletproof proof = bulletproof_PROVE_old(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 verBulletproof(const Bulletproof &proof) bool verBulletproof(const Bulletproof &proof)
{ {
try { return bulletproof_VERIFY(proof); } try { return bulletproof_VERIFY(proof); }
@ -146,6 +157,20 @@ namespace rct {
catch (...) { return false; } catch (...) { return false; }
} }
bool verBulletproof_old(const Bulletproof &proof)
{
try { return bulletproof_VERIFY_old(proof); }
// we can get deep throws from ge_frombytes_vartime if input isn't valid
catch (...) { return false; }
}
bool verBulletproof_old(const std::vector<const Bulletproof*> &proofs)
{
try { return bulletproof_VERIFY_old(proofs); }
// we can get deep throws from ge_frombytes_vartime if input isn't valid
catch (...) { return false; }
}
BulletproofPlus proveRangeBulletproofPlus(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts, epee::span<const key> sk, hw::device &hwdev) 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"); CHECK_AND_ASSERT_THROW_MES(amounts.size() == sk.size(), "Invalid amounts/sk sizes");
@ -616,7 +641,7 @@ namespace rct {
hashes.push_back(hash2rct(h)); hashes.push_back(hash2rct(h));
keyV kv; keyV kv;
if (rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG) if (rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeSimpleBulletproof || rv.type == RCTTypeFullBulletproof || rv.type == RCTTypeCLSAG)
{ {
kv.reserve((6*2+9) * rv.p.bulletproofs.size()); kv.reserve((6*2+9) * rv.p.bulletproofs.size());
for (const auto &p: rv.p.bulletproofs) for (const auto &p: rv.p.bulletproofs)
@ -1044,6 +1069,7 @@ namespace rct {
// Note: For txn fees, the last index in the amounts vector should contain that // Note: For txn fees, the last index in the amounts vector should contain that
// Thus the amounts vector will be "one" longer than the destinations vectort // Thus the amounts vector will be "one" longer than the destinations vectort
rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev) { rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev) {
const bool bulletproof = rct_config.range_proof_type != RangeProofBorromean;
CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations"); CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations");
CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations"); CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations");
CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing"); CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing");
@ -1053,10 +1079,11 @@ namespace rct {
CHECK_AND_ASSERT_THROW_MES(inSk.size() < 2, "genRct is not suitable for 2+ rings"); CHECK_AND_ASSERT_THROW_MES(inSk.size() < 2, "genRct is not suitable for 2+ rings");
rctSig rv; rctSig rv;
rv.type = RCTTypeFull; rv.type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull;
rv.message = message; rv.message = message;
rv.outPk.resize(destinations.size()); rv.outPk.resize(destinations.size());
rv.p.rangeSigs.resize(destinations.size()); if (!bulletproof)
rv.p.rangeSigs.resize(destinations.size());
rv.ecdhInfo.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size());
size_t i = 0; size_t i = 0;
@ -1066,10 +1093,45 @@ namespace rct {
//add destination to sig //add destination to sig
rv.outPk[i].dest = copy(destinations[i]); rv.outPk[i].dest = copy(destinations[i]);
//compute range proof //compute range proof
rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); if (!bulletproof)
{
rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]);
#ifdef DBG #ifdef DBG
CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof");
#endif #endif
}
}
rv.p.bulletproofs.clear();
if (bulletproof)
{
std::vector<uint64_t> proof_amounts;
size_t amounts_proved = 0;
while (amounts_proved < amounts.size())
{
size_t batch_size = 1;
if (rct_config.range_proof_type == RangeProofMultiOutputBulletproof)
while (batch_size * 2 + amounts_proved <= amounts.size())
batch_size *= 2;
rct::keyV C, masks;
std::vector<uint64_t> batch_amounts(batch_size);
for (i = 0; i < batch_size; ++i)
batch_amounts[i] = amounts[i + amounts_proved];
rv.p.bulletproofs.push_back(proveRangeBulletproof_old(C, masks, batch_amounts));
#ifdef DBG
CHECK_AND_ASSERT_THROW_MES(verBulletproof_old(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
#endif
for (i = 0; i < batch_size; ++i)
{
rv.outPk[i + amounts_proved].mask = C[i];
outSk[i + amounts_proved].mask = masks[i];
}
amounts_proved += batch_size;
}
}
for (i = 0; i < outSk.size(); ++i)
{
//mask amount and mask //mask amount and mask
rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].mask = copy(outSk[i].mask);
rv.ecdhInfo[i].amount = d2h(amounts[i]); rv.ecdhInfo[i].amount = d2h(amounts[i]);
@ -1195,7 +1257,10 @@ namespace rct {
} }
for (i = 0; i < outamounts.size(); ++i) for (i = 0; i < outamounts.size(); ++i)
{ {
rv.outPk[i].mask = rct::scalarmult8(C[i]); if (plus)
rv.outPk[i].mask = C[i];
else
rv.outPk[i].mask = rct::scalarmult8(C[i]);
outSk[i].mask = masks[i]; outSk[i].mask = masks[i];
} }
} }
@ -1233,7 +1298,10 @@ namespace rct {
} }
for (i = 0; i < batch_size; ++i) for (i = 0; i < batch_size; ++i)
{ {
rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]); if (plus)
rv.outPk[i + amounts_proved].mask = C[i];
else
rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]);
outSk[i + amounts_proved].mask = masks[i]; outSk[i + amounts_proved].mask = masks[i];
} }
amounts_proved += batch_size; amounts_proved += batch_size;
@ -1279,10 +1347,7 @@ namespace rct {
{ {
if (is_rct_clsag(rv.type)) if (is_rct_clsag(rv.type))
{ {
if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE) rv.p.CLSAGs[i] = proveRctCLSAGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], index[i], hwdev);
rv.p.CLSAGs[i] = make_dummy_clsag(rv.mixRing[i].size());
else
rv.p.CLSAGs[i] = proveRctCLSAGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], index[i], hwdev);
} }
else else
{ {
@ -1305,6 +1370,98 @@ namespace rct {
return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk, rct_config, hwdev); return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk, rct_config, hwdev);
} }
//RCT simple
//for post-rct only
rctSig genRctSimple_old(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<unsigned int> & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev) {
const bool bulletproof = 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");
CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations");
CHECK_AND_ASSERT_THROW_MES(index.size() == inSk.size(), "Different number of index/inSk");
CHECK_AND_ASSERT_THROW_MES(mixRing.size() == inSk.size(), "Different number of mixRing/inSk");
for (size_t n = 0; n < mixRing.size(); ++n) {
CHECK_AND_ASSERT_THROW_MES(index[n] < mixRing[n].size(), "Bad index into mixRing");
}
rctSig rv;
rv.type = bulletproof ? RCTTypeSimpleBulletproof : RCTTypeSimple;
rv.message = message;
rv.outPk.resize(destinations.size());
if (bulletproof)
rv.p.bulletproofs.resize(destinations.size());
else
rv.p.rangeSigs.resize(destinations.size());
rv.ecdhInfo.resize(destinations.size());
size_t i;
keyV masks(destinations.size()); //sk mask..
outSk.resize(destinations.size());
key sumout = zero();
for (i = 0; i < destinations.size(); i++) {
//add destination to sig
rv.outPk[i].dest = copy(destinations[i]);
//compute range proof
if (bulletproof)
rv.p.bulletproofs[i] = proveRangeBulletproof_old(rv.outPk[i].mask, outSk[i].mask, outamounts[i]);
else
rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]);
#ifdef DBG
if (bulletproof)
CHECK_AND_ASSERT_THROW_MES(verBulletproof_old(rv.p.bulletproofs[i]), "verBulletproof failed on newly created proof");
else
CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof");
#endif
sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes);
//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 == RCTTypeSimpleBulletproof);
}
//set txn fee
rv.txnFee = txnFee;
// TODO: unused ??
// key txnFeeKey = scalarmultH(d2h(rv.txnFee));
rv.mixRing = mixRing;
keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
pseudoOuts.resize(inamounts.size());
rv.p.MGs.resize(inamounts.size());
key sumpouts = zero(); //sum pseudoOut masks
keyV a(inamounts.size());
for (i = 0 ; i < inamounts.size() - 1; i++) {
skGen(a[i]);
sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes);
genC(pseudoOuts[i], a[i], inamounts[i]);
}
rv.mixRing = mixRing;
sc_sub(a[i].bytes, sumout.bytes, sumpouts.bytes);
genC(pseudoOuts[i], a[i], inamounts[i]);
DP(pseudoOuts[i]);
key full_message = get_pre_mlsag_hash(rv,hwdev);
for (i = 0 ; i < inamounts.size(); i++) {
rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], index[i], hwdev);
}
return rv;
}
rctSig genRctSimple_old(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin, const RCTConfig &rct_config, hw::device &hwdev) {
std::vector<unsigned int> index;
index.resize(inPk.size());
ctkeyM mixRing;
ctkeyV outSk;
mixRing.resize(inPk.size());
for (size_t i = 0; i < inPk.size(); ++i) {
mixRing[i].resize(mixin+1);
index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin);
}
return genRctSimple_old(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk, rct_config, hwdev);
}
//RingCT protocol //RingCT protocol
//genRct: //genRct:
// creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the
@ -1317,10 +1474,13 @@ namespace rct {
// must know the destination private key to find the correct amount, else will return a random number // must know the destination private key to find the correct amount, else will return a random number
bool verRct(const rctSig & rv, bool semantics) { bool verRct(const rctSig & rv, bool semantics) {
PERF_TIMER(verRct); PERF_TIMER(verRct);
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig"); const bool bulletproof = is_rct_bulletproof(rv.type);
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "verRct called on non-full rctSig");
if (semantics) if (semantics)
{ {
if (rv.type == RCTTypeBulletproof)
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
else
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG"); CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG");
} }
@ -1335,10 +1495,23 @@ namespace rct {
if (semantics) { if (semantics) {
tools::threadpool& tpool = tools::threadpool::getInstanceForCompute(); tools::threadpool& tpool = tools::threadpool::getInstanceForCompute();
tools::threadpool::waiter waiter(tpool); tools::threadpool::waiter waiter(tpool);
std::deque<bool> results(rv.outPk.size(), false); std::deque<bool> results(bulletproof ? rv.p.bulletproofs.size() : rv.outPk.size(), false);
DP("range proofs verified?"); DP("range proofs verified?");
for (size_t i = 0; i < rv.outPk.size(); i++) if (rct::is_rct_new_bulletproof(rv.type))
tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); {
for (size_t i = 0; i < rv.p.bulletproofs.size(); i++)
tpool.submit(&waiter, [&, i] { results[i] = verBulletproof(rv.p.bulletproofs[i]); });
}
else if (bulletproof)
{
for (size_t i = 0; i < rv.p.bulletproofs.size(); i++)
tpool.submit(&waiter, [&, i] { results[i] = verBulletproof_old(rv.p.bulletproofs[i]); });
}
else
{
for (size_t i = 0; i < rv.outPk.size(); i++)
tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); });
}
if (!waiter.wait()) if (!waiter.wait())
return false; return false;
@ -1439,7 +1612,10 @@ namespace rct {
rct::keyV masks(rv.outPk.size()); rct::keyV masks(rv.outPk.size());
for (size_t i = 0; i < rv.outPk.size(); i++) { for (size_t i = 0; i < rv.outPk.size(); i++) {
masks[i] = rv.outPk[i].mask; if (bulletproof_plus)
masks[i] = rct::scalarmult8(rv.outPk[i].mask);
else
masks[i] = rv.outPk[i].mask;
} }
key sumOutpks = addKeys(masks); key sumOutpks = addKeys(masks);
DP(sumOutpks); DP(sumOutpks);
@ -1589,7 +1765,7 @@ namespace rct {
// uses the attached ecdh info to find the amounts represented by each output commitment // uses the attached ecdh info to find the amounts represented by each output commitment
// must know the destination private key to find the correct amount, else will return a random number // must know the destination private key to find the correct amount, else will return a random number
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev) { xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev) {
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig"); CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "decodeRct called on non-full rctSig");
CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); 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"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
@ -1599,6 +1775,8 @@ namespace rct {
mask = ecdh_info.mask; mask = ecdh_info.mask;
key amount = ecdh_info.amount; key amount = ecdh_info.amount;
key C = rv.outPk[i].mask; key C = rv.outPk[i].mask;
if (is_rct_bulletproof_plus(rv.type))
C = scalarmult8(C);
DP("C"); DP("C");
DP(C); DP(C);
key Ctmp; key Ctmp;
@ -1630,6 +1808,8 @@ namespace rct {
mask = ecdh_info.mask; mask = ecdh_info.mask;
key amount = ecdh_info.amount; key amount = ecdh_info.amount;
key C = rv.outPk[i].mask; key C = rv.outPk[i].mask;
if (is_rct_bulletproof_plus(rv.type))
C = scalarmult8(C);
DP("C"); DP("C");
DP(C); DP(C);
key Ctmp; key Ctmp;

@ -127,6 +127,9 @@ namespace rct {
rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const keyV &amount_keys, const int mixin, const RCTConfig &rct_config, hw::device &hwdev); rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const keyV &amount_keys, const int mixin, const RCTConfig &rct_config, hw::device &hwdev);
rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin, const RCTConfig &rct_config, hw::device &hwdev); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin, const RCTConfig &rct_config, hw::device &hwdev);
rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev);
rctSig genRctSimple_old(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin, const RCTConfig &rct_config, hw::device &hwdev);
rctSig genRctSimple_old(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev);
bool verRct(const rctSig & rv, bool semantics); bool verRct(const rctSig & rv, bool semantics);
static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); } static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); }
bool verRctSemanticsSimple(const rctSig & rv); bool verRctSemanticsSimple(const rctSig & rv);

@ -193,6 +193,7 @@ namespace rct {
switch (type) switch (type)
{ {
case RCTTypeSimple: case RCTTypeSimple:
case RCTTypeSimpleBulletproof:
case RCTTypeBulletproof: case RCTTypeBulletproof:
case RCTTypeBulletproof2: case RCTTypeBulletproof2:
case RCTTypeCLSAG: case RCTTypeCLSAG:
@ -207,6 +208,8 @@ namespace rct {
{ {
switch (type) switch (type)
{ {
case RCTTypeSimpleBulletproof:
case RCTTypeFullBulletproof:
case RCTTypeBulletproof: case RCTTypeBulletproof:
case RCTTypeBulletproof2: case RCTTypeBulletproof2:
case RCTTypeCLSAG: case RCTTypeCLSAG:
@ -216,6 +219,23 @@ namespace rct {
} }
} }
bool is_rct_old_bulletproof(int type)
{
switch (type)
{
case RCTTypeSimpleBulletproof:
case RCTTypeFullBulletproof:
return true;
default:
return false;
}
}
bool is_rct_new_bulletproof(int type)
{
return is_rct_bulletproof(type) && !is_rct_old_bulletproof(type);
}
bool is_rct_bulletproof_plus(int type) bool is_rct_bulletproof_plus(int type)
{ {
switch (type) switch (type)
@ -238,6 +258,26 @@ namespace rct {
return false; return false;
} }
} }
size_t n_bulletproof_v1_amounts(const Bulletproof &proof)
{
CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size");
CHECK_AND_ASSERT_MES(proof.L.size() <= 31, 0, "Insane bulletproof L size");
return 1 << (proof.L.size() - 6);
}
size_t n_bulletproof_v1_amounts(const std::vector<Bulletproof> &proofs)
{
size_t n = 0;
for (const Bulletproof &proof: proofs)
{
size_t n2 = n_bulletproof_v1_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;
}
bool is_rct_clsag(int type) bool is_rct_clsag(int type)
{ {
@ -251,22 +291,19 @@ namespace rct {
} }
} }
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) size_t n_bulletproof_amounts(const Bulletproof &proof)
{ {
CHECK_AND_ASSERT_MES(L_size >= 6, 0, "Invalid bulletproof L size"); CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size");
CHECK_AND_ASSERT_MES(L_size == R_size, 0, "Mismatched bulletproof L/R size"); CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), 0, "Mismatched bulletproof L/R size");
static const size_t extra_bits = 4; static const size_t extra_bits = 4;
CHECK_AND_ASSERT_MES((1 << extra_bits) == max_outputs, 0, "log2(max_outputs) is out of date"); static_assert((1 << extra_bits) == BULLETPROOF_MAX_OUTPUTS, "log2(BULLETPROOF_MAX_OUTPUTS) is out of date");
CHECK_AND_ASSERT_MES(L_size <= 6 + extra_bits, 0, "Invalid bulletproof L size"); CHECK_AND_ASSERT_MES(proof.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(proof.V.size() <= (1u<<(proof.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(proof.V.size() * 2 > (1u<<(proof.L.size()-6)), 0, "Invalid bulletproof V/L");
CHECK_AND_ASSERT_MES(V_size > 0, 0, "Empty bulletproof"); CHECK_AND_ASSERT_MES(proof.V.size() > 0, 0, "Empty bulletproof");
return V_size; return proof.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_bulletproof_amounts(const std::vector<Bulletproof> &proofs)
{ {
size_t n = 0; size_t n = 0;
@ -281,12 +318,22 @@ namespace rct {
return n; return n;
} }
size_t n_bulletproof_plus_amounts(const std::vector<BulletproofPlus> &proofs) size_t n_bulletproof_max_amounts(const Bulletproof &proof)
{
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");
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);
}
size_t n_bulletproof_max_amounts(const std::vector<Bulletproof> &proofs)
{ {
size_t n = 0; size_t n = 0;
for (const BulletproofPlus &proof: proofs) for (const Bulletproof &proof: proofs)
{ {
size_t n2 = n_bulletproof_plus_amounts(proof); size_t n2 = n_bulletproof_max_amounts(proof);
CHECK_AND_ASSERT_MES(n2 < std::numeric_limits<uint32_t>::max() - n, 0, "Invalid number of bulletproofs"); CHECK_AND_ASSERT_MES(n2 < std::numeric_limits<uint32_t>::max() - n, 0, "Invalid number of bulletproofs");
if (n2 == 0) if (n2 == 0)
return 0; return 0;
@ -295,24 +342,25 @@ namespace rct {
return n; return n;
} }
static size_t n_bulletproof_max_amounts_base(size_t L_size, size_t R_size, size_t max_outputs) size_t n_bulletproof_plus_amounts(const BulletproofPlus &proof)
{ {
CHECK_AND_ASSERT_MES(L_size >= 6, 0, "Invalid bulletproof L size"); CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size");
CHECK_AND_ASSERT_MES(L_size == R_size, 0, "Mismatched bulletproof L/R size"); CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), 0, "Mismatched bulletproof L/R size");
static const size_t extra_bits = 4; static const size_t extra_bits = 4;
CHECK_AND_ASSERT_MES((1 << extra_bits) == max_outputs, 0, "log2(max_outputs) is out of date"); static_assert((1 << extra_bits) == BULLETPROOF_PLUS_MAX_OUTPUTS, "log2(BULLETPROOF_PLUS_MAX_OUTPUTS) is out of date");
CHECK_AND_ASSERT_MES(L_size <= 6 + extra_bits, 0, "Invalid bulletproof L size"); CHECK_AND_ASSERT_MES(proof.L.size() <= 6 + extra_bits, 0, "Invalid bulletproof L size");
return 1 << (L_size - 6); 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();
} }
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) size_t n_bulletproof_plus_amounts(const std::vector<BulletproofPlus> &proofs)
{ {
size_t n = 0; size_t n = 0;
for (const Bulletproof &proof: proofs) for (const BulletproofPlus &proof: proofs)
{ {
size_t n2 = n_bulletproof_max_amounts(proof); 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"); CHECK_AND_ASSERT_MES(n2 < std::numeric_limits<uint32_t>::max() - n, 0, "Invalid number of bulletproofs");
if (n2 == 0) if (n2 == 0)
return 0; return 0;
@ -321,6 +369,16 @@ namespace rct {
return n; return n;
} }
size_t n_bulletproof_plus_max_amounts(const BulletproofPlus &proof)
{
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");
static const size_t extra_bits = 4;
static_assert((1 << extra_bits) == BULLETPROOF_PLUS_MAX_OUTPUTS, "log2(BULLETPROOF_PLUS_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);
}
size_t n_bulletproof_plus_max_amounts(const std::vector<BulletproofPlus> &proofs) size_t n_bulletproof_plus_max_amounts(const std::vector<BulletproofPlus> &proofs)
{ {
size_t n = 0; size_t n = 0;

@ -245,7 +245,8 @@ namespace rct {
rct::key r1, s1, d1; rct::key r1, s1, d1;
rct::keyV L, R; rct::keyV L, R;
BulletproofPlus() {} BulletproofPlus():
A({}), A1({}), B({}), r1({}), s1({}), d1({}) {}
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): 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) {} 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): 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):
@ -271,8 +272,10 @@ namespace rct {
}; };
size_t n_bulletproof_amounts(const Bulletproof &proof); size_t n_bulletproof_amounts(const Bulletproof &proof);
size_t n_bulletproof_v1_amounts(const Bulletproof &proof);
size_t n_bulletproof_max_amounts(const Bulletproof &proof); size_t n_bulletproof_max_amounts(const Bulletproof &proof);
size_t n_bulletproof_amounts(const std::vector<Bulletproof> &proofs); size_t n_bulletproof_amounts(const std::vector<Bulletproof> &proofs);
size_t n_bulletproof_v1_amounts(const std::vector<Bulletproof> &proofs);
size_t n_bulletproof_max_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_amounts(const BulletproofPlus &proof);
@ -291,10 +294,12 @@ namespace rct {
RCTTypeNull = 0, RCTTypeNull = 0,
RCTTypeFull = 1, RCTTypeFull = 1,
RCTTypeSimple = 2, RCTTypeSimple = 2,
RCTTypeBulletproof = 3, RCTTypeFullBulletproof = 3,
RCTTypeBulletproof2 = 4, RCTTypeSimpleBulletproof = 4,
RCTTypeCLSAG = 5, RCTTypeBulletproof = 5,
RCTTypeBulletproofPlus = 6, RCTTypeBulletproof2 = 6,
RCTTypeCLSAG = 7,
RCTTypeBulletproofPlus = 8,
}; };
enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof }; enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof };
struct RCTConfig { struct RCTConfig {
@ -323,7 +328,7 @@ namespace rct {
FIELD(type) FIELD(type)
if (type == RCTTypeNull) if (type == RCTTypeNull)
return ar.good(); return ar.good();
if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeBulletproofPlus) if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeFullBulletproof && type != RCTTypeSimpleBulletproof && type != RCTTypeCLSAG && type != RCTTypeBulletproofPlus)
return false; return false;
VARINT_FIELD(txnFee) VARINT_FIELD(txnFee)
// inputs/outputs not saved, only here for serialization help // inputs/outputs not saved, only here for serialization help
@ -415,13 +420,28 @@ namespace rct {
return false; return false;
if (type == RCTTypeNull) if (type == RCTTypeNull)
return ar.good(); return ar.good();
if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeBulletproofPlus) if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeFullBulletproof && type != RCTTypeSimpleBulletproof && type != RCTTypeCLSAG && type != RCTTypeBulletproofPlus)
return false; return false;
if (type == RCTTypeBulletproofPlus) if (type == RCTTypeSimpleBulletproof || type == RCTTypeFullBulletproof)
{
ar.tag("bp");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs);
if (bulletproofs.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
{
FIELDS(bulletproofs[i])
if (outputs - i > 1)
ar.delimit_array();
}
ar.end_array();
}
else if (type == RCTTypeBulletproofPlus)
{ {
uint32_t nbp = bulletproofs_plus.size(); uint32_t nbp = bulletproofs_plus.size();
VARINT_FIELD(nbp) VARINT_FIELD(nbp)
ar.tag("bpp"); ar.tag(type >= RCTTypeBulletproofPlus ? "bpp" : "bp");
ar.begin_array(); ar.begin_array();
if (nbp > outputs) if (nbp > outputs)
return false; return false;
@ -520,7 +540,7 @@ namespace rct {
ar.begin_array(); ar.begin_array();
// we keep a byte for size of MGs, because we don't know whether this is // we keep a byte for size of MGs, because we don't know whether this is
// a simple or full rct signature, and it's starting to annoy the hell out of me // a simple or full rct signature, and it's starting to annoy the hell out of me
size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2) ? inputs : 1; size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeSimpleBulletproof) ? inputs : 1;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs); PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs);
if (MGs.size() != mg_elements) if (MGs.size() != mg_elements)
return false; return false;
@ -538,7 +558,7 @@ namespace rct {
for (size_t j = 0; j < mixin + 1; ++j) for (size_t j = 0; j < mixin + 1; ++j)
{ {
ar.begin_array(); ar.begin_array();
size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2) ? 1 : inputs) + 1; size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeSimpleBulletproof) ? 1 : inputs) + 1;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]); PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]);
if (MGs[i].ss[j].size() != mg_ss2_elements) if (MGs[i].ss[j].size() != mg_ss2_elements)
return false; return false;
@ -565,7 +585,7 @@ namespace rct {
} }
ar.end_array(); ar.end_array();
} }
if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus) if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeSimpleBulletproof || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus)
{ {
ar.tag("pseudoOuts"); ar.tag("pseudoOuts");
ar.begin_array(); ar.begin_array();
@ -597,12 +617,12 @@ namespace rct {
keyV& get_pseudo_outs() keyV& get_pseudo_outs()
{ {
return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus ? p.pseudoOuts : pseudoOuts; return type == RCTTypeBulletproof || type == RCTTypeSimpleBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus ? p.pseudoOuts : pseudoOuts;
} }
keyV const& get_pseudo_outs() const keyV const& get_pseudo_outs() const
{ {
return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus ? p.pseudoOuts : pseudoOuts; return type == RCTTypeBulletproof || type == RCTTypeSimpleBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeBulletproofPlus ? p.pseudoOuts : pseudoOuts;
} }
BEGIN_SERIALIZE_OBJECT() BEGIN_SERIALIZE_OBJECT()
@ -714,6 +734,8 @@ namespace rct {
bool is_rct_simple(int type); bool is_rct_simple(int type);
bool is_rct_bulletproof(int type); bool is_rct_bulletproof(int type);
bool is_rct_old_bulletproof(int type);
bool is_rct_new_bulletproof(int type);
bool is_rct_bulletproof_plus(int type); bool is_rct_bulletproof_plus(int type);
bool is_rct_borromean(int type); bool is_rct_borromean(int type);
bool is_rct_clsag(int type); bool is_rct_clsag(int type);

@ -1839,12 +1839,14 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
switch (rv.type) switch (rv.type)
{ {
case rct::RCTTypeSimple: case rct::RCTTypeSimple:
case rct::RCTTypeSimpleBulletproof:
case rct::RCTTypeBulletproof: case rct::RCTTypeBulletproof:
case rct::RCTTypeBulletproof2: case rct::RCTTypeBulletproof2:
case rct::RCTTypeCLSAG: case rct::RCTTypeCLSAG:
case rct::RCTTypeBulletproofPlus: case rct::RCTTypeBulletproofPlus:
return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev); return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev);
case rct::RCTTypeFull: case rct::RCTTypeFull:
case rct::RCTTypeFullBulletproof:
return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev); return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev);
default: default:
LOG_ERROR("Unsupported rct type: " << rv.type); LOG_ERROR("Unsupported rct type: " << rv.type);
@ -10279,8 +10281,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
++original_output_index; ++original_output_index;
} }
if (!out_slots_exhausted && available_amount > 0 && !dsts.empty() && 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, use_view_tags) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag, bulletproof_plus, use_view_tags) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
// we can partially fill that destination // 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) << 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)); " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount));
@ -11628,7 +11629,9 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
crypto::derivation_to_scalar(found_derivation, n, scalar1); crypto::derivation_to_scalar(found_derivation, n, scalar1);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n]; 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 || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus); 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 C = tx.rct_signatures.outPk[n].mask;
if (rct::is_rct_bulletproof_plus(tx.rct_signatures.type))
C = rct::scalarmult8(C);
rct::key Ctmp; rct::key Ctmp;
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask"); THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask");
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.amount.bytes) != 0, error::wallet_internal_error, "Bad ECDH input amount"); THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.amount.bytes) != 0, error::wallet_internal_error, "Bad ECDH input amount");

@ -45,7 +45,7 @@ set(core_tests_sources
rct.cpp rct.cpp
bulletproofs.cpp bulletproofs.cpp
bulletproof_plus.cpp bulletproof_plus.cpp
rct2.cpp rct2.cpp
wallet_tools.cpp) wallet_tools.cpp)
set(core_tests_headers set(core_tests_headers

@ -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); crypto::derivation_to_scalar(derivation, o, amount_key);
rct::key rct_tx_mask; rct::key rct_tx_mask;
const uint8_t type = rct_txes.back().rct_signatures.type; const uint8_t type = rct_txes.back().rct_signatures.type;
if (rct::is_rct_simple(type)) if (type == rct::RCTTypeSimple || type == rct::RCTTypeBulletproof || type == rct::RCTTypeBulletproof2 || type == rct::RCTTypeCLSAG)
rct::decodeRctSimple(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default")); rct::decodeRctSimple(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default"));
else else
rct::decodeRct(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default")); rct::decodeRct(rct_txes.back().rct_signatures, rct::sk2rct(amount_key), o, rct_tx_mask, hw::get_device("default"));
@ -193,6 +193,7 @@ bool gen_bp_tx_validation_base::check_bp(const cryptonote::transaction &tx, size
{ {
DEFINE_TESTS_ERROR_CONTEXT(context); DEFINE_TESTS_ERROR_CONTEXT(context);
CHECK_TEST_CONDITION(tx.version >= 2); CHECK_TEST_CONDITION(tx.version >= 2);
MGINFO("type: " << (unsigned)tx.rct_signatures.type);
CHECK_TEST_CONDITION(rct::is_rct_bulletproof(tx.rct_signatures.type)); CHECK_TEST_CONDITION(rct::is_rct_bulletproof(tx.rct_signatures.type));
size_t n_sizes = 0, n_amounts = 0; size_t n_sizes = 0, n_amounts = 0;
for (size_t n = 0; n < tx_idx; ++n) for (size_t n = 0; n < tx_idx; ++n)
@ -232,7 +233,7 @@ bool gen_bp_tx_invalid_1_1::generate(std::vector<test_event_entry>& events) cons
{ {
const size_t mixin = 10; const size_t mixin = 10;
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof , 3 } }; const rct::RCTConfig rct_config[] = { { rct::RangeProofBulletproof , 0 } };
return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_CLSAG, NULL, NULL); return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_CLSAG, NULL, NULL);
} }
@ -241,7 +242,7 @@ bool gen_bp_tx_valid_2::generate(std::vector<test_event_entry>& events) const
const size_t mixin = 10; const size_t mixin = 10;
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
const size_t bp_sizes[] = {2, (size_t)-1}; const size_t bp_sizes[] = {2, (size_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 3 } }; const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 0 } };
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"); }); 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 +251,7 @@ bool gen_bp_tx_valid_3::generate(std::vector<test_event_entry>& events) const
const size_t mixin = 10; const size_t mixin = 10;
const uint64_t amounts_paid[] = {5000, 5000, 5000, (uint64_t)-1}; const uint64_t amounts_paid[] = {5000, 5000, 5000, (uint64_t)-1};
const size_t bp_sizes[] = {4, (size_t)-1}; const size_t bp_sizes[] = {4, (size_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 3 } }; const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 0 } };
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"); }); 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 +260,7 @@ bool gen_bp_tx_valid_16::generate(std::vector<test_event_entry>& events) const
const size_t mixin = 10; 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 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 size_t bp_sizes[] = {16, (size_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 3 } }; const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 0 } };
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"); }); 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"); });
} }
@ -383,13 +384,3 @@ bool gen_bp_tx_invalid_bulletproof2_type::generate(std::vector<test_event_entry>
}); });
} }
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;
});
}

@ -212,8 +212,3 @@ struct gen_bp_tx_invalid_bulletproof2_type : public gen_bp_tx_validation_base
}; };
template<> struct get_test_options<gen_bp_tx_invalid_bulletproof2_type>: public get_bp_versioned_test_options<HF_VERSION_CLSAG + 1> {}; 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> {};

@ -280,24 +280,6 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_bp_tx_invalid_wrong_amount); 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_borromean_type);
GENERATE_AND_PLAY(gen_bp_tx_invalid_bulletproof2_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); GENERATE_AND_PLAY(gen_rct2_tx_clsag_malleability);

@ -300,6 +300,11 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
destinations.push_back(td); destinations.push_back(td);
td.amount = 0; td.amount = 0;
destinations.push_back(td); 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) if (pre_tx)
pre_tx(sources, destinations); pre_tx(sources, destinations);
@ -309,8 +314,8 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
std::vector<crypto::secret_key> additional_tx_secret_keys; std::vector<crypto::secret_key> additional_tx_secret_keys;
crypto::secret_key multisig_tx_key_entropy; crypto::secret_key multisig_tx_key_entropy;
auto sources_copy = sources; auto sources_copy = sources;
multisig::signing::tx_builder_ringct_t tx_builder; 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(tx_builder.init(miner_account[creator].get_keys(), {}, 0, 0, {0}, sources, destinations, {}, {rct::RangeProofPaddedBulletproof, 4}, true, false, tx_key, additional_tx_secret_keys, multisig_tx_key_entropy, tx), false, "error: multisig::signing::tx_builder_ringct_t::init"); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
// work out the permutation done on sources // work out the permutation done on sources
std::vector<size_t> ins_order; std::vector<size_t> ins_order;
@ -434,8 +439,10 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
crypto::secret_key scalar1; crypto::secret_key scalar1;
crypto::derivation_to_scalar(derivation, n, scalar1); crypto::derivation_to_scalar(derivation, n, scalar1);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n]; 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 || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus); rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
rct::key C = tx.rct_signatures.outPk[n].mask; rct::key C = tx.rct_signatures.outPk[n].mask;
if (rct::is_rct_bulletproof_plus(tx.rct_signatures.type))
C = rct::scalarmult8(C);
rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H); rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
CHECK_AND_ASSERT_MES(rct::equalKeys(C, Ctmp), false, "Failed to decode amount"); CHECK_AND_ASSERT_MES(rct::equalKeys(C, Ctmp), false, "Failed to decode amount");
amount += rct::h2d(ecdh_info.amount); amount += rct::h2d(ecdh_info.amount);

@ -135,7 +135,7 @@ bool gen_rct_tx_validation_base::generate_with_full(std::vector<test_event_entry
crypto::secret_key amount_key; crypto::secret_key amount_key;
crypto::derivation_to_scalar(derivation, o, amount_key); crypto::derivation_to_scalar(derivation, o, amount_key);
const uint8_t type = rct_txes[n].rct_signatures.type; const uint8_t type = rct_txes[n].rct_signatures.type;
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")); rct::decodeRctSimple(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default"));
else else
rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default")); rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4], hw::get_device("default"));

@ -211,7 +211,7 @@ bool gen_rct2_tx_clsag_malleability::generate(std::vector<test_event_entry>& eve
DEFINE_TESTS_ERROR_CONTEXT("gen_rct_tx_clsag_malleability"); DEFINE_TESTS_ERROR_CONTEXT("gen_rct_tx_clsag_malleability");
const int mixin = 10; const int mixin = 10;
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 3 } }; const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 0 } };
return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_CLSAG + 1, NULL, [&](cryptonote::transaction &tx, size_t tx_idx) { return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_CLSAG + 1, NULL, [&](cryptonote::transaction &tx, size_t tx_idx) {
CHECK_TEST_CONDITION(tx.version == 2); CHECK_TEST_CONDITION(tx.version == 2);
CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeCLSAG); CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeCLSAG);

@ -47,8 +47,7 @@ set(performance_tests_headers
subaddress_expand.h subaddress_expand.h
range_proof.h range_proof.h
bulletproof.h bulletproof.h
bulletproof_plus.h crypto_ops.h
crypto_ops.h
sc_reduce32.h sc_reduce32.h
sc_check.h sc_check.h
multiexp.h multiexp.h

@ -60,7 +60,6 @@
#include "equality.h" #include "equality.h"
#include "range_proof.h" #include "range_proof.h"
#include "bulletproof.h" #include "bulletproof.h"
#include "bulletproof_plus.h"
#include "crypto_ops.h" #include "crypto_ops.h"
#include "multiexp.h" #include "multiexp.h"
#include "sig_mlsag.h" #include "sig_mlsag.h"
@ -248,25 +247,6 @@ int main(int argc, char** argv)
TEST_PERFORMANCE1(filter, p, test_range_proof, true); TEST_PERFORMANCE1(filter, p, test_range_proof, true);
TEST_PERFORMANCE1(filter, p, test_range_proof, false); TEST_PERFORMANCE1(filter, p, test_range_proof, false);
TEST_PERFORMANCE2(filter, p, test_bulletproof_plus, true, 1); // 1 bulletproof_plus with 1 amount
TEST_PERFORMANCE2(filter, p, test_bulletproof_plus, false, 1);
TEST_PERFORMANCE2(filter, p, test_bulletproof_plus, true, 2); // 1 bulletproof_plus with 2 amounts
TEST_PERFORMANCE2(filter, p, test_bulletproof_plus, false, 2);
TEST_PERFORMANCE2(filter, p, test_bulletproof_plus, true, 15); // 1 bulletproof_plus with 15 amounts
TEST_PERFORMANCE2(filter, p, test_bulletproof_plus, false, 15);
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof_plus, false, 2, 1, 1, 0, 4);
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof_plus, true, 2, 1, 1, 0, 4); // 4 proofs, each with 2 amounts
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof_plus, false, 8, 1, 1, 0, 4);
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof_plus, true, 8, 1, 1, 0, 4); // 4 proofs, each with 8 amounts
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof_plus, false, 1, 1, 2, 0, 4);
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof_plus, true, 1, 1, 2, 0, 4); // 4 proofs with 1, 2, 4, 8 amounts
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof_plus, false, 1, 8, 1, 1, 4);
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof_plus, true, 1, 8, 1, 1, 4); // 32 proofs, with 1, 2, 3, 4 amounts, 8 of each
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof_plus, false, 2, 1, 1, 0, 64);
TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof_plus, true, 2, 1, 1, 0, 64); // 64 proof, each with 2 amounts
TEST_PERFORMANCE2(filter, p, test_bulletproof, true, 1); // 1 bulletproof with 1 amount TEST_PERFORMANCE2(filter, p, test_bulletproof, true, 1); // 1 bulletproof with 1 amount
TEST_PERFORMANCE2(filter, p, test_bulletproof, false, 1); TEST_PERFORMANCE2(filter, p, test_bulletproof, false, 1);

@ -560,7 +560,7 @@ static void expand_tsx(cryptonote::transaction &tx)
rv.p.MGs[n].II[0] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image); rv.p.MGs[n].II[0] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
} }
} }
else if (rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus) else if (rv.type == rct::RCTTypeCLSAG)
{ {
if (!tx.pruned) if (!tx.pruned)
{ {

@ -36,7 +36,6 @@ set(unit_tests_sources
block_reward.cpp block_reward.cpp
bootstrap_node_selector.cpp bootstrap_node_selector.cpp
bulletproofs.cpp bulletproofs.cpp
bulletproofs_plus.cpp
canonical_amounts.cpp canonical_amounts.cpp
chacha.cpp chacha.cpp
checkpoints.cpp checkpoints.cpp

@ -131,7 +131,7 @@ TEST(bulletproofs, multi_splitting)
} }
rct::ctkeyV outSk; rct::ctkeyV outSk;
rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 4 }; rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 0 };
rct::rctSig s = rct::genRctSimple(rct::zero(), sc, destinations, inamounts, outamounts, available, mixRing, amount_keys, index, outSk, rct_config, hw::get_device("default")); rct::rctSig s = rct::genRctSimple(rct::zero(), sc, destinations, inamounts, outamounts, available, mixRing, amount_keys, index, outSk, rct_config, hw::get_device("default"));
ASSERT_TRUE(rct::verRctSimple(s)); ASSERT_TRUE(rct::verRctSimple(s));

Loading…
Cancel
Save