aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cryptonote_core/blockchain.cpp222
-rw-r--r--src/cryptonote_core/blockchain.h15
-rw-r--r--src/cryptonote_core/tx_pool.cpp10
-rw-r--r--src/ringct/rctSigs.cpp134
-rw-r--r--src/ringct/rctSigs.h6
-rw-r--r--tests/unit_tests/ringct.cpp8
6 files changed, 195 insertions, 200 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index c81445f1d..08cb85a55 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -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;
}
//------------------------------------------------------------------
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index b22074c57..94701608e 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -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
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index dcbb7cc04..2ed12eb5c 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -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;
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index d031f6c79..ca38f13dd 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -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
diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h
index 558af22fd..bf9d4be81 100644
--- a/src/ringct/rctSigs.h
+++ b/src/ringct/rctSigs.h
@@ -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);
diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp
index 81a63b502..5be74ec7e 100644
--- a/tests/unit_tests/ringct.cpp
+++ b/tests/unit_tests/ringct.cpp
@@ -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)