diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/blockchain_db/asset_db.cpp | 25 | ||||
| -rw-r--r-- | src/blockchain_db/asset_db.h | 11 | ||||
| -rw-r--r-- | src/blockchain_db/blockchain_db.cpp | 5 | ||||
| -rw-r--r-- | src/blockchain_db/blockchain_db.h | 14 | ||||
| -rw-r--r-- | src/blockchain_db/lmdb/db_lmdb.cpp | 156 | ||||
| -rw-r--r-- | src/blockchain_db/lmdb/db_lmdb.h | 11 | ||||
| -rw-r--r-- | src/blockchain_db/testdb.h | 6 |
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 {} }; } |
