support old BP

This commit is contained in:
wowario 2023-01-07 18:36:01 +03:00
parent 47f78dc11a
commit a636325df3
No known key found for this signature in database
GPG key ID: 24DCBE762DE9C111
15 changed files with 1566 additions and 86 deletions

View file

@ -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;
} }

View file

@ -192,7 +192,7 @@ namespace cryptonote
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] = rv.outPk[i].mask; rv.p.bulletproofs_plus[0].V[i] = rv.outPk[i].mask;
} }
else if (bulletproof) 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");

View file

@ -3201,7 +3201,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)
@ -3213,7 +3213,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)
@ -3225,7 +3225,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)
@ -3237,7 +3237,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)
@ -3249,7 +3249,21 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
} }
} }
// from v15, allow bulletproofs plus
// 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);
@ -3262,7 +3276,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);
@ -3275,7 +3289,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;
@ -3307,7 +3321,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());
@ -3322,7 +3336,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());
@ -3341,7 +3355,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)
{ {
@ -3351,7 +3365,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)
{ {
@ -3658,6 +3672,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:
@ -3725,6 +3740,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
{ {

View file

@ -859,7 +859,6 @@ namespace cryptonote
tvc.m_verifivation_failed = true; tvc.m_verifivation_failed = true;
return false; return false;
} }
return true; return true;
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
@ -928,6 +927,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 +938,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");

View file

@ -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

View file

@ -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;

View file

@ -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}")

View file

@ -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

View file

@ -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);
} }

1165
src/ringct/bulletproofs2.cc Normal file

File diff suppressed because it is too large Load diff

View file

@ -133,6 +133,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); }
@ -147,6 +165,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");
@ -617,7 +649,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)
@ -1045,6 +1077,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");
@ -1054,9 +1087,10 @@ 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());
if (!bulletproof)
rv.p.rangeSigs.resize(destinations.size()); rv.p.rangeSigs.resize(destinations.size());
rv.ecdhInfo.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size());
@ -1067,10 +1101,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
if (!bulletproof)
{
rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); 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]);
@ -1312,6 +1381,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
@ -1324,10 +1485,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");
} }
@ -1342,10 +1506,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?");
if (rct::is_rct_new_bulletproof(rv.type))
{
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++) 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]); }); 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;
@ -1635,7 +1812,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");

View file

@ -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);

View file

@ -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)
@ -239,6 +259,26 @@ namespace rct {
} }
} }
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)
{ {
switch (type) switch (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,6 +318,43 @@ namespace rct {
return n; return n;
} }
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;
for (const Bulletproof &proof: proofs)
{
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");
if (n2 == 0)
return 0;
n += n2;
}
return n;
}
size_t n_bulletproof_plus_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");
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_plus_amounts(const std::vector<BulletproofPlus> &proofs) size_t n_bulletproof_plus_amounts(const std::vector<BulletproofPlus> &proofs)
{ {
size_t n = 0; size_t n = 0;
@ -295,30 +369,14 @@ 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_max_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); return 1 << (proof.L.size() - 6);
}
size_t n_bulletproof_max_amounts(const Bulletproof &proof) { return n_bulletproof_max_amounts_base(proof.L.size(), proof.R.size(), BULLETPROOF_MAX_OUTPUTS); }
size_t n_bulletproof_plus_max_amounts(const BulletproofPlus &proof) { return n_bulletproof_max_amounts_base(proof.L.size(), proof.R.size(), BULLETPROOF_PLUS_MAX_OUTPUTS); }
size_t n_bulletproof_max_amounts(const std::vector<Bulletproof> &proofs)
{
size_t n = 0;
for (const Bulletproof &proof: proofs)
{
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");
if (n2 == 0)
return 0;
n += n2;
}
return n;
} }
size_t n_bulletproof_plus_max_amounts(const std::vector<BulletproofPlus> &proofs) size_t n_bulletproof_plus_max_amounts(const std::vector<BulletproofPlus> &proofs)

View file

@ -271,8 +271,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 +293,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 +327,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,9 +419,24 @@ 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)
@ -520,7 +539,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 +557,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 +584,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 +616,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 +733,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);

View file

@ -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);