diff options
| author | cenobite pinhead <pinhead@pinhead> | 2026-08-15 22:41:55 +0100 |
|---|---|---|
| committer | cenobite pinhead <pinhead@pinhead> | 2026-08-15 22:41:55 +0100 |
| commit | 274b4caccd4a13ec83897be58cfc2310a83c7af5 (patch) | |
| tree | 60e2164921579130b1ec34bfb17036aa99b01e8b /tests | |
| parent | 7c17e803151ef3d568a28e7a3c4533ec6c549cf4 (diff) | |
| download | monzero-core-274b4caccd4a13ec83897be58cfc2310a83c7af5.tar.gz monzero-core-274b4caccd4a13ec83897be58cfc2310a83c7af5.tar.xz monzero-core-274b4caccd4a13ec83897be58cfc2310a83c7af5.zip | |
verify confidential per-asset conservation
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/unit_tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | tests/unit_tests/asset_confidential.cpp | 96 |
2 files changed, 97 insertions, 0 deletions
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 16e498ae9..b3328fe74 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -30,6 +30,7 @@ set(unit_tests_sources account.cpp apply_permutation.cpp address_from_url.cpp + asset_confidential.cpp asset_types.cpp base58.cpp blockchain_db.cpp diff --git a/tests/unit_tests/asset_confidential.cpp b/tests/unit_tests/asset_confidential.cpp new file mode 100644 index 000000000..2b3980758 --- /dev/null +++ b/tests/unit_tests/asset_confidential.cpp @@ -0,0 +1,96 @@ +#include "gtest/gtest.h" + +#include "cryptonote_basic/asset_confidential.h" +#include "ringct/bulletproofs_plus.h" +#include "ringct/rctOps.h" + +namespace +{ + crypto::hash asset_id(unsigned char value) + { + crypto::hash id{}; + id.data[0] = value; + return id; + } + + cryptonote::assets::confidential_asset_balance make_balance( + const crypto::hash& id, uint64_t input_amount, + const std::vector<uint64_t>& outputs, const std::vector<uint64_t>& burns) + { + rct::keyV masks = rct::skvGen(outputs.size() + burns.size()); + rct::key input_mask = rct::zero(); + for (const rct::key& mask : masks) + sc_add(input_mask.bytes, input_mask.bytes, mask.bytes); + cryptonote::assets::confidential_asset_balance balance; + balance.asset_id = id; + balance.pseudo_inputs.push_back({id, rct::commit(input_amount, input_mask)}); + size_t index = 0; + for (const uint64_t amount : outputs) + balance.outputs.push_back(rct::commit(amount, masks[index++])); + for (const uint64_t amount : burns) + balance.burns.push_back(rct::commit(amount, masks[index++])); + std::vector<uint64_t> amounts = outputs; + amounts.insert(amounts.end(), burns.begin(), burns.end()); + balance.range_proofs.push_back(rct::bulletproof_plus_PROVE(amounts, masks)); + return balance; + } +} + +TEST(asset_confidential, verifies_private_transfer_and_explicit_burn) +{ + const crypto::hash id = asset_id(1); + const auto balance = make_balance(id, 10, {7}, {3}); + std::string error; + ASSERT_TRUE(cryptonote::assets::verify_confidential_asset_balance(balance, &error)) << error; + ASSERT_TRUE(cryptonote::assets::verify_confidential_asset_transaction({balance}, {id}, boost::none, &error)) << error; +} + +TEST(asset_confidential, rejects_inflation_and_commitment_substitution) +{ + const crypto::hash id = asset_id(2); + std::string error; + auto inflated = make_balance(id, 10, {11}, {}); + EXPECT_FALSE(cryptonote::assets::verify_confidential_asset_balance(inflated, &error)); + auto substituted = make_balance(id, 10, {10}, {}); + substituted.outputs.front() = rct::commit(10, rct::skGen()); + EXPECT_FALSE(cryptonote::assets::verify_confidential_asset_balance(substituted, &error)); + auto malformed = make_balance(id, 10, {10}, {}); + malformed.range_proofs.front().A.bytes[0] ^= 1; + EXPECT_FALSE(cryptonote::assets::verify_confidential_asset_balance(malformed, &error)); +} + +TEST(asset_confidential, rejects_cross_asset_and_duplicate_balance_domains) +{ + const crypto::hash first = asset_id(3); + const crypto::hash second = asset_id(4); + std::string error; + auto crossed = make_balance(first, 9, {9}, {}); + crossed.pseudo_inputs.front().source_asset_id = second; + EXPECT_FALSE(cryptonote::assets::verify_confidential_asset_balance(crossed, &error)); + const auto valid = make_balance(first, 9, {9}, {}); + EXPECT_FALSE(cryptonote::assets::verify_confidential_asset_transaction({valid, valid}, {first}, boost::none, &error)); + EXPECT_FALSE(cryptonote::assets::verify_confidential_asset_transaction({valid}, {second}, boost::none, &error)); +} + +TEST(asset_confidential, validates_fixed_supply_issuance_commitment) +{ + crypto::public_key issuer{}; + crypto::secret_key secret{}; + crypto::generate_keys(issuer, secret); + cryptonote::assets::issuance_descriptor descriptor; + descriptor.network = cryptonote::TESTNET; + descriptor.issuer_key = issuer; + descriptor.atomic_supply = 10; + descriptor.issuance_nonce.data[0] = 9; + crypto::hash id{}; + ASSERT_TRUE(cryptonote::assets::derive_asset_id(descriptor, id)); + cryptonote::assets::confidential_asset_balance balance; + balance.asset_id = id; + balance.pseudo_inputs.push_back({id, rct::commit(10, rct::zero())}); + balance.outputs.push_back(rct::commit(10, rct::zero())); + balance.range_proofs.push_back(rct::bulletproof_plus_PROVE(10, rct::zero())); + std::string error; + ASSERT_TRUE(cryptonote::assets::verify_confidential_asset_transaction({balance}, {}, descriptor, &error)) << error; + balance.pseudo_inputs.front().commitment = rct::commit(11, rct::zero()); + EXPECT_FALSE(cryptonote::assets::verify_confidential_asset_transaction({balance}, {}, descriptor, &error)); +} |
