diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 4b0c07496..2daddcfe8 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -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); diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 4bbac1301..b2a42a038 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -335,7 +335,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 && 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"); // 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 @@ -369,7 +369,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 && 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"); // 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 @@ -389,7 +389,7 @@ namespace boost a & x.p.MGs; if (ver >= 1u) 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; } diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 0d7c10b1f..d9445451d 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -166,7 +166,6 @@ namespace cryptonote if (!base_only) { - const bool bulletproof = rct::is_rct_bulletproof(rv.type); const bool bulletproof_plus = rct::is_rct_bulletproof_plus(rv.type); 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)); 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()) { 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"); rv.p.bulletproofs_plus[0].V.resize(n_amounts); for (size_t i = 0; i < n_amounts; ++i) - rv.p.bulletproofs_plus[0].V[i] = rct::scalarmultKey(rv.outPk[i].mask, rct::INV_EIGHT); + 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) { @@ -205,7 +205,7 @@ namespace cryptonote return false; } 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)); return false; @@ -216,6 +216,26 @@ namespace cryptonote for (size_t i = 0; i < n_amounts; ++i) 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; @@ -451,6 +471,11 @@ namespace cryptonote const bool bulletproof_plus = rct::is_rct_bulletproof_plus(rv.type); if (!bulletproof && !bulletproof_plus) 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); uint64_t bp_clawback = get_transaction_weight_clawback(tx, n_padded_outputs); CHECK_AND_ASSERT_THROW_MES_L1(bp_clawback <= std::numeric_limits::max() - blob_size, "Weight overflow"); @@ -462,7 +487,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(tx.pruned, std::numeric_limits::max(), "get_pruned_transaction_weight does not support non pruned txes"); CHECK_AND_ASSERT_MES(tx.version >= 2, std::numeric_limits::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, - std::numeric_limits::max(), "Unsupported rct_signatures type in get_pruned_transaction_weight"); + std::numeric_limits::max(), "get_pruned_transaction_weight does not support older range proof types"); CHECK_AND_ASSERT_MES(!tx.vin.empty(), std::numeric_limits::max(), "empty vin"); CHECK_AND_ASSERT_MES(tx.vin[0].type() == typeid(cryptonote::txin_to_key), std::numeric_limits::max(), "empty vin"); @@ -485,7 +510,7 @@ namespace cryptonote // calculate deterministic CLSAG/MLSAG data size const size_t ring_size = boost::get(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; else extra = tx.vin.size() * (ring_size * (1 + 1) * 32 + 32 /* cc */); diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 83cc92670..921b927ba 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3166,13 +3166,13 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } } - // from v11, allow bulletproofs - if (hf_version < 11) { + // from v8, allow bulletproofs + if (hf_version < 8) { if (tx.version >= 2) { const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type); 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; 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 (tx.version >= 2) { 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 (tx.version >= 2) { 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 (tx.version >= 2) { 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 (tx.version >= 2) { 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 (tx.version >= 2) { 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 (tx.version >= 2) { 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)) { 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); // 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"); 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"); rv.mixRing.resize(pubkeys.size()); @@ -3332,7 +3352,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } // II - if (rv.type == rct::RCTTypeFull) + if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof) { 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(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) { @@ -3649,6 +3669,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, return false; } case rct::RCTTypeSimple: + case rct::RCTTypeSimpleBulletproof: case rct::RCTTypeBulletproof: case rct::RCTTypeBulletproof2: 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()) { 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) { bool error; - if (rct::is_rct_clsag(rv.type)) + if (rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus) error = memcmp(&boost::get(tx.vin[n]).k_image, &rv.p.CLSAGs[n].I, 32); else error = rv.p.MGs[n].II.empty() || memcmp(&boost::get(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; } case rct::RCTTypeFull: + case rct::RCTTypeFullBulletproof: { // 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 if (rct::is_rct_bulletproof(rv.type)) { - if (hf_version < 11) + if (hf_version < 8) { for (const rct::Bulletproof &proof: rv.p.bulletproofs) { 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; } } diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index adb143ebc..d797bf0cc 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -860,6 +860,44 @@ namespace cryptonote 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(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; } //----------------------------------------------------------------------------------------------- @@ -928,6 +966,7 @@ namespace cryptonote tx_info[n].result = false; break; case rct::RCTTypeSimple: + case rct::RCTTypeSimpleBulletproof: if (!rct::verRctSemanticsSimple(rv)) { MERROR_VER("rct signature semantics check failed"); @@ -938,6 +977,7 @@ namespace cryptonote } break; case rct::RCTTypeFull: + case rct::RCTTypeFullBulletproof: if (!rct::verRct(rv, true)) { MERROR_VER("rct signature semantics check failed"); diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 7cb45c2c9..99c7486c5 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -601,7 +601,9 @@ namespace cryptonote crypto::hash tx_prefix_hash; get_transaction_prefix_hash(tx, tx_prefix_hash, hwdev); 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); 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 diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 16403b4cf..b8172510f 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -112,8 +112,8 @@ namespace cryptonote uint64_t get_transaction_weight_limit(uint8_t version) { - // from v8, limit a tx to 50% of the minimum block weight - if (version >= 8) + // from v12, limit a tx to 50% of the minimum block weight + if (version >= 12) return get_min_block_weight(version) / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; else return get_min_block_weight(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 22a1fd986..f873999a3 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -1931,7 +1931,7 @@ namespace hw { // ====== Aout, Bout, AKout, C, v, k ====== 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; } else { C_offset = kv_offset+ (32+32)*outputs_size; @@ -1948,7 +1948,7 @@ namespace hw { offset = set_command_header(INS_VALIDATE, 0x02, i+1); //options 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; //is_subaddress this->buffer_send[offset] = outKeys.is_subaddress; @@ -1969,7 +1969,7 @@ namespace hw { memmove(this->buffer_send+offset, data+C_offset,32); offset += 32; C_offset += 32; - if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG || type==rct::RCTTypeBulletproofPlus) { + if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG) { //k memset(this->buffer_send+offset, 0, 32); offset += 32; diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt index d415e6409..02ec7da35 100644 --- a/src/ringct/CMakeLists.txt +++ b/src/ringct/CMakeLists.txt @@ -32,6 +32,7 @@ set(ringct_basic_sources rctCryptoOps.c multiexp.cc bulletproofs.cc + bulletproofs2.cc bulletproofs_plus.cc) monero_find_all_headers(ringct_basic_private_headers "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index ba7ab2c0d..f4e03c381 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -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 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 std::shared_ptr straus_HiGi_cache; static std::shared_ptr 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 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 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 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 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_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 twoN = vector_powers(TWO, maxN); 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) { - 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; ge_p3 e_p3; 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); for (size_t i = 0; i < maxN*maxM; ++i) { - const rct::key Hi = get_exponent(rct::H, i * 2); - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Hi_p3[i], Hi.bytes) == 0, "ge_frombytes_vartime failed"); - const rct::key Gi = 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"); + Hi[i] = get_exponent(rct::H, i * 2); + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Hi_p3[i], Hi[i].bytes) == 0, "ge_frombytes_vartime failed"); + Gi[i] = get_exponent(rct::H, i * 2 + 1); + 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(), Hi_p3[i]}); @@ -131,10 +133,11 @@ static void init_exponents() straus_HiGi_cache = straus_init_cache(data, STRAUS_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("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"); - 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"); init_done = true; } @@ -892,8 +895,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) multiexp_data.resize(2 * maxMN); PERF_TIMER_START_BP(VERIFY_line_24_25_invert); - const std::vector inverses = invert(std::move(to_invert)); - to_invert.clear(); + const std::vector inverses = invert(to_invert); PERF_TIMER_STOP_BP(VERIFY_line_24_25_invert); // setup weighted aggregates diff --git a/src/ringct/bulletproofs.h b/src/ringct/bulletproofs.h index c3fe6f8b9..d137f82dc 100644 --- a/src/ringct/bulletproofs.h +++ b/src/ringct/bulletproofs.h @@ -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(const rct::keyV &v, const rct::keyV &gamma); Bulletproof bulletproof_PROVE(const std::vector &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 &v, const rct::keyV &gamma); bool bulletproof_VERIFY(const Bulletproof &proof); bool bulletproof_VERIFY(const std::vector &proofs); bool bulletproof_VERIFY(const std::vector &proofs); +bool bulletproof_VERIFY_old(const Bulletproof &proof); +bool bulletproof_VERIFY_old(const std::vector &proofs); +bool bulletproof_VERIFY_old(const std::vector &proofs); } diff --git a/src/ringct/bulletproofs2.cc b/src/ringct/bulletproofs2.cc new file mode 100644 index 000000000..524e2610b --- /dev/null +++ b/src/ringct/bulletproofs2.cc @@ -0,0 +1,1165 @@ +// Copyright (c) 2017-2018, The Monero And Italo Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Adapted from Java code by Sarang Noether + +#include +#include +#include +#include "misc_log_ex.h" +#include "common/perf_timer.h" +extern "C" +{ +#include "crypto/crypto-ops.h" +} +#include "rctOps.h" +#include "multiexp.h" +#include "bulletproofs.h" +#include + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "bulletproofs" + +//#define DEBUG_BP + +#define PERF_TIMER_START_BP(x) PERF_TIMER_START_UNIT(x, 1000000) + +namespace rct +{ + +static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b); +static rct::keyV vector_powers(const rct::key &x, size_t n); +static rct::keyV vector_dup(const rct::key &x, size_t n); +static rct::key inner_product(const rct::keyV &a, const rct::keyV &b); + +static constexpr size_t maxN = 64; +static constexpr size_t maxM = 16; +static rct::key Hi[maxN*maxM], Gi[maxN*maxM]; +static ge_p3 Hi_p3[maxN*maxM], Gi_p3[maxN*maxM]; +static ge_dsmp Gprecomp[maxN*maxM], Hprecomp[maxN*maxM]; +static std::shared_ptr HiGi_cache; +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 rct::keyV oneN = vector_dup(rct::identity(), maxN); +static const rct::keyV twoN = vector_powers(TWO, maxN); +static const rct::key ip12 = inner_product(oneN, twoN); +static boost::mutex init_mutex; + +static inline rct::key multiexp(const std::vector &data, bool HiGi) +{ + static const size_t STEP = getenv("STRAUS_STEP") ? atoi(getenv("STRAUS_STEP")) : 0; + if (HiGi || data.size() < 1000) + return straus(data, HiGi ? HiGi_cache: NULL, STEP); + else + return bos_coster_heap_conv_robust(data); +} + +//addKeys3acc_p3 +//aAbB += a*A + b*B where a, b are scalars, A, B are curve points +//A and B must be input after applying "precomp" +static void addKeys3acc_p3(ge_p3 *aAbB, const key &a, const ge_dsmp A, const key &b, const ge_dsmp B) +{ + ge_p3 rv; + ge_p1p1 p1; + ge_p2 p2; + ge_double_scalarmult_precomp_vartime2_p3(&rv, a.bytes, A, b.bytes, B); + ge_cached cached; + ge_p3_to_cached(&cached, aAbB); + ge_add(&p1, &rv, &cached); + ge_p1p1_to_p3(aAbB, &p1); +} + +static void addKeys_acc_p3(ge_p3 *acc_p3, const rct::key &a, const rct::key &point) +{ + ge_p3 p3; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&p3, point.bytes) == 0, "ge_frombytes_vartime failed"); + ge_scalarmult_p3(&p3, a.bytes, &p3); + ge_cached cached; + ge_p3_to_cached(&cached, acc_p3); + ge_p1p1 p1; + ge_add(&p1, &p3, &cached); + ge_p1p1_to_p3(acc_p3, &p1); +} + +static rct::key get_exponent(const rct::key &base, size_t idx) +{ + static const std::string salt("bulletproof"); + std::string hashed = std::string((const char*)base.bytes, sizeof(base)) + salt + tools::get_varint_data(idx); + rct::key e; + ge_p3 e_p3; + rct::hash_to_p3(e_p3, rct::hash2rct(crypto::cn_fast_hash(hashed.data(), hashed.size()))); + ge_p3_tobytes(e.bytes, &e_p3); + CHECK_AND_ASSERT_THROW_MES(!(e == rct::identity()), "Exponent is point at infinity"); + return e; +} + +static void init_exponents() +{ + boost::lock_guard lock(init_mutex); + + static bool init_done = false; + if (init_done) + return; + std::vector data; + for (size_t i = 0; i < maxN*maxM; ++i) + { + Hi[i] = get_exponent(rct::H, i * 2); + rct::precomp(Hprecomp[i], Hi[i]); + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Hi_p3[i], Hi[i].bytes) == 0, "ge_frombytes_vartime failed"); + Gi[i] = get_exponent(rct::H, i * 2 + 1); + rct::precomp(Gprecomp[i], Gi[i]); + 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[i]}); + data.push_back({rct::zero(), Hi[i]}); + } + HiGi_cache = straus_init_cache(data); + size_t cache_size = (sizeof(Hi)+sizeof(Hprecomp)+sizeof(Hi_p3))*2 + straus_get_cache_size(HiGi_cache); + MINFO("cache size: " << cache_size/1024 << " kB"); + init_done = true; +} + +/* Given two scalar arrays, construct a vector commitment */ +static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN*maxM, "Incompatible sizes of a and maxN"); + + std::vector multiexp_data; + multiexp_data.reserve(a.size()*2); + for (size_t i = 0; i < a.size(); ++i) + { + multiexp_data.emplace_back(a[i], Gi_p3[i]); + multiexp_data.emplace_back(b[i], Hi_p3[i]); + } + return multiexp(multiexp_data, true); +} + +/* Compute a custom vector-scalar commitment */ +static rct::key vector_exponent_custom(const rct::keyV &A, const rct::keyV &B, const rct::keyV &a, const rct::keyV &b) +{ + 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"); + CHECK_AND_ASSERT_THROW_MES(a.size() == A.size(), "Incompatible sizes of a and A"); + CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN*maxM, "Incompatible sizes of a and maxN"); + + std::vector multiexp_data; + multiexp_data.reserve(a.size()*2); + for (size_t i = 0; i < a.size(); ++i) + { + multiexp_data.resize(multiexp_data.size() + 1); + multiexp_data.back().scalar = a[i]; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&multiexp_data.back().point, A[i].bytes) == 0, "ge_frombytes_vartime failed"); + multiexp_data.resize(multiexp_data.size() + 1); + multiexp_data.back().scalar = b[i]; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&multiexp_data.back().point, B[i].bytes) == 0, "ge_frombytes_vartime failed"); + } + return multiexp(multiexp_data, false); +} + +/* Given a scalar, construct a vector of powers */ +static rct::keyV vector_powers(const rct::key &x, size_t n) +{ + rct::keyV res(n); + if (n == 0) + return res; + res[0] = rct::identity(); + if (n == 1) + return res; + res[1] = x; + for (size_t i = 2; i < n; ++i) + { + sc_mul(res[i].bytes, res[i-1].bytes, x.bytes); + } + return res; +} + +/* Given a scalar, return the sum of its powers from 0 to n-1 */ +static rct::key vector_power_sum(const rct::key &x, size_t n) +{ + if (n == 0) + return rct::zero(); + rct::key res = rct::identity(); + if (n == 1) + return res; + rct::key prev = x; + for (size_t i = 1; i < n; ++i) + { + if (i > 1) + sc_mul(prev.bytes, prev.bytes, x.bytes); + sc_add(res.bytes, res.bytes, prev.bytes); + } + return res; +} + +/* Given two scalar arrays, construct the inner product */ +static rct::key inner_product(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::key res = rct::zero(); + for (size_t i = 0; i < a.size(); ++i) + { + sc_muladd(res.bytes, a[i].bytes, b[i].bytes, res.bytes); + } + return res; +} + +/* Given two scalar arrays, construct the Hadamard product */ +static rct::keyV hadamard(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_mul(res[i].bytes, a[i].bytes, b[i].bytes); + } + return res; +} + +/* Given two curvepoint arrays, construct the Hadamard product */ +static rct::keyV hadamard2(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + rct::addKeys(res[i], a[i], b[i]); + } + return res; +} + +/* Add two vectors */ +static rct::keyV vector_add(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_add(res[i].bytes, a[i].bytes, b[i].bytes); + } + return res; +} + +/* Subtract two vectors */ +static rct::keyV vector_subtract(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_sub(res[i].bytes, a[i].bytes, b[i].bytes); + } + return res; +} + +/* Multiply a scalar and a vector */ +static rct::keyV vector_scalar(const rct::keyV &a, const rct::key &x) +{ + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_mul(res[i].bytes, a[i].bytes, x.bytes); + } + return res; +} + +/* Create a vector from copies of a single value */ +static rct::keyV vector_dup(const rct::key &x, size_t N) +{ + return rct::keyV(N, x); +} + +/* Exponentiate a curve vector by a scalar */ +static rct::keyV vector_scalar2(const rct::keyV &a, const rct::key &x) +{ + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + rct::scalarmultKey(res[i], a[i], x); + } + return res; +} + +/* Get the sum of a vector's elements */ +static rct::key vector_sum(const rct::keyV &a) +{ + rct::key res = rct::zero(); + for (size_t i = 0; i < a.size(); ++i) + { + sc_add(res.bytes, res.bytes, a[i].bytes); + } + return res; +} + +static rct::key switch_endianness(rct::key k) +{ + std::reverse(k.bytes, k.bytes + sizeof(k)); + return k; +} + +/* Compute the inverse of a scalar, the stupid way */ +static rct::key invert(const rct::key &x) +{ + rct::key inv; + + BN_CTX *ctx = BN_CTX_new(); + BIGNUM *X = BN_new(); + BIGNUM *L = BN_new(); + BIGNUM *I = BN_new(); + + BN_bin2bn(switch_endianness(x).bytes, sizeof(rct::key), X); + BN_bin2bn(switch_endianness(rct::curveOrder()).bytes, sizeof(rct::key), L); + + CHECK_AND_ASSERT_THROW_MES(BN_mod_inverse(I, X, L, ctx), "Failed to invert"); + + const int len = BN_num_bytes(I); + CHECK_AND_ASSERT_THROW_MES((size_t)len <= sizeof(rct::key), "Invalid number length"); + inv = rct::zero(); + BN_bn2bin(I, inv.bytes); + std::reverse(inv.bytes, inv.bytes + len); + + BN_free(I); + BN_free(L); + BN_free(X); + BN_CTX_free(ctx); + +#ifdef DEBUG_BP + rct::key tmp; + sc_mul(tmp.bytes, inv.bytes, x.bytes); + CHECK_AND_ASSERT_THROW_MES(tmp == rct::identity(), "invert failed"); +#endif + return inv; +} + +/* Compute the slice of a vector */ +static rct::keyV slice(const rct::keyV &a, size_t start, size_t stop) +{ + CHECK_AND_ASSERT_THROW_MES(start < a.size(), "Invalid start index"); + CHECK_AND_ASSERT_THROW_MES(stop <= a.size(), "Invalid stop index"); + CHECK_AND_ASSERT_THROW_MES(start < stop, "Invalid start/stop indices"); + rct::keyV res(stop - start); + for (size_t i = start; i < stop; ++i) + { + res[i - start] = a[i]; + } + return res; +} + +static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1) +{ + rct::keyV data; + data.reserve(3); + data.push_back(hash_cache); + data.push_back(mash0); + data.push_back(mash1); + return hash_cache = rct::hash_to_scalar(data); +} + +static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1, const rct::key &mash2) +{ + rct::keyV data; + data.reserve(4); + data.push_back(hash_cache); + data.push_back(mash0); + data.push_back(mash1); + data.push_back(mash2); + return hash_cache = rct::hash_to_scalar(data); +} + +static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1, const rct::key &mash2, const rct::key &mash3) +{ + rct::keyV data; + data.reserve(5); + data.push_back(hash_cache); + data.push_back(mash0); + data.push_back(mash1); + data.push_back(mash2); + data.push_back(mash3); + return hash_cache = rct::hash_to_scalar(data); +} + +/* Given a value v (0..2^N-1) and a mask gamma, construct a range proof */ +Bulletproof bulletproof_PROVE_old(const rct::key &sv, const rct::key &gamma) +{ + init_exponents(); + + PERF_TIMER_UNIT(PROVE, 1000000); + + constexpr size_t logN = 6; // log2(64) + constexpr size_t N = 1< 0; ) + { + if (sv[i/8] & (((uint64_t)1)<<(i%8))) + { + aL[i] = rct::identity(); + } + else + { + aL[i] = rct::zero(); + } + sc_sub(aR[i].bytes, aL[i].bytes, rct::identity().bytes); + } + PERF_TIMER_STOP(PROVE_aLaR); + + rct::key hash_cache = rct::hash_to_scalar(V); + + // DEBUG: Test to ensure this recovers the value +#ifdef DEBUG_BP + uint64_t test_aL = 0, test_aR = 0; + for (size_t i = 0; i < N; ++i) + { + if (aL[i] == rct::identity()) + test_aL += ((uint64_t)1)< 1) + { + // PAPER LINE 15 + nprime /= 2; + + // PAPER LINES 16-17 + rct::key cL = inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); + rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + + // PAPER LINES 18-19 + L[round] = vector_exponent_custom(slice(Gprime, nprime, Gprime.size()), slice(Hprime, 0, nprime), slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); + sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); + rct::addKeys(L[round], L[round], rct::scalarmultKey(rct::H, tmp)); + R[round] = vector_exponent_custom(slice(Gprime, 0, nprime), slice(Hprime, nprime, Hprime.size()), slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); + rct::addKeys(R[round], R[round], rct::scalarmultKey(rct::H, tmp)); + + // PAPER LINES 21-22 + w[round] = hash_cache_mash(hash_cache, L[round], R[round]); + + // PAPER LINES 24-25 + const rct::key winv = invert(w[round]); + Gprime = hadamard2(vector_scalar2(slice(Gprime, 0, nprime), winv), vector_scalar2(slice(Gprime, nprime, Gprime.size()), w[round])); + Hprime = hadamard2(vector_scalar2(slice(Hprime, 0, nprime), w[round]), vector_scalar2(slice(Hprime, nprime, Hprime.size()), winv)); + + // PAPER LINES 28-29 + aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv)); + bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round])); + + ++round; + } + PERF_TIMER_STOP(PROVE_step4); + + // PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20) + return Bulletproof(V, A, S, T1, T2, taux, mu, L, R, aprime[0], bprime[0], t); +} + +Bulletproof bulletproof_PROVE_old(uint64_t v, const rct::key &gamma) +{ + // vG + gammaH + PERF_TIMER_START_BP(PROVE_v); + rct::key sv = rct::zero(); + sv.bytes[0] = v & 255; + sv.bytes[1] = (v >> 8) & 255; + sv.bytes[2] = (v >> 16) & 255; + sv.bytes[3] = (v >> 24) & 255; + sv.bytes[4] = (v >> 32) & 255; + sv.bytes[5] = (v >> 40) & 255; + sv.bytes[6] = (v >> 48) & 255; + sv.bytes[7] = (v >> 56) & 255; + PERF_TIMER_STOP(PROVE_v); + return bulletproof_PROVE_old(sv, gamma); +} + +/* Given a set of values v (0..2^N-1) and masks gamma, construct a range proof */ +Bulletproof bulletproof_PROVE_old(const rct::keyV &sv, const rct::keyV &gamma) +{ + CHECK_AND_ASSERT_THROW_MES(sv.size() == gamma.size(), "Incompatible sizes of sv and gamma"); + CHECK_AND_ASSERT_THROW_MES(!sv.empty(), "sv is empty"); + + init_exponents(); + + PERF_TIMER_UNIT(PROVE, 1000000); + + constexpr size_t logN = 6; // log2(64) + constexpr size_t N = 1< 0; ) + { + if (j >= sv.size()) + { + aL[j*N+i] = rct::zero(); + } + else if (sv[j][i/8] & (((uint64_t)1)<<(i%8))) + { + aL[j*N+i] = rct::identity(); + } + else + { + aL[j*N+i] = rct::zero(); + } + sc_sub(aR[j*N+i].bytes, aL[j*N+i].bytes, rct::identity().bytes); + } + } + PERF_TIMER_STOP(PROVE_aLaR); + + rct::key hash_cache = rct::hash_to_scalar(V); + + // DEBUG: Test to ensure this recovers the value +#ifdef DEBUG_BP + for (size_t j = 0; j < M; ++j) + { + uint64_t test_aL = 0, test_aR = 0; + for (size_t i = 0; i < N; ++i) + { + if (aL[j*N+i] == rct::identity()) + test_aL += ((uint64_t)1)<= (j-1)*N && i < j*N) + { + CHECK_AND_ASSERT_THROW_MES(1+j < zpow.size(), "invalid zpow index"); + CHECK_AND_ASSERT_THROW_MES(i-(j-1)*N < twoN.size(), "invalid twoN index"); + sc_muladd(zero_twos[i].bytes, zpow[1+j].bytes, twoN[i-(j-1)*N].bytes, zero_twos[i].bytes); + } + } + } + + rct::keyV r0 = vector_add(aR, zMN); + const auto yMN = vector_powers(y, MN); + r0 = hadamard(r0, yMN); + r0 = vector_add(r0, zero_twos); + rct::keyV r1 = hadamard(yMN, sR); + + // Polynomial construction before PAPER LINE 46 + rct::key t1_1 = inner_product(l0, r1); + rct::key t1_2 = inner_product(l1, r0); + rct::key t1; + sc_add(t1.bytes, t1_1.bytes, t1_2.bytes); + rct::key t2 = inner_product(l1, r1); + + PERF_TIMER_STOP(PROVE_step1); + + PERF_TIMER_START_BP(PROVE_step2); + // PAPER LINES 47-48 + rct::key tau1 = rct::skGen(), tau2 = rct::skGen(); + + rct::key T1 = rct::addKeys(rct::scalarmultKey(rct::H, t1), rct::scalarmultBase(tau1)); + rct::key T2 = rct::addKeys(rct::scalarmultKey(rct::H, t2), rct::scalarmultBase(tau2)); + + // PAPER LINES 49-51 + rct::key x = hash_cache_mash(hash_cache, z, T1, T2); + + // PAPER LINES 52-53 + rct::key taux; + sc_mul(taux.bytes, tau1.bytes, x.bytes); + rct::key xsq; + sc_mul(xsq.bytes, x.bytes, x.bytes); + sc_muladd(taux.bytes, tau2.bytes, xsq.bytes, taux.bytes); + for (size_t j = 1; j <= sv.size(); ++j) + { + CHECK_AND_ASSERT_THROW_MES(j+1 < zpow.size(), "invalid zpow index"); + sc_muladd(taux.bytes, zpow[j+1].bytes, gamma[j-1].bytes, taux.bytes); + } + rct::key mu; + sc_muladd(mu.bytes, x.bytes, rho.bytes, alpha.bytes); + + // PAPER LINES 54-57 + rct::keyV l = l0; + l = vector_add(l, vector_scalar(l1, x)); + rct::keyV r = r0; + r = vector_add(r, vector_scalar(r1, x)); + PERF_TIMER_STOP(PROVE_step2); + + PERF_TIMER_START_BP(PROVE_step3); + rct::key t = inner_product(l, r); + + // DEBUG: Test if the l and r vectors match the polynomial forms +#ifdef DEBUG_BP + rct::key test_t; + const rct::key t0 = inner_product(l0, r0); + sc_muladd(test_t.bytes, t1.bytes, x.bytes, t0.bytes); + sc_muladd(test_t.bytes, t2.bytes, xsq.bytes, test_t.bytes); + CHECK_AND_ASSERT_THROW_MES(test_t == t, "test_t check failed"); +#endif + + // PAPER LINES 32-33 + rct::key x_ip = hash_cache_mash(hash_cache, x, taux, mu, t); + + // These are used in the inner product rounds + size_t nprime = MN; + rct::keyV Gprime(MN); + rct::keyV Hprime(MN); + rct::keyV aprime(MN); + rct::keyV bprime(MN); + const rct::key yinv = invert(y); + rct::key yinvpow = rct::identity(); + for (size_t i = 0; i < MN; ++i) + { + Gprime[i] = Gi[i]; + Hprime[i] = scalarmultKey(Hi[i], yinvpow); + sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes); + aprime[i] = l[i]; + bprime[i] = r[i]; + } + rct::keyV L(logMN); + rct::keyV R(logMN); + int round = 0; + rct::keyV w(logMN); // this is the challenge x in the inner product protocol + PERF_TIMER_STOP(PROVE_step3); + + PERF_TIMER_START_BP(PROVE_step4); + // PAPER LINE 13 + while (nprime > 1) + { + // PAPER LINE 15 + nprime /= 2; + + // PAPER LINES 16-17 + rct::key cL = inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); + rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + + // PAPER LINES 18-19 + L[round] = vector_exponent_custom(slice(Gprime, nprime, Gprime.size()), slice(Hprime, 0, nprime), slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); + sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); + rct::addKeys(L[round], L[round], rct::scalarmultKey(rct::H, tmp)); + R[round] = vector_exponent_custom(slice(Gprime, 0, nprime), slice(Hprime, nprime, Hprime.size()), slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); + rct::addKeys(R[round], R[round], rct::scalarmultKey(rct::H, tmp)); + + // PAPER LINES 21-22 + w[round] = hash_cache_mash(hash_cache, L[round], R[round]); + + // PAPER LINES 24-25 + const rct::key winv = invert(w[round]); + Gprime = hadamard2(vector_scalar2(slice(Gprime, 0, nprime), winv), vector_scalar2(slice(Gprime, nprime, Gprime.size()), w[round])); + Hprime = hadamard2(vector_scalar2(slice(Hprime, 0, nprime), w[round]), vector_scalar2(slice(Hprime, nprime, Hprime.size()), winv)); + + // PAPER LINES 28-29 + aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv)); + bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round])); + + ++round; + } + PERF_TIMER_STOP(PROVE_step4); + + // PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20) + return Bulletproof(V, A, S, T1, T2, taux, mu, L, R, aprime[0], bprime[0], t); +} + +Bulletproof bulletproof_PROVE_old(const std::vector &v, const rct::keyV &gamma) +{ + CHECK_AND_ASSERT_THROW_MES(v.size() == gamma.size(), "Incompatible sizes of v and gamma"); + + // vG + gammaH + PERF_TIMER_START_BP(PROVE_v); + rct::keyV sv(v.size()); + for (size_t i = 0; i < v.size(); ++i) + { + sv[i] = rct::zero(); + sv[i].bytes[0] = v[i] & 255; + sv[i].bytes[1] = (v[i] >> 8) & 255; + sv[i].bytes[2] = (v[i] >> 16) & 255; + sv[i].bytes[3] = (v[i] >> 24) & 255; + sv[i].bytes[4] = (v[i] >> 32) & 255; + sv[i].bytes[5] = (v[i] >> 40) & 255; + sv[i].bytes[6] = (v[i] >> 48) & 255; + sv[i].bytes[7] = (v[i] >> 56) & 255; + } + PERF_TIMER_STOP(PROVE_v); + return bulletproof_PROVE_old(sv, gamma); +} + +/* Given a range proof, determine if it is valid */ +bool bulletproof_VERIFY_old(const std::vector &proofs) +{ + init_exponents(); + + PERF_TIMER_START_BP(VERIFY); + + // sanity and figure out which proof is longest + size_t max_length = 0; + for (const Bulletproof *p: proofs) + { + const Bulletproof &proof = *p; + CHECK_AND_ASSERT_MES(proof.V.size() >= 1, false, "V does not have at least one element"); + CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), false, "Mismatched L and R sizes"); + CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof"); + + max_length = std::max(max_length, proof.L.size()); + } + CHECK_AND_ASSERT_MES(max_length < 32, false, "At least one proof is too large"); + size_t maxMN = 1u << max_length; + + const size_t logN = 6; + const size_t N = 1 << logN; + rct::key tmp; + + // setup weighted aggregates + rct::key Z0 = rct::identity(); + rct::key z1 = rct::zero(); + rct::key Z2 = rct::identity(); + rct::key z3 = rct::zero(); + rct::keyV z4(maxMN, rct::zero()), z5(maxMN, rct::zero()); + for (const Bulletproof *p: proofs) + { + const Bulletproof &proof = *p; + + size_t M, logM; + for (logM = 0; (M = 1< multiexp_data; + multiexp_data.reserve(3+proof.V.size()); + multiexp_data.emplace_back(tmp, rct::H); + for (size_t j = 0; j < proof.V.size(); j++) + { + multiexp_data.emplace_back(zpow[j+2], proof.V[j]); + } + multiexp_data.emplace_back(x, proof.T1); + rct::key xsq; + sc_mul(xsq.bytes, x.bytes, x.bytes); + multiexp_data.emplace_back(xsq, proof.T2); + L61Right = multiexp(multiexp_data, false); + PERF_TIMER_STOP(VERIFY_line_61rl_new); + } + else + { + PERF_TIMER_START_BP(VERIFY_line_61rl_old); + sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); + L61Right = rct::scalarmultKey(rct::H, tmp); + ge_p3 L61Right_p3; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&L61Right_p3, L61Right.bytes) == 0, "ge_frombytes_vartime failed"); + for (size_t j = 0; j+1 < proof.V.size(); j += 2) + { + CHECK_AND_ASSERT_MES(j+2+1 < zpow.size(), false, "invalid zpow index"); + ge_dsmp precomp0, precomp1; + rct::precomp(precomp0, j < proof.V.size() ? proof.V[j] : rct::identity()); + rct::precomp(precomp1, j+1 < proof.V.size() ? proof.V[j+1] : rct::identity()); + rct::addKeys3acc_p3(&L61Right_p3, zpow[j+2], precomp0, zpow[j+2+1], precomp1); + } + for (size_t j = proof.V.size() & 0xfffffffe; j < M; j++) + { + CHECK_AND_ASSERT_MES(j+2 < zpow.size(), false, "invalid zpow index"); + // faster equivalent to: + // tmp = rct::scalarmultKey(j < proof.V.size() ? proof.V[j] : rct::identity(), zpow[j+2]); + // rct::addKeys(L61Right, L61Right, tmp); + if (j < proof.V.size()) + addKeys_acc_p3(&L61Right_p3, zpow[j+2], proof.V[j]); + } + + addKeys_acc_p3(&L61Right_p3, x, proof.T1); + + rct::key xsq; + sc_mul(xsq.bytes, x.bytes, x.bytes); + addKeys_acc_p3(&L61Right_p3, xsq, proof.T2); + ge_p3_tobytes(L61Right.bytes, &L61Right_p3); + PERF_TIMER_STOP(VERIFY_line_61rl_old); + } + + if (!(L61Right == L61Left)) + { + MERROR("Verification failure at step 1"); + return false; + } + + PERF_TIMER_START_BP(VERIFY_line_62); + // PAPER LINE 62 + rct::addKeys(Z0, Z0, rct::scalarmultKey(rct::addKeys(proof.A, rct::scalarmultKey(proof.S, x)), weight)); + PERF_TIMER_STOP(VERIFY_line_62); + + // Compute the number of rounds for the inner product + const size_t rounds = logM+logN; + CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds"); + + PERF_TIMER_START_BP(VERIFY_line_21_22); + // PAPER LINES 21-22 + // The inner product challenges are computed per round + rct::keyV w(rounds); + for (size_t i = 0; i < rounds; ++i) + { + w[i] = hash_cache_mash(hash_cache, proof.L[i], proof.R[i]); + } + PERF_TIMER_STOP(VERIFY_line_21_22); + + PERF_TIMER_START_BP(VERIFY_line_24_25); + // Basically PAPER LINES 24-25 + // Compute the curvepoints from G[i] and H[i] + rct::key yinvpow = rct::identity(); + rct::key ypow = rct::identity(); + + PERF_TIMER_START_BP(VERIFY_line_24_25_invert); + const rct::key yinv = invert(y); + rct::keyV winv(rounds); + for (size_t i = 0; i < rounds; ++i) + winv[i] = invert(w[i]); + PERF_TIMER_STOP(VERIFY_line_24_25_invert); + + for (size_t i = 0; i < MN; ++i) + { + // Convert the index to binary IN REVERSE and construct the scalar exponent + rct::key g_scalar = proof.a; + rct::key h_scalar; + sc_mul(h_scalar.bytes, proof.b.bytes, yinvpow.bytes); + + for (size_t j = rounds; j-- > 0; ) + { + size_t J = w.size() - j - 1; + + if ((i & (((size_t)1)< multiexp_data; + multiexp_data.reserve(2*rounds); + + sc_muladd(z1.bytes, proof.mu.bytes, weight.bytes, z1.bytes); + for (size_t i = 0; i < rounds; ++i) + { + sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); + multiexp_data.emplace_back(tmp, proof.L[i]); + sc_mul(tmp.bytes, winv[i].bytes, winv[i].bytes); + multiexp_data.emplace_back(tmp, proof.R[i]); + } + rct::key acc = multiexp(multiexp_data, false); + rct::addKeys(Z2, Z2, rct::scalarmultKey(acc, weight)); + sc_mulsub(tmp.bytes, proof.a.bytes, proof.b.bytes, proof.t.bytes); + sc_mul(tmp.bytes, tmp.bytes, x_ip.bytes); + sc_muladd(z3.bytes, tmp.bytes, weight.bytes, z3.bytes); + PERF_TIMER_STOP(VERIFY_line_26_new); + } + + // now check all proofs at once + PERF_TIMER_START_BP(VERIFY_step2_check); + rct::key Y = Z0; + sc_sub(tmp.bytes, rct::zero().bytes, z1.bytes); + rct::addKeys(Y, Y, rct::scalarmultBase(tmp)); + rct::addKeys(Y, Y, Z2); + rct::addKeys(Y, Y, rct::scalarmultKey(rct::H, z3)); + + std::vector multiexp_data; + multiexp_data.reserve(2 * maxMN); + for (size_t i = 0; i < maxMN; ++i) + { + sc_sub(tmp.bytes, rct::zero().bytes, z4[i].bytes); + multiexp_data.emplace_back(tmp, Gi_p3[i]); + sc_sub(tmp.bytes, rct::zero().bytes, z5[i].bytes); + multiexp_data.emplace_back(tmp, Hi_p3[i]); + } + rct::addKeys(Y, Y, multiexp(multiexp_data, true)); + PERF_TIMER_STOP(VERIFY_step2_check); + + if (!(Y == rct::identity())) + { + MERROR("Verification failure at step 2"); + return false; + } + + PERF_TIMER_STOP(VERIFY); + return true; +} + +bool bulletproof_VERIFY_old(const std::vector &proofs) +{ + std::vector proof_pointers; + for (const Bulletproof &proof: proofs) + proof_pointers.push_back(&proof); + return bulletproof_VERIFY_old(proof_pointers); +} + +bool bulletproof_VERIFY_old(const Bulletproof &proof) +{ + std::vector proofs; + proofs.push_back(&proof); + return bulletproof_VERIFY_old(proofs); +} + +} diff --git a/src/ringct/bulletproofs_plus.cc b/src/ringct/bulletproofs_plus.cc index 77b800064..d123beb2b 100644 --- a/src/ringct/bulletproofs_plus.cc +++ b/src/ringct/bulletproofs_plus.cc @@ -265,7 +265,7 @@ namespace rct rct::key res = ONE; if (n == 1) - return x; + return res; n += 1; rct::key x1 = copy(x); @@ -317,7 +317,16 @@ namespace rct static rct::key weighted_inner_product(const rct::keyV &a, const epee::span &b, const rct::key &y) { 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 @@ -644,7 +653,8 @@ try_again: { sc_mul(temp.bytes, temp.bytes, z_squared.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 @@ -705,8 +715,7 @@ try_again: rct::key challenge_squared; sc_mul(challenge_squared.bytes, challenge.bytes, challenge.bytes); - rct::key challenge_squared_inv; - sc_mul(challenge_squared_inv.bytes, challenge_inv.bytes, challenge_inv.bytes); + rct::key challenge_squared_inv = invert(challenge_squared); sc_muladd(alpha1.bytes, dL.bytes, challenge_squared.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()); 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); } diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 477a7907d..bc5527d49 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -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)}; } - - 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 { @@ -132,6 +125,24 @@ namespace rct { 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 &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) { try { return bulletproof_VERIFY(proof); } @@ -146,6 +157,20 @@ namespace rct { 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 &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 &amounts, epee::span sk, hw::device &hwdev) { CHECK_AND_ASSERT_THROW_MES(amounts.size() == sk.size(), "Invalid amounts/sk sizes"); @@ -616,7 +641,7 @@ namespace rct { hashes.push_back(hash2rct(h)); 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()); 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 // Thus the amounts vector will be "one" longer than the destinations vectort rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk, 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(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations"); 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"); rctSig rv; - rv.type = RCTTypeFull; + rv.type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull; rv.message = message; rv.outPk.resize(destinations.size()); - rv.p.rangeSigs.resize(destinations.size()); + if (!bulletproof) + rv.p.rangeSigs.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size()); size_t i = 0; @@ -1066,10 +1093,45 @@ namespace rct { //add destination to sig rv.outPk[i].dest = copy(destinations[i]); //compute range proof - rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); + if (!bulletproof) + { + rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); + #ifdef DBG CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); #endif + } + } + rv.p.bulletproofs.clear(); + if (bulletproof) + { + std::vector 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 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 rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].amount = d2h(amounts[i]); @@ -1195,7 +1257,10 @@ namespace rct { } 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]; } } @@ -1233,7 +1298,10 @@ namespace rct { } 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]; } amounts_proved += batch_size; @@ -1279,10 +1347,7 @@ namespace rct { { if (is_rct_clsag(rv.type)) { - if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE) - 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); + rv.p.CLSAGs[i] = proveRctCLSAGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], index[i], hwdev); } else { @@ -1305,6 +1370,98 @@ namespace rct { 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 &inamounts, const vector &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector & 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 &inamounts, const vector &outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin, const RCTConfig &rct_config, hw::device &hwdev) { + std::vector 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 //genRct: // 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 bool verRct(const rctSig & rv, bool semantics) { 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 (rv.type == RCTTypeBulletproof) 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.p.MGs.size() == 1, false, "full rctSig has not one MG"); } @@ -1335,10 +1495,23 @@ namespace rct { if (semantics) { tools::threadpool& tpool = tools::threadpool::getInstanceForCompute(); tools::threadpool::waiter waiter(tpool); - std::deque results(rv.outPk.size(), false); + std::deque results(bulletproof ? rv.p.bulletproofs.size() : rv.outPk.size(), false); DP("range proofs verified?"); - for (size_t i = 0; i < rv.outPk.size(); i++) - tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); + if (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++) + tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); + } if (!waiter.wait()) return false; @@ -1439,7 +1612,10 @@ namespace rct { rct::keyV masks(rv.outPk.size()); 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); DP(sumOutpks); @@ -1589,7 +1765,7 @@ namespace rct { // 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 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(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); @@ -1599,6 +1775,8 @@ namespace rct { mask = ecdh_info.mask; key amount = ecdh_info.amount; key C = rv.outPk[i].mask; + if (is_rct_bulletproof_plus(rv.type)) + C = scalarmult8(C); DP("C"); DP(C); key Ctmp; @@ -1630,6 +1808,8 @@ namespace rct { mask = ecdh_info.mask; key amount = ecdh_info.amount; key C = rv.outPk[i].mask; + if (is_rct_bulletproof_plus(rv.type)) + C = scalarmult8(C); DP("C"); DP(C); key Ctmp; diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index 17cfd77b9..5846cb46e 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -127,6 +127,9 @@ namespace rct { rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector & amounts, const keyV &amount_keys, const int mixin, const RCTConfig &rct_config, hw::device &hwdev); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin, const RCTConfig &rct_config, hw::device &hwdev); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector & index, ctkeyV &outSk, 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 & inamounts, const std::vector & 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 & inamounts, const std::vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev); + bool verRct(const rctSig & rv, bool semantics); static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); } bool verRctSemanticsSimple(const rctSig & rv); diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp index c22b0524f..f92ea4a55 100644 --- a/src/ringct/rctTypes.cpp +++ b/src/ringct/rctTypes.cpp @@ -193,6 +193,7 @@ namespace rct { switch (type) { case RCTTypeSimple: + case RCTTypeSimpleBulletproof: case RCTTypeBulletproof: case RCTTypeBulletproof2: case RCTTypeCLSAG: @@ -207,6 +208,8 @@ namespace rct { { switch (type) { + case RCTTypeSimpleBulletproof: + case RCTTypeFullBulletproof: case RCTTypeBulletproof: case RCTTypeBulletproof2: 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) { switch (type) @@ -238,6 +258,26 @@ namespace rct { 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 &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::max() - n, 0, "Invalid number of bulletproofs"); + if (n2 == 0) + return 0; + n += n2; + } + return n; + } 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(L_size == R_size, 0, "Mismatched bulletproof L/R size"); + 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; - CHECK_AND_ASSERT_MES((1 << extra_bits) == max_outputs, 0, "log2(max_outputs) is out of date"); - CHECK_AND_ASSERT_MES(L_size <= 6 + extra_bits, 0, "Invalid bulletproof L size"); - CHECK_AND_ASSERT_MES(V_size <= (1u<<(L_size-6)), 0, "Invalid bulletproof V/L"); - CHECK_AND_ASSERT_MES(V_size * 2 > (1u<<(L_size-6)), 0, "Invalid bulletproof V/L"); - CHECK_AND_ASSERT_MES(V_size > 0, 0, "Empty bulletproof"); - return V_size; + static_assert((1 << extra_bits) == BULLETPROOF_MAX_OUTPUTS, "log2(BULLETPROOF_MAX_OUTPUTS) is out of date"); + CHECK_AND_ASSERT_MES(proof.L.size() <= 6 + extra_bits, 0, "Invalid bulletproof L size"); + CHECK_AND_ASSERT_MES(proof.V.size() <= (1u<<(proof.L.size()-6)), 0, "Invalid bulletproof V/L"); + CHECK_AND_ASSERT_MES(proof.V.size() * 2 > (1u<<(proof.L.size()-6)), 0, "Invalid bulletproof V/L"); + CHECK_AND_ASSERT_MES(proof.V.size() > 0, 0, "Empty bulletproof"); + return proof.V.size(); } - 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 &proofs) { size_t n = 0; @@ -281,6 +318,43 @@ namespace rct { 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 &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::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 &proofs) { size_t n = 0; @@ -295,30 +369,14 @@ namespace rct { 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(L_size == R_size, 0, "Mismatched bulletproof L/R size"); + 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; - CHECK_AND_ASSERT_MES((1 << extra_bits) == max_outputs, 0, "log2(max_outputs) is out of date"); - CHECK_AND_ASSERT_MES(L_size <= 6 + extra_bits, 0, "Invalid bulletproof L size"); - return 1 << (L_size - 6); - } - size_t n_bulletproof_max_amounts(const Bulletproof &proof) { return n_bulletproof_max_amounts_base(proof.L.size(), proof.R.size(), BULLETPROOF_MAX_OUTPUTS); } - size_t n_bulletproof_plus_max_amounts(const BulletproofPlus &proof) { return n_bulletproof_max_amounts_base(proof.L.size(), proof.R.size(), BULLETPROOF_PLUS_MAX_OUTPUTS); } - - size_t n_bulletproof_max_amounts(const std::vector &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::max() - n, 0, "Invalid number of bulletproofs"); - if (n2 == 0) - return 0; - n += n2; - } - return n; + 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 &proofs) diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 59ed4d6a6..edbae5e09 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -245,7 +245,8 @@ namespace rct { rct::key r1, s1, d1; 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): 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): @@ -271,8 +272,10 @@ namespace rct { }; 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_amounts(const std::vector &proofs); + size_t n_bulletproof_v1_amounts(const std::vector &proofs); size_t n_bulletproof_max_amounts(const std::vector &proofs); size_t n_bulletproof_plus_amounts(const BulletproofPlus &proof); @@ -291,10 +294,12 @@ namespace rct { RCTTypeNull = 0, RCTTypeFull = 1, RCTTypeSimple = 2, - RCTTypeBulletproof = 3, - RCTTypeBulletproof2 = 4, - RCTTypeCLSAG = 5, - RCTTypeBulletproofPlus = 6, + RCTTypeFullBulletproof = 3, + RCTTypeSimpleBulletproof = 4, + RCTTypeBulletproof = 5, + RCTTypeBulletproof2 = 6, + RCTTypeCLSAG = 7, + RCTTypeBulletproofPlus = 8, }; enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof }; struct RCTConfig { @@ -323,7 +328,7 @@ namespace rct { FIELD(type) if (type == RCTTypeNull) 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; VARINT_FIELD(txnFee) // inputs/outputs not saved, only here for serialization help @@ -415,13 +420,28 @@ namespace rct { return false; if (type == RCTTypeNull) 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; - 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(); VARINT_FIELD(nbp) - ar.tag("bpp"); + ar.tag(type >= RCTTypeBulletproofPlus ? "bpp" : "bp"); ar.begin_array(); if (nbp > outputs) return false; @@ -520,7 +540,7 @@ namespace rct { ar.begin_array(); // 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 - 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); if (MGs.size() != mg_elements) return false; @@ -538,7 +558,7 @@ namespace rct { for (size_t j = 0; j < mixin + 1; ++j) { 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]); if (MGs[i].ss[j].size() != mg_ss2_elements) return false; @@ -565,7 +585,7 @@ namespace rct { } 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.begin_array(); @@ -597,12 +617,12 @@ namespace rct { 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 { - 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() @@ -714,6 +734,8 @@ namespace rct { bool is_rct_simple(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_borromean(int type); bool is_rct_clsag(int type); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 14463dce0..7e1b000e2 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1839,12 +1839,14 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation & switch (rv.type) { case rct::RCTTypeSimple: + case rct::RCTTypeSimpleBulletproof: case rct::RCTTypeBulletproof: case rct::RCTTypeBulletproof2: case rct::RCTTypeCLSAG: case rct::RCTTypeBulletproofPlus: return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev); case rct::RCTTypeFull: + case rct::RCTTypeFullBulletproof: return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev); default: LOG_ERROR("Unsupported rct type: " << rv.type); @@ -10279,8 +10281,7 @@ std::vector wallet2::create_transactions_2(std::vector 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)) { + 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)) { // we can partially fill that destination LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) << " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount)); @@ -11628,7 +11629,9 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt crypto::derivation_to_scalar(found_derivation, n, scalar1); rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n]; rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || 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; 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"); diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt index d098a8e04..f77085257 100644 --- a/tests/core_tests/CMakeLists.txt +++ b/tests/core_tests/CMakeLists.txt @@ -45,7 +45,7 @@ set(core_tests_sources rct.cpp bulletproofs.cpp bulletproof_plus.cpp - rct2.cpp + rct2.cpp wallet_tools.cpp) set(core_tests_headers diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp index 2f0f33cd6..bc9ce5532 100644 --- a/tests/core_tests/bulletproofs.cpp +++ b/tests/core_tests/bulletproofs.cpp @@ -158,7 +158,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector& eve crypto::derivation_to_scalar(derivation, o, amount_key); rct::key rct_tx_mask; const uint8_t type = rct_txes.back().rct_signatures.type; - if (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")); else 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); CHECK_TEST_CONDITION(tx.version >= 2); +MGINFO("type: " << (unsigned)tx.rct_signatures.type); CHECK_TEST_CONDITION(rct::is_rct_bulletproof(tx.rct_signatures.type)); size_t n_sizes = 0, n_amounts = 0; for (size_t n = 0; n < tx_idx; ++n) @@ -232,7 +233,7 @@ bool gen_bp_tx_invalid_1_1::generate(std::vector& events) cons { const size_t mixin = 10; 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); } @@ -241,7 +242,7 @@ bool gen_bp_tx_valid_2::generate(std::vector& events) const const size_t mixin = 10; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; const size_t bp_sizes[] = {2, (size_t)-1}; - const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 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"); }); } @@ -250,7 +251,7 @@ bool gen_bp_tx_valid_3::generate(std::vector& events) const const size_t mixin = 10; const uint64_t amounts_paid[] = {5000, 5000, 5000, (uint64_t)-1}; const size_t bp_sizes[] = {4, (size_t)-1}; - const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 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"); }); } @@ -259,7 +260,7 @@ bool gen_bp_tx_valid_16::generate(std::vector& events) const const size_t mixin = 10; const uint64_t amounts_paid[] = {500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, (uint64_t)-1}; const size_t bp_sizes[] = {16, (size_t)-1}; - const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 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"); }); } @@ -383,13 +384,3 @@ bool gen_bp_tx_invalid_bulletproof2_type::generate(std::vector }); } -bool gen_bp_tx_invalid_clsag_type::generate(std::vector& events) const -{ - DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_clsag_type"); - const size_t mixin = 10; - const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; - const rct::RCTConfig rct_config[] = { { rct::RangeProofPaddedBulletproof, 3 } }; - return generate_with(events, mixin, 1, amounts_paid, false, rct_config, HF_VERSION_BULLETPROOF_PLUS + 1, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){ - return true; - }); -} diff --git a/tests/core_tests/bulletproofs.h b/tests/core_tests/bulletproofs.h index 56c83f522..f81aa9ee3 100644 --- a/tests/core_tests/bulletproofs.h +++ b/tests/core_tests/bulletproofs.h @@ -212,8 +212,3 @@ struct gen_bp_tx_invalid_bulletproof2_type : public gen_bp_tx_validation_base }; template<> struct get_test_options: public get_bp_versioned_test_options {}; -struct gen_bp_tx_invalid_clsag_type : public gen_bp_tx_validation_base -{ - bool generate(std::vector& events) const; -}; -template<> struct get_test_options: public get_bp_versioned_test_options {}; diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index b89dff445..04ecaa2c2 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -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_borromean_type); GENERATE_AND_PLAY(gen_bp_tx_invalid_bulletproof2_type); - GENERATE_AND_PLAY(gen_bp_tx_invalid_clsag_type); - - GENERATE_AND_PLAY(gen_bpp_tx_invalid_before_fork); - GENERATE_AND_PLAY(gen_bpp_tx_valid_at_fork); - GENERATE_AND_PLAY(gen_bpp_tx_invalid_1_1); - GENERATE_AND_PLAY(gen_bpp_tx_valid_2); - GENERATE_AND_PLAY(gen_bpp_tx_valid_3); - GENERATE_AND_PLAY(gen_bpp_tx_valid_16); - GENERATE_AND_PLAY(gen_bpp_tx_invalid_4_2_1); - GENERATE_AND_PLAY(gen_bpp_tx_invalid_16_16); - GENERATE_AND_PLAY(gen_bpp_txs_valid_2_and_2); - GENERATE_AND_PLAY(gen_bpp_txs_invalid_2_and_8_2_and_16_16_1); - GENERATE_AND_PLAY(gen_bpp_txs_valid_2_and_3_and_2_and_4); - GENERATE_AND_PLAY(gen_bpp_tx_invalid_not_enough_proofs); - GENERATE_AND_PLAY(gen_bpp_tx_invalid_empty_proofs); - GENERATE_AND_PLAY(gen_bpp_tx_invalid_too_many_proofs); - GENERATE_AND_PLAY(gen_bpp_tx_invalid_wrong_amount); - GENERATE_AND_PLAY(gen_bpp_tx_invalid_clsag_type); GENERATE_AND_PLAY(gen_rct2_tx_clsag_malleability); diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index 28b44d293..cb5c11a23 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -300,6 +300,11 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector additional_tx_secret_keys; crypto::secret_key multisig_tx_key_entropy; auto sources_copy = sources; - multisig::signing::tx_builder_ringct_t tx_builder; - 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"); + r = construct_tx_and_get_tx_key(miner_account[creator].get_keys(), subaddresses, sources, destinations, boost::none, std::vector(), tx, 0, tx_key, additional_tx_secret_keys, true, { rct::RangeProofPaddedBulletproof, 0 }, msoutp); + CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); // work out the permutation done on sources std::vector ins_order; @@ -434,8 +439,10 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector& eve DEFINE_TESTS_ERROR_CONTEXT("gen_rct_tx_clsag_malleability"); const int mixin = 10; 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) { CHECK_TEST_CONDITION(tx.version == 2); CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeCLSAG); diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index c079afe3a..4a5fc76ed 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -47,8 +47,7 @@ set(performance_tests_headers subaddress_expand.h range_proof.h bulletproof.h - bulletproof_plus.h - crypto_ops.h + crypto_ops.h sc_reduce32.h sc_check.h multiexp.h diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 71c736977..3a89b7753 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -60,7 +60,6 @@ #include "equality.h" #include "range_proof.h" #include "bulletproof.h" -#include "bulletproof_plus.h" #include "crypto_ops.h" #include "multiexp.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, 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, false, 1); diff --git a/tests/trezor/trezor_tests.cpp b/tests/trezor/trezor_tests.cpp index cf85ffb30..5486e861f 100644 --- a/tests/trezor/trezor_tests.cpp +++ b/tests/trezor/trezor_tests.cpp @@ -560,7 +560,7 @@ static void expand_tsx(cryptonote::transaction &tx) rv.p.MGs[n].II[0] = rct::ki2rct(boost::get(tx.vin[n]).k_image); } } - else if (rv.type == rct::RCTTypeCLSAG || rv.type == rct::RCTTypeBulletproofPlus) + else if (rv.type == rct::RCTTypeCLSAG) { if (!tx.pruned) { diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 55818dc93..0edc3d660 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -36,7 +36,6 @@ set(unit_tests_sources block_reward.cpp bootstrap_node_selector.cpp bulletproofs.cpp - bulletproofs_plus.cpp canonical_amounts.cpp chacha.cpp checkpoints.cpp diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index 65f0d85f8..25cf8772a 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -131,7 +131,7 @@ TEST(bulletproofs, multi_splitting) } rct::ctkeyV outSk; - rct::RCTConfig rct_config { rct::RangeProofPaddedBulletproof, 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")); ASSERT_TRUE(rct::verRctSimple(s));