aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core/tx_verification_utils.cpp
diff options
context:
space:
mode:
authortobtoht <tob@featherwallet.org>2025-03-10 16:20:00 +0000
committertobtoht <tob@featherwallet.org>2025-03-10 16:20:00 +0000
commit3da68db9789697d30dc309df2f1da5d348bc4ce2 (patch)
tree68c98fab9146797007ab778cdb4a609e1e41f43c /src/cryptonote_core/tx_verification_utils.cpp
parent0232839913b13cf0ab0bb7ad25fff0c05f37d2fe (diff)
parent008ba966da88f073f226b299533faca905ceabf8 (diff)
downloadmonzero-core-3da68db9789697d30dc309df2f1da5d348bc4ce2.tar.gz
monzero-core-3da68db9789697d30dc309df2f1da5d348bc4ce2.tar.xz
monzero-core-3da68db9789697d30dc309df2f1da5d348bc4ce2.zip
Merge pull request #9740
008ba966d blockchain sync: reduce disk writes from 2 to 1 per tx (jeffro256)
Diffstat (limited to 'src/cryptonote_core/tx_verification_utils.cpp')
-rw-r--r--src/cryptonote_core/tx_verification_utils.cpp197
1 files changed, 197 insertions, 0 deletions
diff --git a/src/cryptonote_core/tx_verification_utils.cpp b/src/cryptonote_core/tx_verification_utils.cpp
index a93ef2f25..e79adcfb0 100644
--- a/src/cryptonote_core/tx_verification_utils.cpp
+++ b/src/cryptonote_core/tx_verification_utils.cpp
@@ -26,8 +26,12 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <boost/iterator/transform_iterator.hpp>
+
#include "cryptonote_core/blockchain.h"
+#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/tx_verification_utils.h"
+#include "hardforks/hardforks.h"
#include "ringct/rctSigs.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -105,11 +109,104 @@ static crypto::hash calc_tx_mixring_hash(const transaction& tx, const rct::ctkey
return tx_and_mixring_hash;
}
+static bool is_canonical_bulletproof_layout(const std::vector<rct::Bulletproof> &proofs)
+{
+ if (proofs.size() != 1)
+ return false;
+ const size_t sz = proofs[0].V.size();
+ if (sz == 0 || sz > BULLETPROOF_MAX_OUTPUTS)
+ return false;
+ return true;
+}
+
+static bool is_canonical_bulletproof_plus_layout(const std::vector<rct::BulletproofPlus> &proofs)
+{
+ if (proofs.size() != 1)
+ return false;
+ const size_t sz = proofs[0].V.size();
+ if (sz == 0 || sz > BULLETPROOF_PLUS_MAX_OUTPUTS)
+ return false;
+ return true;
+}
+
+template <class TxForwardIt>
+static bool ver_non_input_consensus_templated(TxForwardIt tx_begin, TxForwardIt tx_end,
+ tx_verification_context& tvc, std::uint8_t hf_version)
+{
+ std::vector<const rct::rctSig*> rvv;
+ rvv.reserve(static_cast<size_t>(std::distance(tx_begin, tx_end)));
+
+ const size_t max_tx_version = hf_version < HF_VERSION_DYNAMIC_FEE ? 1 : 2;
+
+ const size_t tx_weight_limit = get_transaction_weight_limit(hf_version);
+
+ for (; tx_begin != tx_end; ++tx_begin)
+ {
+ const transaction& tx = *tx_begin;
+ const uint64_t blob_size = get_transaction_blob_size(tx);
+
+ // Rule 1
+ if (blob_size > get_max_tx_size())
+ {
+ tvc.m_verifivation_failed = true;
+ tvc.m_too_big = true;
+ return false;
+ }
+
+ // Rule 2 & 3
+ if (tx.version == 0 || tx.version > max_tx_version)
+ {
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
+
+ // Rule 4
+ const size_t tx_weight = get_transaction_weight(tx, blob_size);
+ if (hf_version >= HF_VERSION_PER_BYTE_FEE && tx_weight > tx_weight_limit)
+ {
+ tvc.m_verifivation_failed = true;
+ tvc.m_too_big = true;
+ return false;
+ }
+
+ // Rule 5
+ if (!core::check_tx_semantic(tx, tvc, hf_version))
+ return false;
+
+ // Rule 6
+ if (!Blockchain::check_tx_outputs(tx, tvc, hf_version) || tvc.m_verifivation_failed)
+ return false;
+
+ // We only want to check RingCT semantics if this is actually a RingCT transaction
+ if (tx.version >= 2)
+ rvv.push_back(&tx.rct_signatures);
+ }
+
+ // Rule 7
+ if (!ver_mixed_rct_semantics(std::move(rvv)))
+ {
+ tvc.m_verifivation_failed = true;
+ tvc.m_invalid_input = true;
+ return false;
+ }
+
+ return true;
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace cryptonote
{
+uint64_t get_transaction_weight_limit(const uint8_t hf_version)
+{
+ // from v8, limit a tx to 50% of the minimum block weight
+ if (hf_version >= HF_VERSION_PER_BYTE_FEE)
+ return get_min_block_weight(hf_version) / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
+ else
+ return get_min_block_weight(hf_version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
+}
+
bool ver_rct_non_semantics_simple_cached
(
transaction& tx,
@@ -164,4 +261,104 @@ bool ver_rct_non_semantics_simple_cached
return true;
}
+bool ver_mixed_rct_semantics(std::vector<const rct::rctSig*> rvv)
+{
+ size_t batch_rv_size = 0; // this acts as an "end" iterator to the last simple batchable sig ptr
+ for (size_t i = 0; i < rvv.size(); ++i)
+ {
+ const rct::rctSig& rv = *rvv[i];
+
+ bool is_batchable_rv = false;
+
+ switch (rv.type)
+ {
+ case rct::RCTTypeNull:
+ // coinbase should not come here, so we reject for all other types
+ MERROR("Unexpected Null rctSig type");
+ return false;
+ break;
+ case rct::RCTTypeSimple:
+ if (!rct::verRctSemanticsSimple(rv))
+ {
+ MERROR("rct signature semantics check failed: type simple");
+ return false;
+ }
+ break;
+ case rct::RCTTypeFull:
+ if (!rct::verRct(rv, /*semantics=*/true))
+ {
+ MERROR("rct signature semantics check failed: type full");
+ return false;
+ }
+ break;
+ case rct::RCTTypeBulletproof:
+ case rct::RCTTypeBulletproof2:
+ case rct::RCTTypeCLSAG:
+ if (!is_canonical_bulletproof_layout(rv.p.bulletproofs))
+ {
+ MERROR("Bulletproof does not have canonical form");
+ return false;
+ }
+ is_batchable_rv = true;
+ break;
+ case rct::RCTTypeBulletproofPlus:
+ if (!is_canonical_bulletproof_plus_layout(rv.p.bulletproofs_plus))
+ {
+ MERROR("Bulletproof_plus does not have canonical form");
+ return false;
+ }
+ is_batchable_rv = true;
+ break;
+ default:
+ MERROR("Unknown rct type: " << rv.type);
+ return false;
+ break;
+ }
+
+ // Save this ring sig for later, as we will attempt simple RCT semantics batch verification
+ if (is_batchable_rv)
+ rvv[batch_rv_size++] = rvv[i];
+ }
+
+ if (batch_rv_size) // if any simple, batchable ring sigs...
+ {
+ rvv.resize(batch_rv_size);
+ if (!rct::verRctSemanticsSimple(rvv))
+ {
+ MERROR("rct signature semantics check failed: simple-style batch verification failed");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ver_non_input_consensus(const transaction& tx, tx_verification_context& tvc,
+ std::uint8_t hf_version)
+{
+ return ver_non_input_consensus_templated(&tx, &tx + 1, tvc, hf_version);
+}
+
+bool ver_non_input_consensus(const pool_supplement& ps, tx_verification_context& tvc,
+ const std::uint8_t hf_version)
+{
+ // We already verified the pool supplement for this hard fork version! Yippee!
+ if (ps.nic_verified_hf_version == hf_version)
+ return true;
+
+ const auto it_transform = [] (const decltype(ps.txs_by_txid)::value_type& in)
+ -> const transaction& { return in.second.first; };
+ const auto tx_begin = boost::make_transform_iterator(ps.txs_by_txid.cbegin(), it_transform);
+ const auto tx_end = boost::make_transform_iterator(ps.txs_by_txid.cend(), it_transform);
+
+ // Perform the checks...
+ const bool verified = ver_non_input_consensus_templated(tx_begin, tx_end, tvc, hf_version);
+
+ // Cache the hard fork version on success
+ if (verified)
+ ps.nic_verified_hf_version = hf_version;
+
+ return verified;
+}
+
} // namespace cryptonote