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:
parent
3ab2ab3e76
commit
d93746b6d3
6 changed files with 195 additions and 200 deletions
|
@ -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
|
||||
// an extra value (hash of highest block that holds an output used as input)
|
||||
// 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__);
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
@ -2235,13 +2235,82 @@ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const
|
|||
}
|
||||
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.
|
||||
// FIXME: consider moving functionality specific to one input into
|
||||
// check_tx_input() rather than here, and use this function simply
|
||||
// to iterate the inputs as necessary (splitting the task
|
||||
// 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__);
|
||||
size_t sig_index = 0;
|
||||
|
@ -2461,71 +2530,29 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
|
|||
}
|
||||
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
|
||||
// obviously, the original and simple rct APIs use a mixRing that's indexes
|
||||
// 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: {
|
||||
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
|
||||
{
|
||||
if (pubkeys.size() != mixRing.size())
|
||||
if (pubkeys.size() != rv.mixRing.size())
|
||||
{
|
||||
LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size");
|
||||
return false;
|
||||
}
|
||||
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");
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
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");
|
||||
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!");
|
||||
return false;
|
||||
|
@ -2572,66 +2599,13 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
|
|||
break;
|
||||
}
|
||||
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
|
||||
{
|
||||
bool size_matches = true;
|
||||
for (size_t i = 0; i < pubkeys.size(); ++i)
|
||||
size_matches &= pubkeys[i].size() == mixRing.size();
|
||||
for (size_t i = 0; i < tx.rct_signatures.mixRing.size(); ++i)
|
||||
size_matches &= pubkeys.size() == mixRing[i].size();
|
||||
size_matches &= pubkeys[i].size() == rv.mixRing.size();
|
||||
for (size_t i = 0; i < rv.mixRing.size(); ++i)
|
||||
size_matches &= pubkeys.size() == rv.mixRing[i].size();
|
||||
if (!size_matches)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
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");
|
||||
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!");
|
||||
return false;
|
||||
|
@ -2678,7 +2652,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
|
|||
break;
|
||||
}
|
||||
default:
|
||||
LOG_PRINT_L1("Unsupported rct type: " << tx.rct_signatures.type);
|
||||
LOG_PRINT_L1("Unsupported rct type: " << rv.type);
|
||||
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) {
|
||||
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 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());
|
||||
}
|
||||
// rct_signatures will be expanded after this
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
|
|
|
@ -500,6 +500,7 @@ namespace cryptonote
|
|||
* validates a transaction's inputs as correctly used and not previously
|
||||
* 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.
|
||||
* The transaction's rct signatures, if any, are expanded.
|
||||
*
|
||||
* @param tx the transaction to validate
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
|
@ -919,6 +920,7 @@ namespace cryptonote
|
|||
* This function validates transaction inputs and their keys. Previously
|
||||
* it also performed double spend checking, but that has been moved to its
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
|
@ -1211,5 +1213,14 @@ namespace cryptonote
|
|||
* a useful state.
|
||||
*/
|
||||
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
|
||||
|
|
|
@ -190,7 +190,9 @@ namespace cryptonote
|
|||
|
||||
crypto::hash max_used_block_id = null_hash;
|
||||
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);
|
||||
if(!ch_inp_res)
|
||||
{
|
||||
|
@ -198,10 +200,9 @@ namespace cryptonote
|
|||
// may become valid again, so ignore the failed inputs check.
|
||||
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");
|
||||
txd_p.first->second.blob_size = blob_size;
|
||||
txd_p.first->second.tx = tx;
|
||||
txd_p.first->second.fee = fee;
|
||||
txd_p.first->second.max_used_block_id = null_hash;
|
||||
txd_p.first->second.max_used_block_height = 0;
|
||||
|
@ -220,10 +221,9 @@ namespace cryptonote
|
|||
}else
|
||||
{
|
||||
//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");
|
||||
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.fee = fee;
|
||||
txd_p.first->second.max_used_block_id = max_used_block_id;
|
||||
|
|
|
@ -237,7 +237,7 @@ namespace rct {
|
|||
// 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
|
||||
// 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();
|
||||
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) {
|
||||
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");
|
||||
for (size_t i = 0; i < cols; ++i) {
|
||||
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);
|
||||
vector<geDsmp> Ip(dsRows);
|
||||
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
|
||||
keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows));
|
||||
|
@ -341,6 +341,43 @@ namespace rct {
|
|||
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
|
||||
//Prove:
|
||||
// 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++) {
|
||||
sc_sub(sk[rows].bytes, sk[rows].bytes, outSk[j].mask.bytes); //subtract output masks in last row..
|
||||
}
|
||||
ctkeyV signed_data = outPk;
|
||||
signed_data.push_back(ctkey({message, identity()}));
|
||||
key msg = cn_fast_hash(signed_data);
|
||||
return MLSAG_Gen(msg, M, sk, index, rows);
|
||||
return MLSAG_Gen(message, M, sk, index, rows);
|
||||
}
|
||||
|
||||
|
||||
|
@ -435,7 +469,7 @@ namespace rct {
|
|||
// this shows that sum inputs = sum outputs
|
||||
//Ver:
|
||||
// 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
|
||||
size_t cols = pubs.size();
|
||||
CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs");
|
||||
|
@ -466,19 +500,14 @@ namespace rct {
|
|||
//subtract txn fee output in last row
|
||||
subKeys(M[i][rows], M[i][rows], txnFeeKey);
|
||||
}
|
||||
ctkeyV signed_data = outPk;
|
||||
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);
|
||||
return MLSAG_Ver(message, M, mg, rows);
|
||||
}
|
||||
|
||||
//Ring-ct Simple MG sigs
|
||||
//Ver:
|
||||
//This does a simplified version, assuming only post Rct
|
||||
//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
|
||||
size_t rows = 1;
|
||||
size_t cols = pubs.size();
|
||||
|
@ -492,7 +521,7 @@ namespace rct {
|
|||
subKeys(M[i][1], pubs[i].mask, C);
|
||||
}
|
||||
//DP(C);
|
||||
return MLSAG_Ver(message, M, mg, II, rows);
|
||||
return MLSAG_Ver(message, M, mg, rows);
|
||||
}
|
||||
|
||||
//These functions get keys from blockchain
|
||||
|
@ -599,8 +628,7 @@ namespace rct {
|
|||
key txnFeeKey = scalarmultH(d2h(rv.txnFee));
|
||||
|
||||
rv.mixRing = mixRing;
|
||||
rv.message = message;
|
||||
rv.MG = proveRctMG(message, rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey);
|
||||
rv.MG = proveRctMG(get_pre_mlsag_hash(rv), rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -626,7 +654,6 @@ namespace rct {
|
|||
|
||||
rctSig rv;
|
||||
rv.type = RCTTypeSimple;
|
||||
rv.message = message;
|
||||
rv.outPk.resize(destinations.size());
|
||||
rv.rangeSigs.resize(destinations.size());
|
||||
rv.ecdhInfo.resize(destinations.size());
|
||||
|
@ -661,18 +688,21 @@ namespace rct {
|
|||
rv.pseudoOuts.resize(inamounts.size());
|
||||
rv.MGs.resize(inamounts.size());
|
||||
key sumpouts = zero(); //sum pseudoOut masks
|
||||
key a;
|
||||
keyV a(inamounts.size());
|
||||
for (i = 0 ; i < inamounts.size() - 1; i++) {
|
||||
skGen(a);
|
||||
sc_add(sumpouts.bytes, a.bytes, sumpouts.bytes);
|
||||
genC(rv.pseudoOuts[i], a, inamounts[i]);
|
||||
rv.MGs[i] = proveRctMGSimple(message, rv.mixRing[i], inSk[i], a, rv.pseudoOuts[i], index[i]);
|
||||
skGen(a[i]);
|
||||
sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes);
|
||||
genC(rv.pseudoOuts[i], a[i], inamounts[i]);
|
||||
}
|
||||
rv.mixRing = mixRing;
|
||||
sc_sub(a.bytes, sumout.bytes, sumpouts.bytes);
|
||||
genC(rv.pseudoOuts[i], a, inamounts[i]);
|
||||
sc_sub(a[i].bytes, sumout.bytes, sumpouts.bytes);
|
||||
genC(rv.pseudoOuts[i], a[i], inamounts[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;
|
||||
}
|
||||
|
||||
|
@ -699,10 +729,10 @@ 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, 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(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.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.rangeSigs");
|
||||
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
|
||||
|
||||
// some rct ops can throw
|
||||
try
|
||||
|
@ -711,14 +741,14 @@ namespace rct {
|
|||
bool rvb = true;
|
||||
bool tmp;
|
||||
DP("range proofs verified?");
|
||||
for (i = 0; i < outPk.size(); i++) {
|
||||
tmp = verRange(outPk[i].mask, rv.rangeSigs[i]);
|
||||
for (i = 0; i < rv.outPk.size(); i++) {
|
||||
tmp = verRange(rv.outPk[i].mask, rv.rangeSigs[i]);
|
||||
DP(tmp);
|
||||
rvb = (rvb && tmp);
|
||||
}
|
||||
//compute txn fee
|
||||
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(mgVerd);
|
||||
|
||||
|
@ -729,45 +759,35 @@ namespace rct {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
bool verRct(const rctSig & rv) {
|
||||
return verRct(rv, rv.mixRing, rv.MG.II, rv.outPk, rv.message);
|
||||
}
|
||||
|
||||
|
||||
//ver RingCT simple
|
||||
//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;
|
||||
bool rvb = true;
|
||||
|
||||
|
||||
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(outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
|
||||
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.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.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(!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");
|
||||
}
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing");
|
||||
|
||||
key sumOutpks = identity();
|
||||
for (i = 0; i < outPk.size(); i++) {
|
||||
if (!verRange(outPk[i].mask, rv.rangeSigs[i])) {
|
||||
for (i = 0; i < rv.outPk.size(); i++) {
|
||||
if (!verRange(rv.outPk[i].mask, rv.rangeSigs[i])) {
|
||||
return false;
|
||||
}
|
||||
addKeys(sumOutpks, sumOutpks, outPk[i].mask);
|
||||
addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask);
|
||||
}
|
||||
DP(sumOutpks);
|
||||
key txnFeeKey = scalarmultH(d2h(rv.txnFee));
|
||||
addKeys(sumOutpks, txnFeeKey, sumOutpks);
|
||||
|
||||
bool tmpb = false;
|
||||
key message = get_pre_mlsag_hash(rv);
|
||||
key sumPseudoOuts = identity();
|
||||
for (i = 0 ; i < mixRing.size() ; i++) {
|
||||
tmpb = verRctMGSimple(message, rv.MGs[i], II ? (*II)[i] : rv.MGs[i].II, mixRing[i], rv.pseudoOuts[i]);
|
||||
for (i = 0 ; i < rv.mixRing.size() ; i++) {
|
||||
tmpb = verRctMGSimple(message, rv.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]);
|
||||
addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]);
|
||||
DP(tmpb);
|
||||
if (!tmpb) {
|
||||
|
@ -788,10 +808,6 @@ namespace rct {
|
|||
return (rvb && mgVerd);
|
||||
}
|
||||
|
||||
bool verRctSimple(const rctSig & rv) {
|
||||
return verRctSimple(rv, rv.mixRing, NULL, rv.outPk, rv.message);
|
||||
}
|
||||
|
||||
//RingCT protocol
|
||||
//genRct:
|
||||
// creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the
|
||||
|
|
|
@ -91,7 +91,7 @@ namespace rct {
|
|||
// Ver verifies that the MG sig was created correctly
|
||||
keyV keyImageV(const keyV &xx);
|
||||
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);
|
||||
|
||||
//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 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 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
|
||||
//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 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, const ctkeyM &mixRing, const keyV &II, const ctkeyV &outPk, const key &message);
|
||||
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 decodeRctFromSharedSecret(const rctSig & rv, const key & sk, unsigned int i, key & mask);
|
||||
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i);
|
||||
|
|
|
@ -130,8 +130,8 @@ TEST(ringct, MG_sigs)
|
|||
sk[j] = xm[ind][j];
|
||||
}
|
||||
key message = identity();
|
||||
mgSig IIccss = MLSAG_Gen(message, P, sk, ind);
|
||||
ASSERT_TRUE(MLSAG_Ver(message, P, IIccss, IIccss.II));
|
||||
mgSig IIccss = MLSAG_Gen(message, P, sk, ind, R);
|
||||
ASSERT_TRUE(MLSAG_Ver(message, P, IIccss, R));
|
||||
|
||||
//#MG sig: false one
|
||||
N = 3;// #cols
|
||||
|
@ -151,8 +151,8 @@ TEST(ringct, MG_sigs)
|
|||
sk[j] = xx[ind][j];
|
||||
}
|
||||
sk[2] = skGen();//asume we don't know one of the private keys..
|
||||
IIccss = MLSAG_Gen(message, P, sk, ind);
|
||||
ASSERT_FALSE(MLSAG_Ver(message, P, IIccss, IIccss.II));
|
||||
IIccss = MLSAG_Gen(message, P, sk, ind, R);
|
||||
ASSERT_FALSE(MLSAG_Ver(message, P, IIccss, R));
|
||||
}
|
||||
|
||||
TEST(ringct, range_proofs)
|
||||
|
|
Loading…
Reference in a new issue