aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptonote_core')
-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
3 files changed, 114 insertions, 133 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;