rct: rework the verification preparation process

The whole rct data apart from the MLSAGs is now included in
the signed message, to avoid malleability issues.

Instead of passing the data that's not serialized as extra
parameters to the verification API, the transaction is modified
to fill all that information. This means the transaction can
not be const anymore, but it cleaner in other ways.
This commit is contained in:
moneromooo-monero 2016-08-09 11:38:54 +01:00
parent 3ab2ab3e76
commit d93746b6d3
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
6 changed files with 195 additions and 200 deletions

View file

@ -2156,7 +2156,7 @@ bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<u
// This function overloads its sister function with // This function overloads its sister function with
// an extra value (hash of highest block that holds an output used as input) // an extra value (hash of highest block that holds an output used as input)
// as a return-by-reference. // as a return-by-reference.
bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block) bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block)
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock); CRITICAL_REGION_LOCAL(m_blockchain_lock);
@ -2235,13 +2235,82 @@ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const
} }
return false; return false;
} }
bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys)
{
CHECK_AND_ASSERT_MES(tx.version == 2, false, "Transaction version is not 2");
rct::rctSig &rv = tx.rct_signatures;
// message - hash of the transaction prefix
rv.message = rct::hash2rct(tx_prefix_hash);
// mixRing - full and simple store it in opposite ways
if (rv.type == rct::RCTTypeFull)
{
rv.mixRing.resize(pubkeys[0].size());
for (size_t m = 0; m < pubkeys[0].size(); ++m)
rv.mixRing[m].clear();
for (size_t n = 0; n < pubkeys.size(); ++n)
{
CHECK_AND_ASSERT_MES(pubkeys[n].size() <= pubkeys[0].size(), false, "More inputs that first ring");
for (size_t m = 0; m < pubkeys[n].size(); ++m)
{
rv.mixRing[m].push_back(pubkeys[n][m]);
}
}
}
else if (rv.type == rct::RCTTypeSimple)
{
rv.mixRing.resize(pubkeys.size());
for (size_t n = 0; n < pubkeys.size(); ++n)
{
rv.mixRing[n].clear();
for (size_t m = 0; m < pubkeys[n].size(); ++m)
{
rv.mixRing[n].push_back(pubkeys[n][m]);
}
}
}
else
{
CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast<std::string>(rv.type));
}
// II
if (rv.type == rct::RCTTypeFull)
{
rv.MG.II.resize(tx.vin.size());
for (size_t n = 0; n < tx.vin.size(); ++n)
rv.MG.II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
}
else if (rv.type == rct::RCTTypeSimple)
{
CHECK_AND_ASSERT_MES(rv.MGs.size() == tx.vin.size(), false, "Bad MGs size");
for (size_t n = 0; n < tx.vin.size(); ++n)
{
rv.MGs[n].II.resize(1);
rv.MGs[n].II[0] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
}
}
else
{
CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast<std::string>(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<txout_to_key>(tx.vout[n].target).key);
return true;
}
//------------------------------------------------------------------ //------------------------------------------------------------------
// This function validates transaction inputs and their keys. // This function validates transaction inputs and their keys.
// FIXME: consider moving functionality specific to one input into // FIXME: consider moving functionality specific to one input into
// check_tx_input() rather than here, and use this function simply // check_tx_input() rather than here, and use this function simply
// to iterate the inputs as necessary (splitting the task // to iterate the inputs as necessary (splitting the task
// using threads, etc.) // using threads, etc.)
bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height) bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height)
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
size_t sig_index = 0; size_t sig_index = 0;
@ -2461,71 +2530,29 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
} }
else else
{ {
if (!expand_transaction_2(tx, tx_prefix_hash, pubkeys))
{
LOG_PRINT_L1("Failed to expand rct signatures!");
return false;
}
// from version 2, check ringct signatures // from version 2, check ringct signatures
// obviously, the original and simple rct APIs use a mixRing that's indexes // obviously, the original and simple rct APIs use a mixRing that's indexes
// in opposite orders, because it'd be too simple otherwise... // in opposite orders, because it'd be too simple otherwise...
switch (tx.rct_signatures.type) const rct::rctSig &rv = tx.rct_signatures;
switch (rv.type)
{ {
case rct::RCTTypeSimple: { case rct::RCTTypeSimple: {
rct::ctkeyM reconstructed_mixRing;
std::vector<rct::keyV> reconstructed_II;
rct::ctkeyV reconstructed_outPk;
// if the tx already has a non empty mixRing, use them,
// else reconstruct them
const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing;
// always do II, because it's split in the simple version, and always do outPk
// all MGs should have empty II
for (size_t n = 0; n < tx.rct_signatures.MGs.size(); ++n)
{
if (tx.rct_signatures.MGs[n].II.size() != 0)
{
LOG_PRINT_L1("Failed to check ringct signatures: non empty MGs II");
return false;
}
}
reconstructed_II.resize(tx.vin.size());
for (size_t n = 0; n < tx.vin.size(); ++n)
{
reconstructed_II[n].push_back(rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image));
}
if (tx.rct_signatures.outPk.size() != tx.vout.size())
{
LOG_PRINT_L1("Failed to check ringct signatures: outPk and vout have different sizes");
return false;
}
reconstructed_outPk.resize(tx.vout.size());
for (size_t n = 0; n < tx.vout.size(); ++n)
{
reconstructed_outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key);
reconstructed_outPk[n].mask = tx.rct_signatures.outPk[n].mask;
}
if (tx.rct_signatures.mixRing.empty())
{
reconstructed_mixRing.resize(pubkeys.size());
for (size_t n = 0; n < pubkeys.size(); ++n)
{
for (size_t m = 0; m < pubkeys[n].size(); ++m)
{
reconstructed_mixRing[n].push_back(pubkeys[n][m]);
}
}
}
// check all this, either recontructed (so should really pass), or not // check all this, either recontructed (so should really pass), or not
{ {
if (pubkeys.size() != mixRing.size()) if (pubkeys.size() != rv.mixRing.size())
{ {
LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size");
return false; return false;
} }
for (size_t i = 0; i < pubkeys.size(); ++i) for (size_t i = 0; i < pubkeys.size(); ++i)
{ {
if (pubkeys[i].size() != mixRing[i].size()) if (pubkeys[i].size() != rv.mixRing[i].size())
{ {
LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size");
return false; return false;
@ -2536,12 +2563,12 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
{ {
for (size_t m = 0; m < pubkeys[n].size(); ++m) for (size_t m = 0; m < pubkeys[n].size(); ++m)
{ {
if (pubkeys[n][m].dest != rct::rct2pk(mixRing[n][m].dest)) if (pubkeys[n][m].dest != rct::rct2pk(rv.mixRing[n][m].dest))
{ {
LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m);
return false; return false;
} }
if (pubkeys[n][m].mask != rct::rct2pk(mixRing[n][m].mask)) if (pubkeys[n][m].mask != rct::rct2pk(rv.mixRing[n][m].mask))
{ {
LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m);
return false; return false;
@ -2550,21 +2577,21 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
} }
} }
if (tx.rct_signatures.MGs.size() != tx.vin.size()) if (rv.MGs.size() != tx.vin.size())
{ {
LOG_PRINT_L1("Failed to check ringct signatures: mismatched MGs/vin sizes"); LOG_PRINT_L1("Failed to check ringct signatures: mismatched MGs/vin sizes");
return false; return false;
} }
for (size_t n = 0; n < tx.vin.size(); ++n) for (size_t n = 0; n < tx.vin.size(); ++n)
{ {
if (memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &reconstructed_II[n][0], 32)) if (memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.MGs[n].II[0], 32))
{ {
LOG_PRINT_L1("Failed to check ringct signatures: mismatched key image"); LOG_PRINT_L1("Failed to check ringct signatures: mismatched key image");
return false; return false;
} }
} }
if (!rct::verRctSimple(tx.rct_signatures, mixRing, &reconstructed_II, reconstructed_outPk, rct::hash2rct(tx_prefix_hash))) if (!rct::verRctSimple(rv))
{ {
LOG_PRINT_L1("Failed to check ringct signatures!"); LOG_PRINT_L1("Failed to check ringct signatures!");
return false; return false;
@ -2572,66 +2599,13 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
break; break;
} }
case rct::RCTTypeFull: { case rct::RCTTypeFull: {
rct::ctkeyM reconstructed_mixRing;
rct::keyV reconstructed_II;
rct::ctkeyV reconstructed_outPk;
// if the tx already has a non empty mixRing and/or II, use them,
// else reconstruct them. Always do outPk.
const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing;
const rct::keyV &II = tx.rct_signatures.MG.II.empty() ? reconstructed_II : tx.rct_signatures.MG.II;
const rct::ctkeyV outPk = reconstructed_outPk;
// RCT needs the same mixin for all inputs
for (size_t n = 1; n < pubkeys.size(); ++n)
{
if (pubkeys[n].size() != pubkeys[0].size())
{
LOG_PRINT_L1("Failed to check ringct signatures: mismatched ring sizes");
return false;
}
}
if (tx.rct_signatures.mixRing.empty())
{
reconstructed_mixRing.resize(pubkeys[0].size());
for (size_t n = 0; n < pubkeys.size(); ++n)
{
for (size_t m = 0; m < pubkeys[n].size(); ++m)
{
reconstructed_mixRing[m].push_back(pubkeys[n][m]);
}
}
}
if (tx.rct_signatures.MG.II.empty())
{
reconstructed_II.resize(tx.vin.size());
for (size_t n = 0; n < tx.vin.size(); ++n)
{
reconstructed_II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
}
}
if (tx.rct_signatures.outPk.size() != tx.vout.size())
{
LOG_PRINT_L1("Failed to check ringct signatures: outPk and vout have different sizes");
return false;
}
reconstructed_outPk.resize(tx.vout.size());
for (size_t n = 0; n < tx.vout.size(); ++n)
{
reconstructed_outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key);
reconstructed_outPk[n].mask = tx.rct_signatures.outPk[n].mask;
}
// check all this, either recontructed (so should really pass), or not // check all this, either recontructed (so should really pass), or not
{ {
bool size_matches = true; bool size_matches = true;
for (size_t i = 0; i < pubkeys.size(); ++i) for (size_t i = 0; i < pubkeys.size(); ++i)
size_matches &= pubkeys[i].size() == mixRing.size(); size_matches &= pubkeys[i].size() == rv.mixRing.size();
for (size_t i = 0; i < tx.rct_signatures.mixRing.size(); ++i) for (size_t i = 0; i < rv.mixRing.size(); ++i)
size_matches &= pubkeys.size() == mixRing[i].size(); size_matches &= pubkeys.size() == rv.mixRing[i].size();
if (!size_matches) if (!size_matches)
{ {
LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size");
@ -2642,12 +2616,12 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
{ {
for (size_t m = 0; m < pubkeys[n].size(); ++m) for (size_t m = 0; m < pubkeys[n].size(); ++m)
{ {
if (pubkeys[n][m].dest != rct::rct2pk(mixRing[m][n].dest)) if (pubkeys[n][m].dest != rct::rct2pk(rv.mixRing[m][n].dest))
{ {
LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m);
return false; return false;
} }
if (pubkeys[n][m].mask != rct::rct2pk(mixRing[m][n].mask)) if (pubkeys[n][m].mask != rct::rct2pk(rv.mixRing[m][n].mask))
{ {
LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m);
return false; return false;
@ -2656,21 +2630,21 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
} }
} }
if (II.size() != tx.vin.size()) if (rv.MG.II.size() != tx.vin.size())
{ {
LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes");
return false; return false;
} }
for (size_t n = 0; n < tx.vin.size(); ++n) for (size_t n = 0; n < tx.vin.size(); ++n)
{ {
if (memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &II[n], 32)) if (memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.MG.II[n], 32))
{ {
LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes");
return false; return false;
} }
} }
if (!rct::verRct(tx.rct_signatures, mixRing, II, outPk, rct::hash2rct(tx_prefix_hash))) if (!rct::verRct(rv))
{ {
LOG_PRINT_L1("Failed to check ringct signatures!"); LOG_PRINT_L1("Failed to check ringct signatures!");
return false; return false;
@ -2678,7 +2652,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
break; break;
} }
default: default:
LOG_PRINT_L1("Unsupported rct type: " << tx.rct_signatures.type); LOG_PRINT_L1("Unsupported rct type: " << rv.type);
return false; return false;
} }
} }
@ -2787,11 +2761,7 @@ bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, cons
if (tx_version == 1) { if (tx_version == 1) {
CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size());
} }
else // rct_signatures will be expanded after this
{
// rct signatures may be empty (and will be reconstructed later in the caller if so)
CHECK_AND_ASSERT_MES(rct_signatures.mixRing.empty() || rct_signatures.mixRing.size() == output_keys.size(), false, "internal error: tx rct signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size());
}
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------

View file

@ -500,6 +500,7 @@ namespace cryptonote
* validates a transaction's inputs as correctly used and not previously * validates a transaction's inputs as correctly used and not previously
* spent. also returns the hash and height of the most recent block * spent. also returns the hash and height of the most recent block
* which contains an output that was used as an input to the transaction. * which contains an output that was used as an input to the transaction.
* The transaction's rct signatures, if any, are expanded.
* *
* @param tx the transaction to validate * @param tx the transaction to validate
* @param pmax_used_block_height return-by-reference block height of most recent input * @param pmax_used_block_height return-by-reference block height of most recent input
@ -509,7 +510,7 @@ namespace cryptonote
* *
* @return false if any input is invalid, otherwise true * @return false if any input is invalid, otherwise true
*/ */
bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false); bool check_tx_inputs(transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false);
/** /**
* @brief check that a transaction's outputs conform to current standards * @brief check that a transaction's outputs conform to current standards
@ -919,6 +920,7 @@ namespace cryptonote
* This function validates transaction inputs and their keys. Previously * This function validates transaction inputs and their keys. Previously
* it also performed double spend checking, but that has been moved to its * it also performed double spend checking, but that has been moved to its
* own function. * own function.
* The transaction's rct signatures, if any, are expanded.
* *
* If pmax_related_block_height is not NULL, its value is set to the height * If pmax_related_block_height is not NULL, its value is set to the height
* of the most recent block which contains an output used in any input set * of the most recent block which contains an output used in any input set
@ -932,7 +934,7 @@ namespace cryptonote
* *
* @return false if any validation step fails, otherwise true * @return false if any validation step fails, otherwise true
*/ */
bool check_tx_inputs(const transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL); bool check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL);
/** /**
* @brief performs a blockchain reorganization according to the longest chain rule * @brief performs a blockchain reorganization according to the longest chain rule
@ -1211,5 +1213,14 @@ namespace cryptonote
* a useful state. * a useful state.
*/ */
void load_compiled_in_block_hashes(); void load_compiled_in_block_hashes();
/**
* @brief expands v2 transaction data from blockchain
*
* RingCT transactions do not transmit some of their data if it
* can be reconstituted by the receiver. This function expands
* that implicit data.
*/
bool expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys);
}; };
} // namespace cryptonote } // namespace cryptonote

View file

@ -190,7 +190,9 @@ namespace cryptonote
crypto::hash max_used_block_id = null_hash; crypto::hash max_used_block_id = null_hash;
uint64_t max_used_block_height = 0; uint64_t max_used_block_height = 0;
bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id, tvc, kept_by_block); tx_details txd;
txd.tx = tx;
bool ch_inp_res = m_blockchain.check_tx_inputs(txd.tx, max_used_block_height, max_used_block_id, tvc, kept_by_block);
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
if(!ch_inp_res) if(!ch_inp_res)
{ {
@ -198,10 +200,9 @@ namespace cryptonote
// may become valid again, so ignore the failed inputs check. // may become valid again, so ignore the failed inputs check.
if(kept_by_block) if(kept_by_block)
{ {
auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); auto txd_p = m_transactions.insert(transactions_container::value_type(id, txd));
CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool"); CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool");
txd_p.first->second.blob_size = blob_size; txd_p.first->second.blob_size = blob_size;
txd_p.first->second.tx = tx;
txd_p.first->second.fee = fee; txd_p.first->second.fee = fee;
txd_p.first->second.max_used_block_id = null_hash; txd_p.first->second.max_used_block_id = null_hash;
txd_p.first->second.max_used_block_height = 0; txd_p.first->second.max_used_block_height = 0;
@ -220,10 +221,9 @@ namespace cryptonote
}else }else
{ {
//update transactions container //update transactions container
auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); auto txd_p = m_transactions.insert(transactions_container::value_type(id, txd));
CHECK_AND_ASSERT_MES(txd_p.second, false, "intrnal error: transaction already exists at inserting in memorypool"); CHECK_AND_ASSERT_MES(txd_p.second, false, "intrnal error: transaction already exists at inserting in memorypool");
txd_p.first->second.blob_size = blob_size; txd_p.first->second.blob_size = blob_size;
txd_p.first->second.tx = tx;
txd_p.first->second.kept_by_block = kept_by_block; txd_p.first->second.kept_by_block = kept_by_block;
txd_p.first->second.fee = fee; txd_p.first->second.fee = fee;
txd_p.first->second.max_used_block_id = max_used_block_id; txd_p.first->second.max_used_block_id = max_used_block_id;

View file

@ -237,7 +237,7 @@ namespace rct {
// Gen creates a signature which proves that for some column in the keymatrix "pk" // Gen creates a signature which proves that for some column in the keymatrix "pk"
// the signer knows a secret key for each row in that column // the signer knows a secret key for each row in that column
// Ver verifies that the MG sig was created correctly // Ver verifies that the MG sig was created correctly
bool MLSAG_Ver(key message, const keyM & pk, const mgSig & rv, const keyV &II, size_t dsRows) { bool MLSAG_Ver(key message, const keyM & pk, const mgSig & rv, size_t dsRows) {
size_t cols = pk.size(); size_t cols = pk.size();
CHECK_AND_ASSERT_MES(cols >= 2, false, "Error! What is c if cols = 1!"); CHECK_AND_ASSERT_MES(cols >= 2, false, "Error! What is c if cols = 1!");
@ -246,7 +246,7 @@ namespace rct {
for (size_t i = 1; i < cols; ++i) { for (size_t i = 1; i < cols; ++i) {
CHECK_AND_ASSERT_MES(pk[i].size() == rows, false, "pk is not rectangular"); CHECK_AND_ASSERT_MES(pk[i].size() == rows, false, "pk is not rectangular");
} }
CHECK_AND_ASSERT_MES(II.size() == dsRows, false, "Bad II size"); CHECK_AND_ASSERT_MES(rv.II.size() == dsRows, false, "Bad II size");
CHECK_AND_ASSERT_MES(rv.ss.size() == cols, false, "Bad rv.ss size"); CHECK_AND_ASSERT_MES(rv.ss.size() == cols, false, "Bad rv.ss size");
for (size_t i = 0; i < cols; ++i) { for (size_t i = 0; i < cols; ++i) {
CHECK_AND_ASSERT_MES(rv.ss[i].size() == rows, false, "rv.ss is not rectangular"); CHECK_AND_ASSERT_MES(rv.ss[i].size() == rows, false, "rv.ss is not rectangular");
@ -258,7 +258,7 @@ namespace rct {
key c_old = copy(rv.cc); key c_old = copy(rv.cc);
vector<geDsmp> Ip(dsRows); vector<geDsmp> Ip(dsRows);
for (i = 0 ; i < dsRows ; i++) { for (i = 0 ; i < dsRows ; i++) {
precomp(Ip[i].k, II[i]); precomp(Ip[i].k, rv.II[i]);
} }
size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper
keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows)); keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows));
@ -341,6 +341,43 @@ namespace rct {
return (reb && rab); return (reb && rab);
} }
key get_pre_mlsag_hash(const rctSig &rv)
{
keyV kv;
kv.push_back(d2h(rv.type));
kv.push_back(rv.message);
for (auto r: rv.rangeSigs)
{
for (size_t n = 0; n < 64; ++n)
kv.push_back(r.asig.L1[n]);
for (size_t n = 0; n < 64; ++n)
kv.push_back(r.asig.s2[n]);
kv.push_back(r.asig.s);
for (size_t n = 0; n < 64; ++n)
kv.push_back(r.Ci[n]);
}
// no MG/MGs, that's what will sign all this
// no mixRing, it's part of the vin already
for (auto o: rv.pseudoOuts)
{
kv.push_back(o);
}
for (auto i: rv.ecdhInfo)
{
kv.push_back(i.mask);
kv.push_back(i.amount);
// no senderPk, unused here
}
for (auto o: rv.outPk)
{
kv.push_back(o.dest);
kv.push_back(o.mask);
}
kv.push_back(d2h(rv.txnFee));
return cn_fast_hash(kv);
}
//Ring-ct MG sigs //Ring-ct MG sigs
//Prove: //Prove:
// c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10. // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10.
@ -393,10 +430,7 @@ namespace rct {
for (size_t j = 0; j < outPk.size(); j++) { for (size_t j = 0; j < outPk.size(); j++) {
sc_sub(sk[rows].bytes, sk[rows].bytes, outSk[j].mask.bytes); //subtract output masks in last row.. sc_sub(sk[rows].bytes, sk[rows].bytes, outSk[j].mask.bytes); //subtract output masks in last row..
} }
ctkeyV signed_data = outPk; return MLSAG_Gen(message, M, sk, index, rows);
signed_data.push_back(ctkey({message, identity()}));
key msg = cn_fast_hash(signed_data);
return MLSAG_Gen(msg, M, sk, index, rows);
} }
@ -435,7 +469,7 @@ namespace rct {
// this shows that sum inputs = sum outputs // this shows that sum inputs = sum outputs
//Ver: //Ver:
// verifies the above sig is created corretly // verifies the above sig is created corretly
bool verRctMG(mgSig mg, const keyV &II, const ctkeyM & pubs, const ctkeyV & outPk, key txnFeeKey, const key &message) { bool verRctMG(mgSig mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFeeKey, const key &message) {
//setup vars //setup vars
size_t cols = pubs.size(); size_t cols = pubs.size();
CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs"); CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs");
@ -466,19 +500,14 @@ namespace rct {
//subtract txn fee output in last row //subtract txn fee output in last row
subKeys(M[i][rows], M[i][rows], txnFeeKey); subKeys(M[i][rows], M[i][rows], txnFeeKey);
} }
ctkeyV signed_data = outPk; return MLSAG_Ver(message, M, mg, rows);
signed_data.push_back(ctkey({message, identity()}));
key msg = cn_fast_hash(signed_data);
DP("message:");
DP(msg);
return MLSAG_Ver(msg, M, mg, II, rows);
} }
//Ring-ct Simple MG sigs //Ring-ct Simple MG sigs
//Ver: //Ver:
//This does a simplified version, assuming only post Rct //This does a simplified version, assuming only post Rct
//inputs //inputs
bool verRctMGSimple(const key &message, const mgSig &mg, const keyV &II, const ctkeyV & pubs, const key & C) { bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C) {
//setup vars //setup vars
size_t rows = 1; size_t rows = 1;
size_t cols = pubs.size(); size_t cols = pubs.size();
@ -492,7 +521,7 @@ namespace rct {
subKeys(M[i][1], pubs[i].mask, C); subKeys(M[i][1], pubs[i].mask, C);
} }
//DP(C); //DP(C);
return MLSAG_Ver(message, M, mg, II, rows); return MLSAG_Ver(message, M, mg, rows);
} }
//These functions get keys from blockchain //These functions get keys from blockchain
@ -599,8 +628,7 @@ namespace rct {
key txnFeeKey = scalarmultH(d2h(rv.txnFee)); key txnFeeKey = scalarmultH(d2h(rv.txnFee));
rv.mixRing = mixRing; rv.mixRing = mixRing;
rv.message = message; rv.MG = proveRctMG(get_pre_mlsag_hash(rv), rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey);
rv.MG = proveRctMG(message, rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey);
return rv; return rv;
} }
@ -626,7 +654,6 @@ namespace rct {
rctSig rv; rctSig rv;
rv.type = RCTTypeSimple; rv.type = RCTTypeSimple;
rv.message = message;
rv.outPk.resize(destinations.size()); rv.outPk.resize(destinations.size());
rv.rangeSigs.resize(destinations.size()); rv.rangeSigs.resize(destinations.size());
rv.ecdhInfo.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size());
@ -661,18 +688,21 @@ namespace rct {
rv.pseudoOuts.resize(inamounts.size()); rv.pseudoOuts.resize(inamounts.size());
rv.MGs.resize(inamounts.size()); rv.MGs.resize(inamounts.size());
key sumpouts = zero(); //sum pseudoOut masks key sumpouts = zero(); //sum pseudoOut masks
key a; keyV a(inamounts.size());
for (i = 0 ; i < inamounts.size() - 1; i++) { for (i = 0 ; i < inamounts.size() - 1; i++) {
skGen(a); skGen(a[i]);
sc_add(sumpouts.bytes, a.bytes, sumpouts.bytes); sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes);
genC(rv.pseudoOuts[i], a, inamounts[i]); genC(rv.pseudoOuts[i], a[i], inamounts[i]);
rv.MGs[i] = proveRctMGSimple(message, rv.mixRing[i], inSk[i], a, rv.pseudoOuts[i], index[i]);
} }
rv.mixRing = mixRing; rv.mixRing = mixRing;
sc_sub(a.bytes, sumout.bytes, sumpouts.bytes); sc_sub(a[i].bytes, sumout.bytes, sumpouts.bytes);
genC(rv.pseudoOuts[i], a, inamounts[i]); genC(rv.pseudoOuts[i], a[i], inamounts[i]);
DP(rv.pseudoOuts[i]); DP(rv.pseudoOuts[i]);
rv.MGs[i] = proveRctMGSimple(message, rv.mixRing[i], inSk[i], a, rv.pseudoOuts[i], index[i]);
key full_message = get_pre_mlsag_hash(rv);
for (i = 0 ; i < inamounts.size(); i++) {
rv.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], rv.pseudoOuts[i], index[i]);
}
return rv; return rv;
} }
@ -699,10 +729,10 @@ 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, const ctkeyM &mixRing, const keyV &II, const ctkeyV &outPk, const key &message) { bool verRct(const rctSig & rv) {
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(outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.rangeSigs"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.rangeSigs");
CHECK_AND_ASSERT_MES(outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
// some rct ops can throw // some rct ops can throw
try try
@ -711,14 +741,14 @@ namespace rct {
bool rvb = true; bool rvb = true;
bool tmp; bool tmp;
DP("range proofs verified?"); DP("range proofs verified?");
for (i = 0; i < outPk.size(); i++) { for (i = 0; i < rv.outPk.size(); i++) {
tmp = verRange(outPk[i].mask, rv.rangeSigs[i]); tmp = verRange(rv.outPk[i].mask, rv.rangeSigs[i]);
DP(tmp); DP(tmp);
rvb = (rvb && tmp); rvb = (rvb && tmp);
} }
//compute txn fee //compute txn fee
key txnFeeKey = scalarmultH(d2h(rv.txnFee)); key txnFeeKey = scalarmultH(d2h(rv.txnFee));
bool mgVerd = verRctMG(rv.MG, II, mixRing, outPk, txnFeeKey, message); bool mgVerd = verRctMG(rv.MG, rv.mixRing, rv.outPk, txnFeeKey, get_pre_mlsag_hash(rv));
DP("mg sig verified?"); DP("mg sig verified?");
DP(mgVerd); DP(mgVerd);
@ -729,45 +759,35 @@ namespace rct {
return false; return false;
} }
} }
bool verRct(const rctSig & rv) {
return verRct(rv, rv.mixRing, rv.MG.II, rv.outPk, rv.message);
}
//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, const ctkeyM &mixRing, const std::vector<keyV> *II, const ctkeyV &outPk, const key &message) { bool verRctSimple(const rctSig & rv) {
size_t i = 0; size_t i = 0;
bool rvb = true; bool rvb = true;
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(outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.rangeSigs"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.rangeSigs");
CHECK_AND_ASSERT_MES(outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.MGs"); CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.MGs");
CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing");
CHECK_AND_ASSERT_MES(!II || II->size() == mixRing.size(), false, "Mismatched II/mixRing size");
if (II)
{
for (size_t n = 0; n < II->size(); ++n)
{
CHECK_AND_ASSERT_MES((*II)[n].size() == 1, false, "Bad II size");
}
}
key sumOutpks = identity(); key sumOutpks = identity();
for (i = 0; i < outPk.size(); i++) { for (i = 0; i < rv.outPk.size(); i++) {
if (!verRange(outPk[i].mask, rv.rangeSigs[i])) { if (!verRange(rv.outPk[i].mask, rv.rangeSigs[i])) {
return false; return false;
} }
addKeys(sumOutpks, sumOutpks, outPk[i].mask); addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask);
} }
DP(sumOutpks); DP(sumOutpks);
key txnFeeKey = scalarmultH(d2h(rv.txnFee)); key txnFeeKey = scalarmultH(d2h(rv.txnFee));
addKeys(sumOutpks, txnFeeKey, sumOutpks); addKeys(sumOutpks, txnFeeKey, sumOutpks);
bool tmpb = false; bool tmpb = false;
key message = get_pre_mlsag_hash(rv);
key sumPseudoOuts = identity(); key sumPseudoOuts = identity();
for (i = 0 ; i < mixRing.size() ; i++) { for (i = 0 ; i < rv.mixRing.size() ; i++) {
tmpb = verRctMGSimple(message, rv.MGs[i], II ? (*II)[i] : rv.MGs[i].II, mixRing[i], rv.pseudoOuts[i]); tmpb = verRctMGSimple(message, rv.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]);
addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]); addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]);
DP(tmpb); DP(tmpb);
if (!tmpb) { if (!tmpb) {
@ -788,10 +808,6 @@ namespace rct {
return (rvb && mgVerd); return (rvb && mgVerd);
} }
bool verRctSimple(const rctSig & rv) {
return verRctSimple(rv, rv.mixRing, NULL, rv.outPk, rv.message);
}
//RingCT protocol //RingCT protocol
//genRct: //genRct:
// creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the

View file

@ -91,7 +91,7 @@ namespace rct {
// Ver verifies that the MG sig was created correctly // Ver verifies that the MG sig was created correctly
keyV keyImageV(const keyV &xx); keyV keyImageV(const keyV &xx);
mgSig MLSAG_Gen(key message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows); mgSig MLSAG_Gen(key message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows);
bool MLSAG_Ver(key message, const keyM &pk, const mgSig &sig, const keyV &II, size_t dsRows); bool MLSAG_Ver(key message, const keyM &pk, const mgSig &sig, size_t dsRows);
//mgSig MLSAG_Gen_Old(const keyM & pk, const keyV & xx, const int index); //mgSig MLSAG_Gen_Old(const keyM & pk, const keyV & xx, const int index);
//proveRange and verRange //proveRange and verRange
@ -115,7 +115,7 @@ namespace rct {
mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, unsigned int index, key txnFee, const key &message); mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, unsigned int index, key txnFee, const key &message);
mgSig proveRctMGSimple(const key & message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, unsigned int index); mgSig proveRctMGSimple(const key & message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, unsigned int index);
bool verRctMG(mgSig mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFee, const key &message); bool verRctMG(mgSig mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFee, const key &message);
bool verRctMGSimple(const key &message, const mgSig &mg, const keyV &II, const ctkeyV & pubs, const key & C); bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C);
//These functions get keys from blockchain //These functions get keys from blockchain
//replace these when connecting blockchain //replace these when connecting blockchain
@ -140,9 +140,7 @@ namespace rct {
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 verRct(const rctSig & rv, const ctkeyM &mixRing, const keyV &II, const ctkeyV &outPk, const key &message);
bool verRctSimple(const rctSig & rv); bool verRctSimple(const rctSig & rv);
bool verRctSimple(const rctSig & rv, const ctkeyM &mixRing, const std::vector<keyV> *II, const ctkeyV &outPk, const key &message);
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 decodeRctFromSharedSecret(const rctSig & rv, const key & sk, unsigned int i, key & mask); xmr_amount decodeRctFromSharedSecret(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);

View file

@ -130,8 +130,8 @@ TEST(ringct, MG_sigs)
sk[j] = xm[ind][j]; sk[j] = xm[ind][j];
} }
key message = identity(); key message = identity();
mgSig IIccss = MLSAG_Gen(message, P, sk, ind); mgSig IIccss = MLSAG_Gen(message, P, sk, ind, R);
ASSERT_TRUE(MLSAG_Ver(message, P, IIccss, IIccss.II)); ASSERT_TRUE(MLSAG_Ver(message, P, IIccss, R));
//#MG sig: false one //#MG sig: false one
N = 3;// #cols N = 3;// #cols
@ -151,8 +151,8 @@ TEST(ringct, MG_sigs)
sk[j] = xx[ind][j]; sk[j] = xx[ind][j];
} }
sk[2] = skGen();//asume we don't know one of the private keys.. sk[2] = skGen();//asume we don't know one of the private keys..
IIccss = MLSAG_Gen(message, P, sk, ind); IIccss = MLSAG_Gen(message, P, sk, ind, R);
ASSERT_FALSE(MLSAG_Ver(message, P, IIccss, IIccss.II)); ASSERT_FALSE(MLSAG_Ver(message, P, IIccss, R));
} }
TEST(ringct, range_proofs) TEST(ringct, range_proofs)