From ba3968f6ce0459f0c0a767e4f54e7cb2c48955d9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 14 Jan 2017 13:29:08 +0000 Subject: [PATCH] rct: split rct checks between semantics and other Semantics can be checked early --- src/cryptonote_core/blockchain.cpp | 9 +- src/cryptonote_core/cryptonote_core.cpp | 44 ++++++ src/ringct/rctSigs.cpp | 175 +++++++++++++----------- src/ringct/rctSigs.h | 6 +- 4 files changed, 149 insertions(+), 85 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index de3af8e0..00068467 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2333,10 +2333,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast(rv.type)); } - // outPk - CHECK_AND_ASSERT_MES(rv.outPk.size() == tx.vout.size(), false, "Bad outPk size"); - 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); + // outPk was already done by handle_incoming_tx return true; } @@ -2641,7 +2638,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (!rct::verRctSimple(rv)) + if (!rct::verRctSimple(rv, false)) { LOG_PRINT_L1("Failed to check ringct signatures!"); return false; @@ -2699,7 +2696,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (!rct::verRct(rv)) + if (!rct::verRct(rv, false)) { LOG_PRINT_L1("Failed to check ringct signatures!"); return false; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index c63a08ee..ea27df50 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -49,6 +49,7 @@ using namespace epee; #if defined(BERKELEY_DB) #include "blockchain_db/berkeleydb/db_bdb.h" #endif +#include "ringct/rctSigs.h" DISABLE_VS_WARNINGS(4355) @@ -494,6 +495,22 @@ 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 (rv.outPk.size() != tx.vout.size()) + { + LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad outPk size in tx " << tx_hash << ", rejected"); + 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); + } + if(!check_tx_semantic(tx, keeped_by_block)) { LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"); @@ -584,6 +601,33 @@ namespace cryptonote return false; } + if (tx.version >= 2) + { + const rct::rctSig &rv = tx.rct_signatures; + switch (rv.type) { + case rct::RCTTypeNull: + // coinbase should not come here, so we reject for all other types + LOG_PRINT_RED_L1("Unexpected Null rctSig type"); + return false; + case rct::RCTTypeSimple: + if (!rct::verRctSimple(rv, true)) + { + LOG_PRINT_RED_L1("rct signature semantics check failed"); + return false; + } + break; + case rct::RCTTypeFull: + if (!rct::verRct(rv, true)) + { + LOG_PRINT_RED_L1("rct signature semantics check failed"); + return false; + } + break; + default: + LOG_PRINT_RED_L1("Unknown rct type: " << rv.type); + return false; + } + } return true; } diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 4f8782cd..1e4f1e85 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -710,43 +710,54 @@ namespace rct { //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) // 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 - bool verRct(const rctSig & rv) { + bool verRct(const rctSig & rv, bool semantics) { PERF_TIMER(verRct); CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig"); - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.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"); + if (semantics) + { + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.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"); + } + else + { + // semantics check is early, we don't have the MGs resolved yet + } // some rct ops can throw try { - std::deque results(rv.outPk.size(), false); - tools::thread_group threadpool(tools::thread_group::optimal_with_max(rv.outPk.size())); + if (semantics) { + std::deque results(rv.outPk.size(), false); + tools::thread_group threadpool(tools::thread_group::optimal_with_max(rv.outPk.size())); - tools::task_region(threadpool, [&] (tools::task_region_handle& region) { - DP("range proofs verified?"); - for (size_t i = 0; i < rv.outPk.size(); i++) { - region.run([&, i] { - results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); - }); - } - }); + tools::task_region(threadpool, [&] (tools::task_region_handle& region) { + DP("range proofs verified?"); + for (size_t i = 0; i < rv.outPk.size(); i++) { + region.run([&, i] { + results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); + }); + } + }); - for (size_t i = 0; i < rv.outPk.size(); ++i) { - if (!results[i]) { - LOG_PRINT_L1("Range proof verified failed for output " << i); - return false; + for (size_t i = 0; i < rv.outPk.size(); ++i) { + if (!results[i]) { + LOG_PRINT_L1("Range proof verified failed for output " << i); + return false; + } } } - //compute txn fee - key txnFeeKey = scalarmultH(d2h(rv.txnFee)); - bool mgVerd = verRctMG(rv.p.MGs[0], rv.mixRing, rv.outPk, txnFeeKey, get_pre_mlsag_hash(rv)); - DP("mg sig verified?"); - DP(mgVerd); - if (!mgVerd) { - LOG_PRINT_L1("MG signature verification failed"); - return false; + if (!semantics) { + //compute txn fee + key txnFeeKey = scalarmultH(d2h(rv.txnFee)); + bool mgVerd = verRctMG(rv.p.MGs[0], rv.mixRing, rv.outPk, txnFeeKey, get_pre_mlsag_hash(rv)); + DP("mg sig verified?"); + DP(mgVerd); + if (!mgVerd) { + LOG_PRINT_L1("MG signature verification failed"); + return false; + } } return true; @@ -759,76 +770,86 @@ namespace rct { //ver RingCT simple //assumes only post-rct style inputs (at least for max anonymity) - bool verRctSimple(const rctSig & rv) { + bool verRctSimple(const rctSig & rv, bool semantics) { try { PERF_TIMER(verRctSimple); CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "verRctSimple called on non simple rctSig"); - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); - CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs"); - CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); + if (semantics) + { + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); + CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs"); + } + else + { + // semantics check is early, and mixRing/MGs aren't resolved yet + CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); + } const size_t threads = std::max(rv.outPk.size(), rv.mixRing.size()); std::deque results(threads); tools::thread_group threadpool(tools::thread_group::optimal_with_max(threads)); - results.clear(); - results.resize(rv.outPk.size()); - tools::task_region(threadpool, [&] (tools::task_region_handle& region) { + if (semantics) { + results.clear(); + results.resize(rv.outPk.size()); + tools::task_region(threadpool, [&] (tools::task_region_handle& region) { + for (size_t i = 0; i < rv.outPk.size(); i++) { + region.run([&, i] { + results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); + }); + } + }); + + for (size_t i = 0; i < results.size(); ++i) { + if (!results[i]) { + LOG_PRINT_L1("Range proof verified failed for output " << i); + return false; + } + } + + key sumOutpks = identity(); for (size_t i = 0; i < rv.outPk.size(); i++) { - region.run([&, i] { - results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); - }); + addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); } - }); + DP(sumOutpks); + key txnFeeKey = scalarmultH(d2h(rv.txnFee)); + addKeys(sumOutpks, txnFeeKey, sumOutpks); - for (size_t i = 0; i < results.size(); ++i) { - if (!results[i]) { - LOG_PRINT_L1("Range proof verified failed for output " << i); - return false; + key sumPseudoOuts = identity(); + for (size_t i = 0 ; i < rv.pseudoOuts.size() ; i++) { + addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]); + } + DP(sumPseudoOuts); + + //check pseudoOuts vs Outs.. + if (!equalKeys(sumPseudoOuts, sumOutpks)) { + LOG_PRINT_L1("Sum check failed"); + return false; } } + else { + const key message = get_pre_mlsag_hash(rv); - key sumOutpks = identity(); - for (size_t i = 0; i < rv.outPk.size(); i++) { - addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); - } - DP(sumOutpks); - key txnFeeKey = scalarmultH(d2h(rv.txnFee)); - addKeys(sumOutpks, txnFeeKey, sumOutpks); + results.clear(); + results.resize(rv.mixRing.size()); + tools::task_region(threadpool, [&] (tools::task_region_handle& region) { + for (size_t i = 0 ; i < rv.mixRing.size() ; i++) { + region.run([&, i] { + results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]); + }); + } + }); - key message = get_pre_mlsag_hash(rv); - - results.clear(); - results.resize(rv.mixRing.size()); - tools::task_region(threadpool, [&] (tools::task_region_handle& region) { - for (size_t i = 0 ; i < rv.mixRing.size() ; i++) { - region.run([&, i] { - results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]); - }); + for (size_t i = 0; i < results.size(); ++i) { + if (!results[i]) { + LOG_PRINT_L1("verRctMGSimple failed for input " << i); + return false; + } } - }); - - for (size_t i = 0; i < results.size(); ++i) { - if (!results[i]) { - LOG_PRINT_L1("verRctMGSimple failed for input " << i); - return false; - } - } - - key sumPseudoOuts = identity(); - for (size_t i = 0 ; i < rv.mixRing.size() ; i++) { - addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]); - } - DP(sumPseudoOuts); - - //check pseudoOuts vs Outs.. - if (!equalKeys(sumPseudoOuts, sumOutpks)) { - LOG_PRINT_L1("Sum check failed"); - return false; } return true; diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index 1fe4aa07..ca40ddd8 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -126,8 +126,10 @@ namespace rct { rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & amounts, const keyV &amount_keys, const int mixin); rctSig genRctSimple(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); rctSig genRctSimple(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); - bool verRct(const rctSig & rv); - bool verRctSimple(const rctSig & rv); + bool verRct(const rctSig & rv, bool semantics); + static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); } + bool verRctSimple(const rctSig & rv, bool semantics); + static inline bool verRctSimple(const rctSig & rv) { return verRctSimple(rv, true) && verRctSimple(rv, false); } xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask); xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i); xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask);