aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorcenobite pinhead <pinhead@pinhead>2026-08-15 23:14:28 +0100
committercenobite pinhead <pinhead@pinhead>2026-08-15 23:14:28 +0100
commit01cd9b0c3b7719ecd6ce45a32192dd1152ebd9ad (patch)
treee43a188ab6b1690b74b785364cb6ea11897c65db /src
parent2c882e39b3f23e2511f5ba90a31d0a168ff1d8ae (diff)
downloadmonzero-core-01cd9b0c3b7719ecd6ce45a32192dd1152ebd9ad.tar.gz
monzero-core-01cd9b0c3b7719ecd6ce45a32192dd1152ebd9ad.tar.xz
monzero-core-01cd9b0c3b7719ecd6ce45a32192dd1152ebd9ad.zip
persist asset outputs and spent key images
Diffstat (limited to 'src')
-rw-r--r--src/blockchain_db/asset_db.cpp25
-rw-r--r--src/blockchain_db/asset_db.h11
-rw-r--r--src/blockchain_db/blockchain_db.cpp5
-rw-r--r--src/blockchain_db/blockchain_db.h14
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp156
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h11
-rw-r--r--src/blockchain_db/testdb.h6
7 files changed, 226 insertions, 2 deletions
diff --git a/src/blockchain_db/asset_db.cpp b/src/blockchain_db/asset_db.cpp
index d80b61028..bb2389d24 100644
--- a/src/blockchain_db/asset_db.cpp
+++ b/src/blockchain_db/asset_db.cpp
@@ -3,6 +3,8 @@
#include <algorithm>
#include <cstring>
+#include "ringct/rctOps.h"
+
namespace cryptonote
{
namespace assets
@@ -92,5 +94,28 @@ bool apply_block_extensions_to_db(BlockchainDB& db,
asset_ids = std::move(candidate_ids);
return true;
}
+
+bool verify_asset_ownership_against_db(const BlockchainDB& db,
+ const asset_ownership_proof& proof, network_type expected_network,
+ const crypto::hash& carrier_prefix_hash, std::string* error)
+{
+ if (db.has_asset_key_image(proof.key_image))
+ return fail(error, "asset key image is already spent");
+
+ for (const asset_ring_member& member : proof.ring)
+ {
+ asset_output_data_t stored{};
+ if (!db.get_asset_output(member.output_id, stored))
+ return fail(error, "asset ownership ring references an unknown output");
+ if (stored.asset_id != member.asset_id || member.asset_id != proof.asset_id)
+ return fail(error, "asset ownership ring member has the wrong asset id");
+ if (!rct::equalKeys(stored.destination, member.public_output.dest))
+ return fail(error, "asset ownership ring destination does not match consensus state");
+ if (!rct::equalKeys(stored.commitment, member.public_output.mask))
+ return fail(error, "asset ownership ring commitment does not match consensus state");
+ }
+ return verify_asset_ownership_proof(
+ proof, expected_network, carrier_prefix_hash, error);
+}
}
}
diff --git a/src/blockchain_db/asset_db.h b/src/blockchain_db/asset_db.h
index 0df5721e0..b5a763b75 100644
--- a/src/blockchain_db/asset_db.h
+++ b/src/blockchain_db/asset_db.h
@@ -4,6 +4,7 @@
#include <vector>
#include "blockchain_db.h"
+#include "cryptonote_basic/asset_confidential.h"
#include "cryptonote_basic/asset_types.h"
namespace cryptonote
@@ -27,5 +28,15 @@ namespace assets
uint64_t height,
std::vector<crypto::hash>& asset_ids,
std::string* error = nullptr);
+
+ // Resolve every claimed ring member against consensus storage and reject
+ // key images already spent by an earlier accepted asset transaction before
+ // performing the cryptographic ownership check.
+ bool verify_asset_ownership_against_db(
+ const BlockchainDB& db,
+ const asset_ownership_proof& proof,
+ network_type expected_network,
+ const crypto::hash& carrier_prefix_hash,
+ std::string* error = nullptr);
}
}
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index b1c4a4136..38203ddb2 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -318,7 +318,10 @@ void BlockchainDB::pop_block(block& blk, std::vector<transaction>& txs)
// Asset records created by the detached block are consensus state and must
// disappear in the same write transaction as the native block and tx data.
- remove_asset_records_from_height(height() - 1);
+ const uint64_t detached_height = height() - 1;
+ remove_asset_records_from_height(detached_height);
+ remove_asset_outputs_from_height(detached_height);
+ remove_asset_key_images_from_height(detached_height);
remove_block();
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index c176a8ef5..a52f2bdad 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -128,6 +128,14 @@ struct output_data_t
uint64_t height; //!< the height of the block which created the output
rct::key commitment; //!< the output's amount commitment (for spend verification)
};
+
+struct asset_output_data_t
+{
+ crypto::hash asset_id;
+ rct::key destination;
+ rct::key commitment;
+ uint64_t height;
+};
#pragma pack(pop)
#pragma pack(push, 1)
@@ -1791,6 +1799,12 @@ public:
virtual bool get_asset_record(const crypto::hash &asset_id, uint64_t &height, cryptonote::blobdata &payload) const = 0;
virtual void remove_asset_records_from_height(uint64_t height) = 0;
virtual bool for_all_asset_records(std::function<bool(const crypto::hash&, uint64_t, const cryptonote::blobdata_ref&)>) const = 0;
+ virtual void add_asset_output(const crypto::hash &output_id, const asset_output_data_t &output) = 0;
+ virtual bool get_asset_output(const crypto::hash &output_id, asset_output_data_t &output) const = 0;
+ virtual void add_asset_key_image(const crypto::key_image &key_image, uint64_t height) = 0;
+ virtual bool has_asset_key_image(const crypto::key_image &key_image) const = 0;
+ virtual void remove_asset_outputs_from_height(uint64_t height) = 0;
+ virtual void remove_asset_key_images_from_height(uint64_t height) = 0;
//
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 0c3a4f470..4ba91b72b 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -58,7 +58,7 @@ using epee::string_tools::pod_to_hex;
using namespace crypto;
// Increase when the DB structure changes
-#define VERSION 6
+#define VERSION 7
namespace
{
@@ -239,6 +239,10 @@ const char* const LMDB_ALT_BLOCKS = "alt_blocks";
const char* const LMDB_ASSET_RECORDS = "asset_records";
const char* const LMDB_ASSET_HEIGHTS = "asset_heights";
+const char* const LMDB_ASSET_OUTPUTS = "asset_outputs";
+const char* const LMDB_ASSET_OUTPUT_HEIGHTS = "asset_output_heights";
+const char* const LMDB_ASSET_KEY_IMAGES = "asset_key_images";
+const char* const LMDB_ASSET_KEY_IMAGE_HEIGHTS = "asset_key_image_heights";
const char* const LMDB_HF_STARTING_HEIGHTS = "hf_starting_heights";
const char* const LMDB_HF_VERSIONS = "hf_versions";
@@ -1511,6 +1515,10 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
lmdb_db_open(txn, LMDB_ASSET_RECORDS, MDB_CREATE, m_asset_records, "Failed to open db handle for m_asset_records");
lmdb_db_open(txn, LMDB_ASSET_HEIGHTS, MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_asset_heights, "Failed to open db handle for m_asset_heights");
+ lmdb_db_open(txn, LMDB_ASSET_OUTPUTS, MDB_CREATE, m_asset_outputs, "Failed to open db handle for m_asset_outputs");
+ lmdb_db_open(txn, LMDB_ASSET_OUTPUT_HEIGHTS, MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_asset_output_heights, "Failed to open db handle for m_asset_output_heights");
+ lmdb_db_open(txn, LMDB_ASSET_KEY_IMAGES, MDB_CREATE, m_asset_key_images, "Failed to open db handle for m_asset_key_images");
+ lmdb_db_open(txn, LMDB_ASSET_KEY_IMAGE_HEIGHTS, MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_asset_key_image_heights, "Failed to open db handle for m_asset_key_image_heights");
// this subdb is dropped on sight, so it may not be present when we open the DB.
// Since we use MDB_CREATE, we'll get an exception if we open read-only and it does not exist.
@@ -1538,6 +1546,10 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
mdb_set_compare(txn, m_alt_blocks, compare_hash32);
mdb_set_compare(txn, m_asset_records, compare_hash32);
mdb_set_dupsort(txn, m_asset_heights, compare_hash32);
+ mdb_set_compare(txn, m_asset_outputs, compare_hash32);
+ mdb_set_dupsort(txn, m_asset_output_heights, compare_hash32);
+ mdb_set_compare(txn, m_asset_key_images, compare_hash32);
+ mdb_set_dupsort(txn, m_asset_key_image_heights, compare_hash32);
mdb_set_compare(txn, m_properties, compare_string);
if (!(mdb_flags & MDB_RDONLY))
@@ -1714,6 +1726,14 @@ void BlockchainLMDB::reset()
throw0(DB_ERROR(lmdb_error("Failed to drop m_asset_records: ", result).c_str()));
if (auto result = mdb_drop(txn, m_asset_heights, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_asset_heights: ", result).c_str()));
+ if (auto result = mdb_drop(txn, m_asset_outputs, 0))
+ throw0(DB_ERROR(lmdb_error("Failed to drop m_asset_outputs: ", result).c_str()));
+ if (auto result = mdb_drop(txn, m_asset_output_heights, 0))
+ throw0(DB_ERROR(lmdb_error("Failed to drop m_asset_output_heights: ", result).c_str()));
+ if (auto result = mdb_drop(txn, m_asset_key_images, 0))
+ throw0(DB_ERROR(lmdb_error("Failed to drop m_asset_key_images: ", result).c_str()));
+ if (auto result = mdb_drop(txn, m_asset_key_image_heights, 0))
+ throw0(DB_ERROR(lmdb_error("Failed to drop m_asset_key_image_heights: ", result).c_str()));
// init with current version
MDB_val_str(k, "version");
@@ -2550,6 +2570,125 @@ bool BlockchainLMDB::for_all_asset_records(std::function<bool(const crypto::hash
return ret;
}
+void BlockchainLMDB::add_asset_output(const crypto::hash &output_id, const asset_output_data_t &output)
+{
+ check_open();
+ TXN_BLOCK_PREFIX(0);
+ MDB_val key = {sizeof(output_id), const_cast<crypto::hash*>(&output_id)};
+ MDB_val value = {sizeof(output), const_cast<asset_output_data_t*>(&output)};
+ int result = mdb_put(*txn_ptr, m_asset_outputs, &key, &value, MDB_NOOVERWRITE);
+ if (result == MDB_KEYEXIST)
+ throw1(DB_ERROR("Attempting to add an asset output that already exists"));
+ if (result)
+ throw1(DB_ERROR(lmdb_error("Error adding asset output: ", result).c_str()));
+ MDB_val_copy<uint64_t> height_key(output.height);
+ MDB_val id_value = {sizeof(output_id), const_cast<crypto::hash*>(&output_id)};
+ if ((result = mdb_put(*txn_ptr, m_asset_output_heights, &height_key, &id_value, MDB_NODUPDATA)))
+ throw1(DB_ERROR(lmdb_error("Error indexing asset output height: ", result).c_str()));
+ TXN_BLOCK_POSTFIX_SUCCESS();
+}
+
+bool BlockchainLMDB::get_asset_output(const crypto::hash &output_id, asset_output_data_t &output) const
+{
+ check_open();
+ TXN_PREFIX_RDONLY();
+ MDB_val key = {sizeof(output_id), const_cast<crypto::hash*>(&output_id)}, value;
+ const int result = mdb_get(m_txn, m_asset_outputs, &key, &value);
+ if (result == MDB_NOTFOUND)
+ return false;
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error retrieving asset output: ", result).c_str()));
+ if (value.mv_size != sizeof(output))
+ throw0(DB_ERROR("Asset output record has an invalid size"));
+ std::memcpy(&output, value.mv_data, sizeof(output));
+ return true;
+}
+
+void BlockchainLMDB::add_asset_key_image(const crypto::key_image &key_image, uint64_t height)
+{
+ check_open();
+ TXN_BLOCK_PREFIX(0);
+ MDB_val key = {sizeof(key_image), const_cast<crypto::key_image*>(&key_image)};
+ MDB_val_copy<uint64_t> value(height);
+ int result = mdb_put(*txn_ptr, m_asset_key_images, &key, &value, MDB_NOOVERWRITE);
+ if (result == MDB_KEYEXIST)
+ throw1(KEY_IMAGE_EXISTS("Attempting to spend an asset key image that already exists"));
+ if (result)
+ throw1(DB_ERROR(lmdb_error("Error adding asset key image: ", result).c_str()));
+ MDB_val_copy<uint64_t> height_key(height);
+ MDB_val image_value = {sizeof(key_image), const_cast<crypto::key_image*>(&key_image)};
+ if ((result = mdb_put(*txn_ptr, m_asset_key_image_heights, &height_key, &image_value, MDB_NODUPDATA)))
+ throw1(DB_ERROR(lmdb_error("Error indexing asset key image height: ", result).c_str()));
+ TXN_BLOCK_POSTFIX_SUCCESS();
+}
+
+bool BlockchainLMDB::has_asset_key_image(const crypto::key_image &key_image) const
+{
+ check_open();
+ TXN_PREFIX_RDONLY();
+ MDB_val key = {sizeof(key_image), const_cast<crypto::key_image*>(&key_image)}, value;
+ const int result = mdb_get(m_txn, m_asset_key_images, &key, &value);
+ if (result == MDB_NOTFOUND)
+ return false;
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error retrieving asset key image: ", result).c_str()));
+ return true;
+}
+
+void BlockchainLMDB::remove_asset_outputs_from_height(uint64_t height)
+{
+ check_open();
+ TXN_BLOCK_PREFIX(0);
+ MDB_cursor *cursor = nullptr;
+ int result = mdb_cursor_open(*txn_ptr, m_asset_output_heights, &cursor);
+ if (result)
+ throw1(DB_ERROR(lmdb_error("Error opening asset output height cursor: ", result).c_str()));
+ MDB_val_copy<uint64_t> key(height);
+ MDB_val value;
+ result = mdb_cursor_get(cursor, &key, &value, MDB_SET_RANGE);
+ while (result == MDB_SUCCESS)
+ {
+ MDB_val output_key = {value.mv_size, value.mv_data};
+ const int deleted = mdb_del(*txn_ptr, m_asset_outputs, &output_key, nullptr);
+ if (deleted != MDB_SUCCESS && deleted != MDB_NOTFOUND)
+ throw1(DB_ERROR(lmdb_error("Error removing asset output: ", deleted).c_str()));
+ if ((result = mdb_cursor_del(cursor, 0)) != MDB_SUCCESS)
+ throw1(DB_ERROR(lmdb_error("Error removing asset output height: ", result).c_str()));
+ result = mdb_cursor_get(cursor, &key, &value, MDB_NEXT);
+ }
+ mdb_cursor_close(cursor);
+ if (result != MDB_NOTFOUND)
+ throw1(DB_ERROR(lmdb_error("Error iterating asset output heights: ", result).c_str()));
+ TXN_BLOCK_POSTFIX_SUCCESS();
+}
+
+void BlockchainLMDB::remove_asset_key_images_from_height(uint64_t height)
+{
+ check_open();
+ TXN_BLOCK_PREFIX(0);
+ MDB_cursor *cursor = nullptr;
+ int result = mdb_cursor_open(*txn_ptr, m_asset_key_image_heights, &cursor);
+ if (result)
+ throw1(DB_ERROR(lmdb_error("Error opening asset key-image height cursor: ", result).c_str()));
+ MDB_val_copy<uint64_t> key(height);
+ MDB_val value;
+ result = mdb_cursor_get(cursor, &key, &value, MDB_SET_RANGE);
+ while (result == MDB_SUCCESS)
+ {
+ MDB_val image_key = {value.mv_size, value.mv_data};
+ const int deleted = mdb_del(*txn_ptr, m_asset_key_images, &image_key, nullptr);
+ if (deleted != MDB_SUCCESS && deleted != MDB_NOTFOUND)
+ throw1(DB_ERROR(lmdb_error("Error removing asset key image: ", deleted).c_str()));
+ if ((result = mdb_cursor_del(cursor, 0)) != MDB_SUCCESS)
+ throw1(DB_ERROR(lmdb_error("Error removing asset key-image height: ", result).c_str()));
+ result = mdb_cursor_get(cursor, &key, &value, MDB_NEXT);
+ }
+ mdb_cursor_close(cursor);
+ if (result != MDB_NOTFOUND)
+ throw1(DB_ERROR(lmdb_error("Error iterating asset key-image heights: ", result).c_str()));
+ TXN_BLOCK_POSTFIX_SUCCESS();
+}
+
bool BlockchainLMDB::block_exists(const crypto::hash& h, uint64_t *height) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -5831,6 +5970,19 @@ void BlockchainLMDB::migrate_5_6()
txn.commit();
}
+void BlockchainLMDB::migrate_6_7()
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ mdb_txn_safe txn(false);
+ if (const int result = mdb_txn_begin(m_env, nullptr, 0, txn))
+ throw0(DB_ERROR(lmdb_error("Failed to create transaction for DB v7 migration: ", result).c_str()));
+ MDB_val_str(key, "version");
+ MDB_val_copy<uint32_t> value(7);
+ if (const int result = mdb_put(txn, m_properties, &key, &value, 0))
+ throw0(DB_ERROR(lmdb_error("Failed to update DB version to 7: ", result).c_str()));
+ txn.commit();
+}
+
void BlockchainLMDB::migrate(const uint32_t oldversion)
{
if (oldversion < 1)
@@ -5845,6 +5997,8 @@ void BlockchainLMDB::migrate(const uint32_t oldversion)
migrate_4_5();
if (oldversion < 6)
migrate_5_6();
+ if (oldversion < 7)
+ migrate_6_7();
}
} // namespace cryptonote
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index d116bcec5..c7851c117 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -315,6 +315,12 @@ public:
virtual bool get_asset_record(const crypto::hash &asset_id, uint64_t &height, cryptonote::blobdata &payload) const;
virtual void remove_asset_records_from_height(uint64_t height);
virtual bool for_all_asset_records(std::function<bool(const crypto::hash&, uint64_t, const cryptonote::blobdata_ref&)>) const;
+ virtual void add_asset_output(const crypto::hash &output_id, const asset_output_data_t &output);
+ virtual bool get_asset_output(const crypto::hash &output_id, asset_output_data_t &output) const;
+ virtual void add_asset_key_image(const crypto::key_image &key_image, uint64_t height);
+ virtual bool has_asset_key_image(const crypto::key_image &key_image) const;
+ virtual void remove_asset_outputs_from_height(uint64_t height);
+ virtual void remove_asset_key_images_from_height(uint64_t height);
virtual uint64_t add_block( const std::pair<block, blobdata>& blk
, size_t block_weight
@@ -449,6 +455,7 @@ private:
// migrate from DB version 5 to 6
void migrate_5_6();
+ void migrate_6_7();
void cleanup_batch();
@@ -479,6 +486,10 @@ private:
MDB_dbi m_asset_records;
MDB_dbi m_asset_heights;
+ MDB_dbi m_asset_outputs;
+ MDB_dbi m_asset_output_heights;
+ MDB_dbi m_asset_key_images;
+ MDB_dbi m_asset_key_image_heights;
MDB_dbi m_hf_starting_heights;
MDB_dbi m_hf_versions;
diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h
index fb97ccee4..c14348245 100644
--- a/src/blockchain_db/testdb.h
+++ b/src/blockchain_db/testdb.h
@@ -170,6 +170,12 @@ public:
virtual bool get_asset_record(const crypto::hash&, uint64_t&, cryptonote::blobdata&) const override { return false; }
virtual void remove_asset_records_from_height(uint64_t) override {}
virtual bool for_all_asset_records(std::function<bool(const crypto::hash&, uint64_t, const cryptonote::blobdata_ref&)>) const override { return true; }
+ virtual void add_asset_output(const crypto::hash&, const asset_output_data_t&) override {}
+ virtual bool get_asset_output(const crypto::hash&, asset_output_data_t&) const override { return false; }
+ virtual void add_asset_key_image(const crypto::key_image&, uint64_t) override {}
+ virtual bool has_asset_key_image(const crypto::key_image&) const override { return false; }
+ virtual void remove_asset_outputs_from_height(uint64_t) override {}
+ virtual void remove_asset_key_images_from_height(uint64_t) override {}
};
}