rct: split rct checks between semantics and other

Semantics can be checked early
This commit is contained in:
moneromooo-monero 2017-01-14 13:29:08 +00:00
parent 2806842200
commit ba3968f6ce
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
4 changed files with 149 additions and 85 deletions

View file

@ -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<std::string>(rv.type)); CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast<std::string>(rv.type));
} }
// outPk // outPk was already done by handle_incoming_tx
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<txout_to_key>(tx.vout[n].target).key);
return true; 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!"); LOG_PRINT_L1("Failed to check ringct signatures!");
return false; 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!"); LOG_PRINT_L1("Failed to check ringct signatures!");
return false; return false;

View file

@ -49,6 +49,7 @@ using namespace epee;
#if defined(BERKELEY_DB) #if defined(BERKELEY_DB)
#include "blockchain_db/berkeleydb/db_bdb.h" #include "blockchain_db/berkeleydb/db_bdb.h"
#endif #endif
#include "ringct/rctSigs.h"
DISABLE_VS_WARNINGS(4355) DISABLE_VS_WARNINGS(4355)
@ -494,6 +495,22 @@ namespace cryptonote
return false; return false;
} }
// resolve outPk references in rct txes
// outPk aren't the only thing that need resolving for a fully resolved tx,
// but outPk (1) are needed now to check range proof semantics, and
// (2) do not need access to the blockchain to find data
if (tx.version >= 2)
{
rct::rctSig &rv = tx.rct_signatures;
if (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<txout_to_key>(tx.vout[n].target).key);
}
if(!check_tx_semantic(tx, keeped_by_block)) if(!check_tx_semantic(tx, keeped_by_block))
{ {
LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"); LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected");
@ -584,6 +601,33 @@ namespace cryptonote
return false; 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; return true;
} }

View file

@ -710,43 +710,54 @@ namespace rct {
//decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) //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 // uses the attached ecdh info to find the amounts represented by each output commitment
// must know the destination private key to find the correct amount, else will return a random number // must know the destination private key to find the correct amount, else will return a random number
bool verRct(const rctSig & rv) { bool verRct(const rctSig & rv, bool semantics) {
PERF_TIMER(verRct); PERF_TIMER(verRct);
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig"); 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"); if (semantics)
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); {
CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG"); CHECK_AND_ASSERT_MES(rv.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 // some rct ops can throw
try try
{ {
std::deque<bool> results(rv.outPk.size(), false); if (semantics) {
tools::thread_group threadpool(tools::thread_group::optimal_with_max(rv.outPk.size())); std::deque<bool> 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) { tools::task_region(threadpool, [&] (tools::task_region_handle& region) {
DP("range proofs verified?"); DP("range proofs verified?");
for (size_t i = 0; i < rv.outPk.size(); i++) { for (size_t i = 0; i < rv.outPk.size(); i++) {
region.run([&, i] { region.run([&, i] {
results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
}); });
} }
}); });
for (size_t i = 0; i < rv.outPk.size(); ++i) { for (size_t i = 0; i < rv.outPk.size(); ++i) {
if (!results[i]) { if (!results[i]) {
LOG_PRINT_L1("Range proof verified failed for output " << i); LOG_PRINT_L1("Range proof verified failed for output " << i);
return false; return false;
}
} }
} }
//compute txn fee if (!semantics) {
key txnFeeKey = scalarmultH(d2h(rv.txnFee)); //compute txn fee
bool mgVerd = verRctMG(rv.p.MGs[0], rv.mixRing, rv.outPk, txnFeeKey, get_pre_mlsag_hash(rv)); key txnFeeKey = scalarmultH(d2h(rv.txnFee));
DP("mg sig verified?"); bool mgVerd = verRctMG(rv.p.MGs[0], rv.mixRing, rv.outPk, txnFeeKey, get_pre_mlsag_hash(rv));
DP(mgVerd); DP("mg sig verified?");
if (!mgVerd) { DP(mgVerd);
LOG_PRINT_L1("MG signature verification failed"); if (!mgVerd) {
return false; LOG_PRINT_L1("MG signature verification failed");
return false;
}
} }
return true; return true;
@ -759,76 +770,86 @@ namespace rct {
//ver RingCT simple //ver RingCT simple
//assumes only post-rct style inputs (at least for max anonymity) //assumes only post-rct style inputs (at least for max anonymity)
bool verRctSimple(const rctSig & rv) { bool verRctSimple(const rctSig & rv, bool semantics) {
try try
{ {
PERF_TIMER(verRctSimple); PERF_TIMER(verRctSimple);
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "verRctSimple called on non simple rctSig"); 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"); if (semantics)
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.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); 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()); const size_t threads = std::max(rv.outPk.size(), rv.mixRing.size());
std::deque<bool> results(threads); std::deque<bool> results(threads);
tools::thread_group threadpool(tools::thread_group::optimal_with_max(threads)); tools::thread_group threadpool(tools::thread_group::optimal_with_max(threads));
results.clear(); if (semantics) {
results.resize(rv.outPk.size()); results.clear();
tools::task_region(threadpool, [&] (tools::task_region_handle& region) { 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++) { for (size_t i = 0; i < rv.outPk.size(); i++) {
region.run([&, i] { addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask);
results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
});
} }
}); DP(sumOutpks);
key txnFeeKey = scalarmultH(d2h(rv.txnFee));
addKeys(sumOutpks, txnFeeKey, sumOutpks);
for (size_t i = 0; i < results.size(); ++i) { key sumPseudoOuts = identity();
if (!results[i]) { for (size_t i = 0 ; i < rv.pseudoOuts.size() ; i++) {
LOG_PRINT_L1("Range proof verified failed for output " << i); addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]);
return false; }
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(); results.clear();
for (size_t i = 0; i < rv.outPk.size(); i++) { results.resize(rv.mixRing.size());
addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); tools::task_region(threadpool, [&] (tools::task_region_handle& region) {
} for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
DP(sumOutpks); region.run([&, i] {
key txnFeeKey = scalarmultH(d2h(rv.txnFee)); results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]);
addKeys(sumOutpks, txnFeeKey, sumOutpks); });
}
});
key message = get_pre_mlsag_hash(rv); for (size_t i = 0; i < results.size(); ++i) {
if (!results[i]) {
results.clear(); LOG_PRINT_L1("verRctMGSimple failed for input " << i);
results.resize(rv.mixRing.size()); return false;
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;
}
}
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; return true;

View file

@ -126,8 +126,10 @@ namespace rct {
rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & amounts, const keyV &amount_keys, const int mixin); rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & amounts, const keyV &amount_keys, const int mixin);
rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & inamounts, const vector<xmr_amount> & outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & inamounts, const vector<xmr_amount> & outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin);
rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & inamounts, const vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & inamounts, const vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk);
bool verRct(const rctSig & rv); bool verRct(const rctSig & rv, bool semantics);
bool verRctSimple(const rctSig & rv); 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, key & mask);
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i); 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); xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask);