Skip to content

Commit

Permalink
Merge pull request monero-project#7170
Browse files Browse the repository at this point in the history
bd27deb Bulletproofs+ (SarangNoether)
b7713cc Precompute initial transcript hash (SarangNoether)
b535d66 Updates from security audit (SarangNoether)
a0d80b1 plug bulletproofs plus into consensus (moneromooo-monero)
75bd004 ringct: a few minor optimizations from review (moneromooo-monero)
a345060 ringct: port some of vtnerd's review changes from BP+ to BP (moneromooo-monero)
4c94cfe store outPk/8 in the tx for speed (moneromooo-monero)
5acdd0e bulletproofs+: some minor cleanup from vtnerd's review (moneromooo-monero)
  • Loading branch information
luigi1111 committed Apr 6, 2022
2 parents fb2f822 + 5acdd0e commit d054def
Show file tree
Hide file tree
Showing 37 changed files with 2,674 additions and 165 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,5 @@ nbproject
/testnet

__pycache__/
*.pyc
*.log
9 changes: 8 additions & 1 deletion src/blockchain_db/blockchain_db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,15 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair
}
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,
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);
Expand Down
32 changes: 27 additions & 5 deletions src/cryptonote_basic/cryptonote_boost_serialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,20 @@ namespace boost
a & x.t;
}

template <class Archive>
inline void serialize(Archive &a, rct::BulletproofPlus &x, const boost::serialization::version_type ver)
{
a & x.V;
a & x.A;
a & x.A1;
a & x.B;
a & x.r1;
a & x.s1;
a & x.d1;
a & x.L;
a & x.R;
}

template <class Archive>
inline void serialize(Archive &a, rct::boroSig &x, const boost::serialization::version_type ver)
{
Expand Down Expand Up @@ -305,7 +319,7 @@ namespace boost
a & x.type;
if (x.type == rct::RCTTypeNull)
return;
if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG)
if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG && x.type != rct::RCTTypeBulletproofPlus)
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
// a & x.message; message is not serialized, as it can be reconstructed from the tx data
// a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
Expand All @@ -321,7 +335,11 @@ namespace boost
{
a & x.rangeSigs;
if (x.rangeSigs.empty())
{
a & x.bulletproofs;
if (ver >= 2u)
a & x.bulletproofs_plus;
}
a & x.MGs;
if (ver >= 1u)
a & x.CLSAGs;
Expand All @@ -335,7 +353,7 @@ namespace boost
a & x.type;
if (x.type == rct::RCTTypeNull)
return;
if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG)
if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG && x.type != rct::RCTTypeBulletproofPlus)
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
// a & x.message; message is not serialized, as it can be reconstructed from the tx data
// a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
Expand All @@ -347,11 +365,15 @@ namespace boost
//--------------
a & x.p.rangeSigs;
if (x.p.rangeSigs.empty())
{
a & x.p.bulletproofs;
if (ver >= 2u)
a & x.p.bulletproofs_plus;
}
a & x.p.MGs;
if (ver >= 1u)
a & x.p.CLSAGs;
if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2 || x.type == rct::RCTTypeCLSAG)
if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2 || x.type == rct::RCTTypeCLSAG || x.type == rct::RCTTypeBulletproofPlus)
a & x.p.pseudoOuts;
}

Expand Down Expand Up @@ -392,6 +414,6 @@ namespace boost
}
}

BOOST_CLASS_VERSION(rct::rctSigPrunable, 1)
BOOST_CLASS_VERSION(rct::rctSig, 1)
BOOST_CLASS_VERSION(rct::rctSigPrunable, 2)
BOOST_CLASS_VERSION(rct::rctSig, 2)
BOOST_CLASS_VERSION(rct::multisig_out, 1)
47 changes: 38 additions & 9 deletions src/cryptonote_basic/cryptonote_format_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,17 @@ namespace cryptonote

uint64_t get_transaction_weight_clawback(const transaction &tx, size_t n_padded_outputs)
{
const uint64_t bp_base = 368;
const rct::rctSig &rv = tx.rct_signatures;
const bool plus = rv.type == rct::RCTTypeBulletproofPlus;
const uint64_t bp_base = (32 * ((plus ? 6 : 9) + 7 * 2)) / 2; // notional size of a 2 output proof, normalized to 1 proof (ie, divided by 2)
const size_t n_outputs = tx.vout.size();
if (n_padded_outputs <= 2)
return 0;
size_t nlr = 0;
while ((1u << nlr) < n_padded_outputs)
++nlr;
nlr += 6;
const size_t bp_size = 32 * (9 + 2 * nlr);
const size_t bp_size = 32 * ((plus ? 6 : 9) + 2 * nlr);
CHECK_AND_ASSERT_THROW_MES_L1(n_outputs <= BULLETPROOF_MAX_OUTPUTS, "maximum number of outputs is " + std::to_string(BULLETPROOF_MAX_OUTPUTS) + " per transaction");
CHECK_AND_ASSERT_THROW_MES_L1(bp_base * n_padded_outputs >= bp_size, "Invalid bulletproof clawback: bp_base " + std::to_string(bp_base) + ", n_padded_outputs "
+ std::to_string(n_padded_outputs) + ", bp_size " + std::to_string(bp_size));
Expand Down Expand Up @@ -164,7 +166,32 @@ namespace cryptonote
if (!base_only)
{
const bool bulletproof = rct::is_rct_bulletproof(rv.type);
if (bulletproof)
const bool bulletproof_plus = rct::is_rct_bulletproof_plus(rv.type);
if (bulletproof_plus)
{
if (rv.p.bulletproofs_plus.size() != 1)
{
LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs_plus size in tx " << get_transaction_hash(tx));
return false;
}
if (rv.p.bulletproofs_plus[0].L.size() < 6)
{
LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs_plus L size in tx " << get_transaction_hash(tx));
return false;
}
const size_t max_outputs = rct::n_bulletproof_plus_max_amounts(rv.p.bulletproofs_plus[0]);
if (max_outputs < tx.vout.size())
{
LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs_plus max outputs in tx " << get_transaction_hash(tx));
return false;
}
const size_t n_amounts = tx.vout.size();
CHECK_AND_ASSERT_MES(n_amounts == rv.outPk.size(), false, "Internal error filling out V");
rv.p.bulletproofs_plus[0].V.resize(n_amounts);
for (size_t i = 0; i < n_amounts; ++i)
rv.p.bulletproofs_plus[0].V[i] = rv.outPk[i].mask;
}
else if (bulletproof)
{
if (rv.p.bulletproofs.size() != 1)
{
Expand Down Expand Up @@ -419,9 +446,11 @@ namespace cryptonote
if (tx.version < 2)
return blob_size;
const rct::rctSig &rv = tx.rct_signatures;
if (!rct::is_rct_bulletproof(rv.type))
const bool bulletproof = rct::is_rct_bulletproof(rv.type);
const bool bulletproof_plus = rct::is_rct_bulletproof_plus(rv.type);
if (!bulletproof && !bulletproof_plus)
return blob_size;
const size_t n_padded_outputs = rct::n_bulletproof_max_amounts(rv.p.bulletproofs);
const size_t n_padded_outputs = bulletproof_plus ? rct::n_bulletproof_plus_max_amounts(rv.p.bulletproofs_plus) : rct::n_bulletproof_max_amounts(rv.p.bulletproofs);
uint64_t bp_clawback = get_transaction_weight_clawback(tx, n_padded_outputs);
CHECK_AND_ASSERT_THROW_MES_L1(bp_clawback <= std::numeric_limits<uint64_t>::max() - blob_size, "Weight overflow");
return blob_size + bp_clawback;
Expand All @@ -431,8 +460,8 @@ namespace cryptonote
{
CHECK_AND_ASSERT_MES(tx.pruned, std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support non pruned txes");
CHECK_AND_ASSERT_MES(tx.version >= 2, std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support v1 txes");
CHECK_AND_ASSERT_MES(tx.rct_signatures.type >= rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG,
std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support older range proof types");
CHECK_AND_ASSERT_MES(tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus,
std::numeric_limits<uint64_t>::max(), "Unsupported rct_signatures type in get_pruned_transaction_weight");
CHECK_AND_ASSERT_MES(!tx.vin.empty(), std::numeric_limits<uint64_t>::max(), "empty vin");
CHECK_AND_ASSERT_MES(tx.vin[0].type() == typeid(cryptonote::txin_to_key), std::numeric_limits<uint64_t>::max(), "empty vin");

Expand All @@ -450,12 +479,12 @@ namespace cryptonote
while ((n_padded_outputs = (1u << nrl)) < tx.vout.size())
++nrl;
nrl += 6;
extra = 32 * (9 + 2 * nrl) + 2;
extra = 32 * ((rct::is_rct_bulletproof_plus(tx.rct_signatures.type) ? 6 : 9) + 2 * nrl) + 2;
weight += extra;

// calculate deterministic CLSAG/MLSAG data size
const size_t ring_size = boost::get<cryptonote::txin_to_key>(tx.vin[0]).key_offsets.size();
if (tx.rct_signatures.type == rct::RCTTypeCLSAG)
if (rct::is_rct_clsag(tx.rct_signatures.type))
extra = tx.vin.size() * (ring_size + 2) * 32;
else
extra = tx.vin.size() * (ring_size * (1 + 1) * 32 + 32 /* cc */);
Expand Down
4 changes: 4 additions & 0 deletions src/cryptonote_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@
#define HF_VERSION_EXACT_COINBASE 13
#define HF_VERSION_CLSAG 13
#define HF_VERSION_DETERMINISTIC_UNLOCK_TIME 13
#define HF_VERSION_BULLETPROOF_PLUS 15

#define PER_KB_FEE_QUANTIZATION_DECIMALS 8

Expand All @@ -190,6 +191,7 @@
#define DEFAULT_TXPOOL_MAX_WEIGHT 648000000ull // 3 days at 300000, in bytes

#define BULLETPROOF_MAX_OUTPUTS 16
#define BULLETPROOF_PLUS_MAX_OUTPUTS 16

#define CRYPTONOTE_PRUNING_STRIPE_SIZE 4096 // the smaller, the smoother the increase
#define CRYPTONOTE_PRUNING_LOG_STRIPES 3 // the higher, the more space saved
Expand Down Expand Up @@ -221,6 +223,8 @@ namespace config

// Hash domain separators
const char HASH_KEY_BULLETPROOF_EXPONENT[] = "bulletproof";
const char HASH_KEY_BULLETPROOF_PLUS_EXPONENT[] = "bulletproof_plus";
const char HASH_KEY_BULLETPROOF_PLUS_TRANSCRIPT[] = "bulletproof_plus_transcript";
const char HASH_KEY_RINGDB[] = "ringdsb";
const char HASH_KEY_SUBADDRESS[] = "SubAddr";
const unsigned char HASH_KEY_ENCRYPTED_PAYMENT_ID = 0x8d;
Expand Down
35 changes: 31 additions & 4 deletions src/cryptonote_core/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3143,6 +3143,32 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
}
}

// from v15, allow bulletproofs plus
if (hf_version < HF_VERSION_BULLETPROOF_PLUS) {
if (tx.version >= 2) {
const bool bulletproof_plus = rct::is_rct_bulletproof_plus(tx.rct_signatures.type);
if (bulletproof_plus || !tx.rct_signatures.p.bulletproofs_plus.empty())
{
MERROR_VER("Bulletproofs plus are not allowed before v" << std::to_string(HF_VERSION_BULLETPROOF_PLUS));
tvc.m_invalid_output = true;
return false;
}
}
}

// from v16, forbid bulletproofs
if (hf_version > HF_VERSION_BULLETPROOF_PLUS) {
if (tx.version >= 2) {
const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type);
if (bulletproof)
{
MERROR_VER("Bulletproof range proofs are not allowed after v" + std::to_string(HF_VERSION_BULLETPROOF_PLUS));
tvc.m_invalid_output = true;
return false;
}
}
}

return true;
}
//------------------------------------------------------------------
Expand Down Expand Up @@ -3183,7 +3209,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
}
}
else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeCLSAG)
else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus)
{
CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys.size());
Expand Down Expand Up @@ -3224,7 +3250,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
}
}
else if (rv.type == rct::RCTTypeCLSAG)
else if (rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus)
{
if (!tx.pruned)
{
Expand Down Expand Up @@ -3516,6 +3542,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
case rct::RCTTypeBulletproof:
case rct::RCTTypeBulletproof2:
case rct::RCTTypeCLSAG:
case rct::RCTTypeBulletproofPlus:
{
// check all this, either reconstructed (so should really pass), or not
{
Expand Down Expand Up @@ -3551,7 +3578,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
}

const size_t n_sigs = rv.type == rct::RCTTypeCLSAG ? rv.p.CLSAGs.size() : rv.p.MGs.size();
const size_t n_sigs = rct::is_rct_clsag(rv.type) ? rv.p.CLSAGs.size() : rv.p.MGs.size();
if (n_sigs != tx.vin.size())
{
MERROR_VER("Failed to check ringct signatures: mismatched MGs/vin sizes");
Expand All @@ -3560,7 +3587,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
for (size_t n = 0; n < tx.vin.size(); ++n)
{
bool error;
if (rv.type == rct::RCTTypeCLSAG)
if (rct::is_rct_clsag(rv.type))
error = memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.CLSAGs[n].I, 32);
else
error = rv.p.MGs[n].II.empty() || memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32);
Expand Down
23 changes: 22 additions & 1 deletion src/cryptonote_core/cryptonote_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,16 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
static bool is_canonical_bulletproof_plus_layout(const std::vector<rct::BulletproofPlus> &proofs)
{
if (proofs.size() != 1)
return false;
const size_t sz = proofs[0].V.size();
if (sz == 0 || sz > BULLETPROOF_PLUS_MAX_OUTPUTS)
return false;
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::handle_incoming_tx_accumulated_batch(std::vector<tx_verification_batch_info> &tx_info, bool keeped_by_block)
{
bool ret = true;
Expand Down Expand Up @@ -943,6 +953,17 @@ namespace cryptonote
}
rvv.push_back(&rv); // delayed batch verification
break;
case rct::RCTTypeBulletproofPlus:
if (!is_canonical_bulletproof_plus_layout(rv.p.bulletproofs_plus))
{
MERROR_VER("Bulletproof_plus does not have canonical form");
set_semantics_failed(tx_info[n].tx_hash);
tx_info[n].tvc.m_verifivation_failed = true;
tx_info[n].result = false;
break;
}
rvv.push_back(&rv); // delayed batch verification
break;
default:
MERROR_VER("Unknown rct type: " << rv.type);
set_semantics_failed(tx_info[n].tx_hash);
Expand All @@ -960,7 +981,7 @@ namespace cryptonote
{
if (!tx_info[n].result)
continue;
if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2 && tx_info[n].tx->rct_signatures.type != rct::RCTTypeCLSAG)
if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2 && tx_info[n].tx->rct_signatures.type != rct::RCTTypeCLSAG && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproofPlus)
continue;
if (assumed_bad || !rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures))
{
Expand Down
3 changes: 3 additions & 0 deletions src/hardforks/hardforks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ const hardfork_t mainnet_hard_forks[] = {

{ 13, 2210000, 0, 1598180817 },
{ 14, 2210720, 0, 1598180818 },

{ 15, 8000000, 0, 1608223241 }, // temp so tests test with these consensus rules
{ 16, 8000001, 0, 1608223242 }, // temp so tests test with these consensus rules
};
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
const uint64_t mainnet_hard_fork_version_1_till = 1009826;
Expand Down
6 changes: 4 additions & 2 deletions src/ringct/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ set(ringct_basic_sources
rctTypes.cpp
rctCryptoOps.c
multiexp.cc
bulletproofs.cc)
bulletproofs.cc
bulletproofs_plus.cc)

set(ringct_basic_private_headers
rctOps.h
rctTypes.h
multiexp.h
bulletproofs.h)
bulletproofs.h
bulletproofs_plus.h)

monero_private_headers(ringct_basic
${crypto_private_headers})
Expand Down
Loading

0 comments on commit d054def

Please sign in to comment.