added unit tests for protocol_tx and check_output_types; bumped version to v0.9.6-rc1

This commit is contained in:
Some Random Crypto Guy
2025-03-19 09:30:02 +00:00
parent 1f49c178ee
commit 28262a30fe
7 changed files with 637 additions and 700 deletions
+5 -5
View File
@@ -1,4 +1,4 @@
# Salvium Zero v0.9.5 # Salvium Zero v0.9.6
Copyright (c) 2023-2024, Salvium Copyright (c) 2023-2024, Salvium
Portions Copyright (c) 2014-2023, The Monero Project Portions Copyright (c) 2014-2023, The Monero Project
@@ -172,7 +172,7 @@ invokes cmake commands as needed.
```bash ```bash
cd salvium cd salvium
git checkout v0.9.5 git checkout v0.9.6
make make
``` ```
@@ -251,7 +251,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
```bash ```bash
git clone https://github.com/salvium/salvium git clone https://github.com/salvium/salvium
cd salvium cd salvium
git checkout v0.9.5 git checkout v0.9.6
``` ```
* Build: * Build:
@@ -370,10 +370,10 @@ application.
cd salvium cd salvium
``` ```
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v0.9.5'. If you don't care about the version and just want binaries from master, skip this step: * If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v0.9.6'. If you don't care about the version and just want binaries from master, skip this step:
```bash ```bash
git checkout v0.9.5 git checkout v0.9.6
``` ```
* If you are on a 64-bit system, run: * If you are on a 64-bit system, run:
+6 -27
View File
@@ -1482,16 +1482,12 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
// if nothing is created by this TX - check no money is included // if nothing is created by this TX - check no money is included
CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 1, false, "coinbase protocol transaction in the block has no inputs"); CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 1, false, "coinbase protocol transaction in the block has no inputs");
size_t vout_size = b.protocol_tx.vout.size();
if (vout_size == 0) {
LOG_PRINT_L2("coinbase protocol transaction in the block has no outputs");
return true;
}
// Can we have matured STAKE transactions yet? // Can we have matured STAKE transactions yet?
uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD; uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD;
if (height <= stake_lock_period) { if (height <= stake_lock_period) {
return false; CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == 0, false, "protocol transaction in the block has outputs");
return true;
} }
// Get the staking data for the block that matured this time // Get the staking data for the block that matured this time
@@ -1543,28 +1539,9 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
// Check we have the correct number of entries // Check we have the correct number of entries
CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size(), false, "Invalid number of outputs in protocol_tx - aborting"); CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size(), false, "Invalid number of outputs in protocol_tx - aborting");
// go through each vout and validate
//std::set<crypto::public_key> used_keys;
// Merge the yield and audit payouts into an iterable vector // Merge the yield and audit payouts into an iterable vector
std::vector<std::pair<yield_tx_info, uint64_t>> payouts{yield_payouts}; std::vector<std::pair<yield_tx_info, uint64_t>> payouts{yield_payouts};
payouts.insert(payouts.end(), audit_payouts.begin(), audit_payouts.end()); payouts.insert(payouts.end(), audit_payouts.begin(), audit_payouts.end());
/*
if (hf_version >= HF_VERSION_AUDIT2) {
std::sort(payouts.begin(), payouts.end(), [](const auto& lhs, const auto& rhs) {
// If block heights are different (only possible with mixed AUDIT+STAKE) sort by them first
if (lhs.first.block_height < rhs.first.block_height) return true;
if (lhs.first.block_height > rhs.first.block_height) return false;
// If output keys are different, sort by them second
if (lhs.first.return_address < rhs.first.return_address) return true;
if (lhs.first.return_address > rhs.first.return_address) return false;
// If block heights _and_ output keys are same, sort by amount third
return lhs.second < rhs.second;
});
}
*/
size_t output_idx = 0; size_t output_idx = 0;
for (auto it = payouts.begin(); it != payouts.end(); it++, output_idx++) { for (auto it = payouts.begin(); it != payouts.end(); it++, output_idx++) {
@@ -1575,8 +1552,9 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
CHECK_AND_ASSERT_MES(out_key == it->first.return_address, false, "Incorrect output key detected in protocol_tx"); CHECK_AND_ASSERT_MES(out_key == it->first.return_address, false, "Incorrect output key detected in protocol_tx");
// Verify the output amount // Verify the output amount
CHECK_AND_ASSERT_MES(b.protocol_tx.vout[output_idx].amount == it->second, false, "Incorrect output amount detected in protocol_tx"); uint64_t expected_amount = it->second;
CHECK_AND_ASSERT_MES(b.protocol_tx.vout[output_idx].amount == expected_amount, false, "Incorrect output amount detected in protocol_tx. expected_amount: " << expected_amount);
// Verify the output asset type // Verify the output asset type
std::string out_asset_type; std::string out_asset_type;
cryptonote::get_output_asset_type(b.protocol_tx.vout[output_idx], out_asset_type); cryptonote::get_output_asset_type(b.protocol_tx.vout[output_idx], out_asset_type);
@@ -4526,6 +4504,7 @@ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vecto
} }
yield_block_info ybi = m_yield_block_info_cache[idx]; yield_block_info ybi = m_yield_block_info_cache[idx];
if (ybi.slippage_total_this_block == 0) continue; if (ybi.slippage_total_this_block == 0) continue;
if (ybi.locked_coins_tally == 0) continue;
boost::multiprecision::int128_t slippage_128 = ybi.slippage_total_this_block; boost::multiprecision::int128_t slippage_128 = ybi.slippage_total_this_block;
+2 -2
View File
@@ -1,7 +1,7 @@
#define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@" #define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@"
#define DEF_SALVIUM_VERSION "0.9.5a" #define DEF_SALVIUM_VERSION "0.9.6-rc1"
#define DEF_MONERO_VERSION_TAG "release" #define DEF_MONERO_VERSION_TAG "release"
#define DEF_MONERO_VERSION "0.18.3.3" #define DEF_MONERO_VERSION "0.18.3.4"
#define DEF_MONERO_RELEASE_NAME "Zero" #define DEF_MONERO_RELEASE_NAME "Zero"
#define DEF_MONERO_VERSION_FULL DEF_SALVIUM_VERSION "-" DEF_SALVIUM_VERSION_TAG ", based on Monero " DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG #define DEF_MONERO_VERSION_FULL DEF_SALVIUM_VERSION "-" DEF_SALVIUM_VERSION_TAG ", based on Monero " DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
#define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@ #define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@
+2 -1
View File
@@ -53,7 +53,8 @@ set(unit_tests_sources
expect.cpp expect.cpp
fee.cpp fee.cpp
json_serialization.cpp json_serialization.cpp
get_tx_asset_types.cpp check_output_types.cpp
protocol_tx.cpp
get_xtype_from_string.cpp get_xtype_from_string.cpp
hashchain.cpp hashchain.cpp
hmac_keccak.cpp hmac_keccak.cpp
+130
View File
@@ -0,0 +1,130 @@
// Copyright (c) 2025, Salvium (author: akil)
// Portions copyright (c) 2019-2021, Haven Protocol
// Portions copyright (c) 2016-2019, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// 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 "gtest/gtest.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/cryptonote_basic.h"
using namespace cryptonote;
TEST(check_output_types, check_all)
{
transaction tx;
tx.version = 2;
uint8_t hf_version = 1;
// should only allow for single output for audit and stake txs
tx.type = transaction_type::AUDIT;
txout_to_key out; out.asset_type = "SAL";
EXPECT_FALSE(check_output_types(tx, hf_version));
tx.vout.push_back(tx_out {0, out});
tx.vout.push_back(tx_out {0, out});
EXPECT_FALSE(check_output_types(tx, hf_version));
tx.vout.pop_back();
EXPECT_TRUE(check_output_types(tx, hf_version));
tx.vout.clear();
tx.type = transaction_type::STAKE;
EXPECT_FALSE(check_output_types(tx, hf_version));
tx.vout.push_back(tx_out {0, out});
tx.vout.push_back(tx_out {0, out});
EXPECT_FALSE(check_output_types(tx, hf_version));
tx.vout.pop_back();
EXPECT_TRUE(check_output_types(tx, hf_version));
tx.vout.clear();
// should only allow txout_to_key or txout_to_tagged_key output types
txout_to_script tx_out_script;
tx.vout.push_back(tx_out {0, tx_out_script});
EXPECT_FALSE(check_output_types(tx, hf_version));
tx.vout.clear();
txout_to_scripthash tx_out_scripthash;
tx.vout.push_back(tx_out {0, tx_out_scripthash});
EXPECT_FALSE(check_output_types(tx, hf_version));
tx.vout.clear();
// should force all outputs to be of the same type
out.asset_type = "SAL";
txout_to_tagged_key out2; out2.asset_type = "SAL";
tx.vout.push_back(tx_out {0, out});
tx.vout.push_back(tx_out {0, out2});
EXPECT_FALSE(check_output_types(tx, hf_version));
tx.vout.clear();
// should only allow SAL outputs before version 6
out.asset_type = "SAL1";
tx.vout.push_back(tx_out {0, out});
EXPECT_FALSE(check_output_types(tx, hf_version));
tx.vout.clear();
out.asset_type = "SAL";
tx.vout.push_back(tx_out {0, out});
EXPECT_TRUE(check_output_types(tx, hf_version));
tx.vout.clear();
// after version 6, AUDIT txs still should only have SAL outputs
hf_version = 6;
tx.type = transaction_type::AUDIT;
out.asset_type = "SAL1";
tx.vout.push_back(tx_out {0, out});
EXPECT_FALSE(check_output_types(tx, hf_version));
tx.vout.clear();
out.asset_type = "SAL";
tx.vout.push_back(tx_out {0, out});
EXPECT_TRUE(check_output_types(tx, hf_version));
tx.vout.clear();
// after version 6, non-AUDIT txs should only allow SAL1 outputs
tx.type = transaction_type::PROTOCOL;
out.asset_type = "SAL";
tx.vout.push_back(tx_out {0, out});
EXPECT_FALSE(check_output_types(tx, hf_version));
tx.vout.clear();
out.asset_type = "SAL1";
tx.vout.push_back(tx_out {0, out});
EXPECT_TRUE(check_output_types(tx, hf_version));
tx.vout.clear();
tx.type = transaction_type::TRANSFER;
out.asset_type = "SAL";
tx.vout.push_back(tx_out {0, out});
EXPECT_FALSE(check_output_types(tx, hf_version));
tx.vout.clear();
out.asset_type = "SAL1";
tx.vout.push_back(tx_out {0, out});
EXPECT_TRUE(check_output_types(tx, hf_version));
tx.vout.clear();
}
-665
View File
@@ -1,665 +0,0 @@
// Copyright (c) 2019-2021, Haven Protocol
// Portions copyright (c) 2016-2019, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// 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 "gtest/gtest.h"
#include "cryptonote_core/cryptonote_tx_utils.cpp"
#include "cryptonote_basic/cryptonote_basic.h"
#include "vector"
/*
// Regular transfers tests. Same asset type in input and output. Should be successful.
TEST(get_tx_asset_types, successful_on_1_input_type_1_output_type_FULM)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_to_key fulm_key;
tx.vin.push_back(fulm_key);
cryptonote::tx_out out;
cryptonote::tx_out out1;
cryptonote::txout_to_key out_fulm;
out.target = out_fulm;
out1.target = out_fulm;
tx.vout.push_back(out);
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_TRUE(get_tx_asset_types(tx, tx.hash, source, dest, false));
EXPECT_EQ(source, "FULM");
EXPECT_EQ(dest, "FULM");
}
TEST(get_tx_asset_types, successful_on_1_input_type_1_output_type_FUSD)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_to_key offshore_key;
tx.vin.push_back(offshore_key);
cryptonote::tx_out out;
cryptonote::tx_out out1;
cryptonote::txout_offshore out_fusd;
out.target = out_fusd;
out1.target = out_fusd;
tx.vout.push_back(out);
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_TRUE(get_tx_asset_types(tx, tx.hash, source, dest, false));
EXPECT_EQ(source, "FUSD");
EXPECT_EQ(dest, "FUSD");
}
// pass on correct conversions
TEST(get_tx_asset_types, successful_offshore)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_to_key fulm_key;
cryptonote::txout_to_key out_fulm;
cryptonote::txout_offshore out_fusd;
tx.vin.push_back(fulm_key);
tx.vin.push_back(fulm_key);
cryptonote::tx_out out;
out.target = out_fulm;
tx.vout.push_back(out);
cryptonote::tx_out out1;
out1.target = out_fusd;
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_TRUE(get_tx_asset_types(tx, tx.hash, source, dest, false));
EXPECT_EQ(source, "FULM");
EXPECT_EQ(dest, "FUSD");
}
TEST(get_tx_asset_types, successful_onshore)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_onshore fusd_key;
cryptonote::txout_to_key out_fulm;
cryptonote::txout_offshore out_fusd;
tx.vin.push_back(fusd_key);
cryptonote::tx_out out;
out.target = out_fulm;
tx.vout.push_back(out);
cryptonote::tx_out out1;
out1.target = out_fusd;
tx.vout.push_back(out1);
cryptonote::tx_out out2;
out2.target = out_fulm;
tx.vout.push_back(out2);
std::string source;
std::string dest;
EXPECT_TRUE(get_tx_asset_types(tx, tx.hash, source, dest, false));
EXPECT_EQ(source, "FUSD");
EXPECT_EQ(dest, "FULM");
}
// fail on multiple input types - 3 or more
TEST(get_tx_asset_types, fail_on_multiple_input_types_3_or_more)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_offshore offshore_key;
cryptonote::txin_onshore onshore_key;
cryptonote::txin_xasset xasset_key;
cryptonote::txin_to_key xhv_key;
cryptonote::txout_to_key out_xhv;
cryptonote::txout_offshore out_offshore;
cryptonote::txout_xasset out_xasset;
tx.vin.push_back(xhv_key);
tx.vin.push_back(xasset_key);
tx.vin.push_back(offshore_key);
cryptonote::tx_out out;
out.target = out_xhv;
tx.vout.push_back(out);
cryptonote::tx_out out1;
out1.target = out_offshore;
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// fail on multiple input types - 1. XHV, 2. not XUSD
TEST(get_tx_asset_types, fail_on_multiple_input_types_XHV_not_XUSD)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_offshore offshore_key;
cryptonote::txin_onshore onshore_key;
cryptonote::txin_xasset xasset_key;
xasset_key.asset_type = "XBTC";
cryptonote::txin_to_key xhv_key;
cryptonote::txout_to_key out_xhv;
cryptonote::txout_offshore out_offshore;
cryptonote::txout_xasset out_xasset;
tx.vin.push_back(xhv_key);
tx.vin.push_back(xasset_key);
cryptonote::tx_out out;
out.target = out_xhv;
tx.vout.push_back(out);
cryptonote::tx_out out1;
out1.target = out_offshore;
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// fail on multiple input types - 1. XUSD, 2. not XHV
TEST(get_tx_asset_types, fail_on_multiple_input_types_XUSD_not_XHV)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_offshore offshore_key;
cryptonote::txin_onshore onshore_key;
cryptonote::txin_xasset xasset_key;
xasset_key.asset_type = "XBTC";
cryptonote::txin_to_key xhv_key;
cryptonote::txout_to_key out_xhv;
cryptonote::txout_offshore out_offshore;
cryptonote::txout_xasset out_xasset;
tx.vin.push_back(onshore_key);
tx.vin.push_back(xasset_key);
cryptonote::tx_out out;
out.target = out_xhv;
tx.vout.push_back(out);
cryptonote::tx_out out1;
out1.target = out_offshore;
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// fail on multiple input types - ins) 1. XHV 2. XUSD outs) only XHV
TEST(get_tx_asset_types, fail_on_multiple_input_types_only_XHV_out)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_offshore offshore_key;
cryptonote::txin_onshore onshore_key;
cryptonote::txin_xasset xasset_key;
cryptonote::txin_to_key xhv_key;
cryptonote::txout_to_key out_xhv;
cryptonote::txout_offshore out_offshore;
cryptonote::txout_xasset out_xasset;
tx.vin.push_back(xhv_key);
tx.vin.push_back(offshore_key);
cryptonote::tx_out out;
out.target = out_xhv;
tx.vout.push_back(out);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// fail on multiple input types - ins) 1. XHV 2. XUSD outs) only XUSD
TEST(get_tx_asset_types, fail_on_multiple_input_types_only_XUSD_out)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_offshore offshore_key;
cryptonote::txin_onshore onshore_key;
cryptonote::txin_xasset xasset_key;
cryptonote::txin_to_key xhv_key;
cryptonote::txout_to_key out_xhv;
cryptonote::txout_offshore out_offshore;
cryptonote::txout_xasset out_xasset;
tx.vin.push_back(xhv_key);
tx.vin.push_back(offshore_key);
cryptonote::tx_out out1;
out1.target = out_offshore;
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// fail on multiple input types - ins) 1. XHV 2. XUSD outs) 1. not XHV 2. not XUSD
TEST(get_tx_asset_types, fail_on_multiple_input_types_out_not_XHV_not_XUSD)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_offshore offshore_key;
cryptonote::txin_onshore onshore_key;
cryptonote::txin_xasset xasset_key;
cryptonote::txin_to_key xhv_key;
cryptonote::txout_to_key out_xhv;
cryptonote::txout_offshore out_offshore;
cryptonote::txout_xasset out_xasset;
out_xasset.asset_type = "XEUR";
tx.vin.push_back(xhv_key);
tx.vin.push_back(offshore_key);
cryptonote::tx_out out;
out.target = out_xasset;
tx.vout.push_back(out);
tx.vout.push_back(out);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// fail on multiple input types - ins) 1. XHV 2. XUSD outs) 1. XHV 2. not XUSD
TEST(get_tx_asset_types, fail_on_multiple_input_types_out_XHV_not_XUSD)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_offshore offshore_key;
cryptonote::txin_onshore onshore_key;
cryptonote::txin_xasset xasset_key;
cryptonote::txin_to_key xhv_key;
cryptonote::txout_to_key out_xhv;
cryptonote::txout_offshore out_offshore;
cryptonote::txout_xasset out_xasset;
out_xasset.asset_type = "XEUR";
tx.vin.push_back(xhv_key);
tx.vin.push_back(xhv_key);
tx.vin.push_back(offshore_key);
cryptonote::tx_out out;
out.target = out_xhv;
tx.vout.push_back(out);
cryptonote::tx_out out1;
out1.target = out_xasset;
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// fail on multiple input types - ins) 1. XHV 2. XUSD outs) 1. not XHV 2. XUSD
TEST(get_tx_asset_types, fail_on_multiple_input_types_out_XUSD_not_XHV)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_offshore offshore_key;
cryptonote::txin_onshore onshore_key;
cryptonote::txin_xasset xasset_key;
cryptonote::txin_to_key xhv_key;
cryptonote::txout_to_key out_xhv;
cryptonote::txout_offshore out_offshore;
cryptonote::txout_xasset out_xasset;
out_xasset.asset_type = "XEUR";
tx.vin.push_back(xhv_key);
tx.vin.push_back(xhv_key);
tx.vin.push_back(offshore_key);
cryptonote::tx_out out;
out.target = out_xasset;
tx.vout.push_back(out);
cryptonote::tx_out out1;
out1.target = out_offshore;
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// pass on multiple input types - ins) 1. XHV 2. XUSD outs) 1. XHV 2. XUSD
TEST(get_tx_asset_types, pass_on_multiple_input_types_onshore)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_offshore offshore_key;
cryptonote::txin_onshore onshore_key;
cryptonote::txin_xasset xasset_key;
cryptonote::txin_to_key xhv_key;
cryptonote::txout_to_key out_xhv;
cryptonote::txout_offshore out_offshore;
cryptonote::txout_xasset out_xasset;
tx.vin.push_back(xhv_key);
tx.vin.push_back(xhv_key);
tx.vin.push_back(offshore_key);
cryptonote::tx_out out;
out.target = out_xhv;
tx.vout.push_back(out);
cryptonote::tx_out out1;
out1.target = out_offshore;
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_TRUE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// fail on single input types with more than 2 output types
TEST(get_tx_asset_types, fail_single_input_and_more_than_2output_types)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_to_key xhv_key;
cryptonote::txout_to_key out_xhv;
cryptonote::txout_offshore out_offshore;
cryptonote::txout_xasset out_xasset;
out_xasset.asset_type = "XAU";
tx.vin.push_back(xhv_key);
tx.vin.push_back(xhv_key);
cryptonote::tx_out out;
out.target = out_xhv;
tx.vout.push_back(out);
cryptonote::tx_out out1;
out1.target = out_offshore;
tx.vout.push_back(out1);
cryptonote::tx_out out2;
out2.target = out_xasset;
tx.vout.push_back(out2);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// fail on single input types & single output types & they are not equal
TEST(get_tx_asset_types, fail_single_input_single_output_types_are_not_equal)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_to_key xhv_key;
cryptonote::txout_xasset out_xasset;
cryptonote::txout_xasset out_xasset1;
out_xasset.asset_type = "XAG";
out_xasset1.asset_type = "XAG";
tx.vin.push_back(xhv_key);
tx.vin.push_back(xhv_key);
cryptonote::tx_out out;
out.target = out_xasset;
tx.vout.push_back(out);
cryptonote::tx_out out1;
out1.target = out_xasset1;
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// fail on single input types & 2 output types & none of the outputs matches inputs
TEST(get_tx_asset_types, none_of_output_mathces_input)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_to_key xhv_key;
cryptonote::txout_xasset out_xasset;
cryptonote::txout_xasset out_xasset1;
out_xasset.asset_type = "XAG";
out_xasset1.asset_type = "XBTC";
tx.vin.push_back(xhv_key);
tx.vin.push_back(xhv_key);
cryptonote::tx_out out;
out.target = out_xasset;
tx.vout.push_back(out);
cryptonote::tx_out out1;
out1.target = out_xasset1;
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// pass on single input types & 2 output types & 1 of the outputs matches inputs, other ddesn't but not allowed. xhv -> xasset
// This case will expected to be caught by get_tx_type()
TEST(get_tx_asset_types, succesfuul_on_logical_input_output_but_not_allowed)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_to_key xhv_key;
cryptonote::txout_to_key out_xhv;
cryptonote::txout_xasset out_xasset1;
out_xasset1.asset_type = "XBTC";
tx.vin.push_back(xhv_key);
tx.vin.push_back(xhv_key);
cryptonote::tx_out out;
out.target = out_xhv;
tx.vout.push_back(out);
cryptonote::tx_out out1;
out1.target = out_xasset1;
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_TRUE(get_tx_asset_types(tx, tx.hash, source, dest, false));
EXPECT_EQ(source, "XHV");
EXPECT_EQ(dest, "XBTC");
}
// pass on 2 different xasset but source and dest are different. This case will expectedd to be catch by get_tx_type()
TEST(get_tx_asset_types, successful_on_logical_input_output_but_not_allowed_xassets)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_xasset xasset_key;
xasset_key.asset_type = "XBTC";
tx.vin.push_back(xasset_key);
tx.vin.push_back(xasset_key);
tx.vin.push_back(xasset_key);
cryptonote::tx_out out;
cryptonote::tx_out out1;
cryptonote::txout_xasset out_xasset;
cryptonote::txout_xasset out_xasset1;
out_xasset.asset_type = "XBTC";
out_xasset1.asset_type = "XJPY";
out.target = out_xasset;
out1.target = out_xasset1;
tx.vout.push_back(out);
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_TRUE(get_tx_asset_types(tx, tx.hash, source, dest, false));
EXPECT_EQ(source, "XBTC");
EXPECT_EQ(dest, "XJPY");
}
// fail on 2 different xasset
TEST(get_tx_asset_types, fail_on_2_different_xasset)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_xasset xasset_key;
xasset_key.asset_type = "XBTC";
tx.vin.push_back(xasset_key);
tx.vin.push_back(xasset_key);
tx.vin.push_back(xasset_key);
cryptonote::tx_out out;
cryptonote::tx_out out1;
cryptonote::txout_xasset out_xasset;
cryptonote::txout_xasset out_xasset1;
out_xasset.asset_type = "XJPY";
out_xasset1.asset_type = "XJPY";
out.target = out_xasset;
out1.target = out_xasset1;
tx.vout.push_back(out);
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// fail on unknown asset types
TEST(get_tx_asset_types, fail_on_2_unknown_asset_types)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_xasset xasset_key;
xasset_key.asset_type = "xabc";
tx.vin.push_back(xasset_key);
tx.vin.push_back(xasset_key);
tx.vin.push_back(xasset_key);
cryptonote::tx_out out;
cryptonote::tx_out out1;
cryptonote::txout_xasset out_xasset;
cryptonote::txout_xasset out_xasset1;
out_xasset.asset_type = "xabc";
out_xasset1.asset_type = "xabc";
out.target = out_xasset;
out1.target = out_xasset1;
tx.vout.push_back(out);
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// fail on unknown asset types
TEST(get_tx_asset_types, fail_on_2_unknown_asset_types_and_multiple_outs)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_xasset xasset_key;
xasset_key.asset_type = "xabc";
tx.vin.push_back(xasset_key);
tx.vin.push_back(xasset_key);
tx.vin.push_back(xasset_key);
cryptonote::tx_out out;
cryptonote::tx_out out1;
cryptonote::txout_xasset out_xasset;
cryptonote::txout_xasset out_xasset1;
out_xasset.asset_type = "xabc";
out_xasset1.asset_type = "xbdc";
out.target = out_xasset;
out1.target = out_xasset1;
tx.vout.push_back(out);
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
// fail on unknown asset types
TEST(get_tx_asset_types, fail_on_1_unknown_asset_type)
{
cryptonote::transaction tx;
tx.version = 7;
cryptonote::txin_xasset xasset_key;
xasset_key.asset_type = "XBTC";
tx.vin.push_back(xasset_key);
tx.vin.push_back(xasset_key);
tx.vin.push_back(xasset_key);
cryptonote::tx_out out;
cryptonote::tx_out out1;
cryptonote::txout_xasset out_xasset;
cryptonote::txout_xasset out_xasset1;
out_xasset.asset_type = "XBTC";
out_xasset1.asset_type = "xbdc";
out.target = out_xasset;
out1.target = out_xasset1;
tx.vout.push_back(out);
tx.vout.push_back(out1);
std::string source;
std::string dest;
EXPECT_FALSE(get_tx_asset_types(tx, tx.hash, source, dest, false));
}
*/
+492
View File
@@ -0,0 +1,492 @@
// Copyright (c) 2025, Salvium (author: akil)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// 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.
#define IN_UNIT_TESTS
#include "gtest/gtest.h"
#include "cryptonote_core/blockchain.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_core/cryptonote_core.h"
#include "blockchain_db/testdb.h"
using namespace cryptonote;
const uint64_t AMOUNT_BURNT = 1000000000000; // 1 SAL
const uint64_t STAKE_REWARD = 10000000000000; // 10 SAL
const uint64_t STAKE_PAYOUT = 216001000000000000; // 216k SAL
const uint64_t STAKE_LOCK_PERIOD = get_config(network_type::FAKECHAIN).STAKE_LOCK_PERIOD;
const auto AUDIT_HARD_FORKS = get_config(network_type::FAKECHAIN).AUDIT_HARD_FORKS;
class TestDB: public BaseTestDB {
public:
TestDB() { m_open = true; }
virtual uint64_t height() const override { return blocks.size(); }
virtual void add_block(const block& blk
, size_t block_weight
, uint64_t long_term_block_weight
, const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
, uint64_t num_rct_outs
, oracle::asset_type_counts& cum_rct_by_asset_type
, const crypto::hash& blk_hash
, uint64_t slippage_total
, uint64_t yield_total
, uint64_t audit_total
, const network_type nettype
, yield_block_info& ybi
, audit_block_info& abi
) override {
uint64_t height = this->height();
yield_block_info ybi_prev = { 0, 0, 0, 0, 100 };
yield_block_info ybi_matured = { 0, 0, 0, 0, 100 };
if (height >= 1) {
ybi_prev = yield_info.at(height - 1);
}
if (height > STAKE_LOCK_PERIOD) {
ybi_matured = yield_info.at(height - STAKE_LOCK_PERIOD - 1);
}
ybi.block_height = height;
ybi.locked_coins_this_block = yield_total;
ybi.slippage_total_this_block = slippage_total;
ybi.locked_coins_tally = yield_total + ybi_prev.locked_coins_tally - ybi_matured.locked_coins_this_block;
abi.locked_coins_this_block = audit_total;
abi.locked_coins_tally = audit_total;
abi.block_height = ybi.block_height;
yield_info[ybi.block_height] = ybi;
audit_info[abi.block_height] = abi;
blocks.push_back(blk);
}
virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const std::pair<transaction, blobdata_ref>& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash, const bool miner_tx) override {
if (tx.first.type == transaction_type::STAKE || tx.first.type == transaction_type::AUDIT) {
yield_tx_info yti;
yti.tx_hash = tx_hash;
yti.block_height = (uint64_t)this->height();
yti.locked_coins = tx.first.amount_burnt;
yti.return_address = tx.first.return_address_list[0];
if (tx.first.type == transaction_type::STAKE) {
yield_txs[yti.block_height].push_back(yti);
} else if (tx.first.type == transaction_type::AUDIT) {
audit_txs[yti.block_height].push_back(yti);
}
}
return 1;
}
virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const override {
if (yield_info.count(height) == 0)
return 1;
ybi = yield_info.at(height);
return 0;
}
virtual int get_audit_block_info(const uint64_t height, audit_block_info& abi) const override {
if (audit_info.count(height) == 0)
return 1;
abi = audit_info.at(height);
return 0;
}
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const override {
if (yield_txs.count(height) == 0)
return 1;
yti_container = yield_txs.at(height);
return 0;
}
virtual int get_audit_tx_info(const uint64_t height, std::vector<yield_tx_info>& ati_container) const override {
if (audit_txs.count(height) == 0)
return 1;
ati_container = audit_txs.at(height);
return 0;
}
virtual crypto::hash get_block_hash_from_height(const uint64_t &height) const override {
crypto::hash hash = crypto::null_hash;
*(uint64_t*)&hash = height;
return hash;
}
virtual crypto::hash top_block_hash(uint64_t *block_height = NULL) const override {
uint64_t h = height();
crypto::hash top = crypto::null_hash;
if (h)
*(uint64_t*)&top = h - 1;
if (block_height)
*block_height = h - 1;
return top;
}
private:
std::vector<block> blocks;
std::map<uint64_t, yield_block_info> yield_info;
std::map<uint64_t, audit_block_info> audit_info;
std::map<uint64_t, std::vector<yield_tx_info>> yield_txs;
std::map<uint64_t, std::vector<yield_tx_info>> audit_txs;
};
transaction get_audit_tx() {
transaction audit_tx;
audit_tx.type = transaction_type::AUDIT;
std::string str = "audit-return-address-32-bytes32";
rct::key audit_return_address;
memcpy(audit_return_address.bytes, str.data(), sizeof(audit_return_address.bytes));
audit_tx.return_address_list.push_back(rct::rct2pk(audit_return_address));
audit_tx.amount_burnt = AMOUNT_BURNT;
audit_tx.version = 2;
return audit_tx;
}
transaction get_stake_tx() {
transaction stake_tx;
stake_tx.type = transaction_type::STAKE;
std::string str = "stake-return-address-32-bytes32";
rct::key stake_return_address;
memcpy(stake_return_address.bytes, str.data(), sizeof(stake_return_address.bytes));
stake_tx.return_address_list.push_back(rct::rct2pk(stake_return_address));
stake_tx.amount_burnt = AMOUNT_BURNT;
stake_tx.version = 2;
return stake_tx;
}
void add_block(Blockchain *bc, uint8_t num_audit_txs, uint8_t num_stake_txs) {
block b;
b.miner_tx.amount_burnt = STAKE_REWARD;
// add txs
std::vector<std::pair<transaction, blobdata>> audit_txs;
std::vector<std::pair<transaction, blobdata>> stake_txs;
for (int i = 0; i < num_audit_txs; i++) {
audit_txs.push_back(std::make_pair(get_audit_tx(), ""));
}
for (int i = 0; i < num_stake_txs; i++) {
stake_txs.push_back(std::make_pair(get_stake_tx(), ""));
}
// merge & add txs to the block
std::vector<std::pair<transaction, blobdata>> txs;
txs.insert(txs.end(), audit_txs.begin(), audit_txs.end());
txs.insert(txs.end(), stake_txs.begin(), stake_txs.end());
for (auto tx : txs) {
b.tx_hashes.push_back(tx.first.hash);
}
// add the block
yield_block_info ybi;
audit_block_info abi;
bc->get_db().add_block(
std::make_pair(b, ""),
0, 0, 1, 0,
txs,
network_type::FAKECHAIN, ybi, abi
);
}
uint64_t progress_chain(Blockchain *bc, uint64_t num_blocks) {
for (int i = 0; i < num_blocks; i++) {
add_block(bc, 0, 0);
}
return bc->get_db().height();
}
TEST(protocol_tx, validate)
{
const std::pair<uint8_t, uint64_t> hard_forks[3] = {
std::make_pair(1, 1),
std::make_pair(5, 1 * STAKE_LOCK_PERIOD),
std::make_pair(6, 2 * STAKE_LOCK_PERIOD),
};
const test_options test_options = {
hard_forks,
1000,
};
BlockchainAndPool bap;
Blockchain *bc = &bap.blockchain;
bool r = bc->init(new TestDB(), network_type::FAKECHAIN, true, &test_options, 0, NULL);
ASSERT_TRUE(r);
block b;
b.miner_tx.amount_burnt = STAKE_REWARD;
// ******** Special cases for genesis block(height is 0) ********
uint64_t height = bc->get_db().height() - 1;
uint8_t hf_version = bc->get_ideal_hard_fork_version(height);
// should have version 1
b.protocol_tx.version = 2;
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
b.protocol_tx.version = 1;
// shouldn't have any inputs
b.protocol_tx.vin.push_back(txin_gen{height});
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
b.protocol_tx.vin.clear();
// shouldn't have any outputs
b.protocol_tx.vout.push_back(tx_out {0, txout_target_v{txout_to_key{}}});
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
b.protocol_tx.vout.clear();
// should now pass
EXPECT_TRUE(bc->validate_protocol_transaction(b, height, hf_version));
// ******** Normal cases ********
height = progress_chain(bc, 1);
hf_version = bc->get_current_hard_fork_version();
// should only have 1 input
b.protocol_tx.vin.clear();
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
b.protocol_tx.vin.push_back(txin_gen{height});
b.protocol_tx.vin.push_back(txin_gen{height});
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
b.protocol_tx.vin.pop_back();
// should now pass with no output
b.protocol_tx.vout.clear();
EXPECT_TRUE(bc->validate_protocol_transaction(b, height, hf_version));
// height should be at least STAKE_LOCK_PERIOD if there is at least 1 output
b.protocol_tx.vout.push_back(tx_out {10000, txout_target_v{txout_to_key{}}});
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
b.protocol_tx.vout.clear();
// reach to stake period height
height = progress_chain(bc, STAKE_LOCK_PERIOD);
hf_version = bc->get_current_hard_fork_version();
// should fail if can't find a ybi & abi entry for the block
// (this will always pass since you can't add a block without ybi & abi entry)
// should fail if protocol tx outputs(1) > audit txs(0) + yield txs(0)
b.protocol_tx.vout.push_back(tx_out {10000, txout_target_v{txout_to_key{}}});
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
b.protocol_tx.vout.clear();
// there shouldn't be outputs for audit txs in hf_version 5
EXPECT_EQ((int)bc->get_ideal_hard_fork_version(height), 5);
add_block(bc, 1, 1);
// progress chain until the audit payout
height = progress_chain(bc, AUDIT_HARD_FORKS.at(6).first);
hf_version = bc->get_current_hard_fork_version();
// make outputs for audit tx
b.protocol_tx.vout.push_back(tx_out {AMOUNT_BURNT, txout_target_v{txout_to_key{
get_audit_tx().return_address_list[0],
"SAL",
CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW
}}});
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
b.protocol_tx.vout.clear();
// progress chain until the stake payout
height = progress_chain(bc, STAKE_LOCK_PERIOD - AUDIT_HARD_FORKS.at(6).first);
hf_version = bc->get_current_hard_fork_version();
// make sure its paid out with correct asset type & amount
b.protocol_tx.vout.push_back(
tx_out {
STAKE_PAYOUT - 1, // wrong amount
txout_target_v{txout_to_key{
get_stake_tx().return_address_list[0],
"SAL1", // wrong asset type
CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - 1 // wrong unlock window
}
}
}
);
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
// fix amount
b.protocol_tx.vout[0].amount = STAKE_PAYOUT;
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
// fix asset type
boost::get<txout_to_key>(b.protocol_tx.vout[0].target).asset_type = "SAL";
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
// fix unlock window
boost::get<txout_to_key>(b.protocol_tx.vout[0].target).unlock_time = CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
EXPECT_TRUE(bc->validate_protocol_transaction(b, height, hf_version));
b.protocol_tx.vout.clear();
// audit txs should now be paid out in hf_version 6
EXPECT_EQ((int)bc->get_ideal_hard_fork_version(height), 6);
add_block(bc, 1, 0);
// progress chain until the payout
height = progress_chain(bc, AUDIT_HARD_FORKS.at(6).first);
hf_version = bc->get_current_hard_fork_version();
// validate we get paid out with correct asset type & amount
b.protocol_tx.vout.push_back(
tx_out {
AMOUNT_BURNT - 1, // wrong amount
txout_target_v{txout_to_key{
get_audit_tx().return_address_list[0],
"SAL", // wrong asset type
CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - 1 // wrong unlock window
}
}
}
);
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
// fix amount
b.protocol_tx.vout[0].amount = AMOUNT_BURNT;
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
// fix asset type
boost::get<txout_to_key>(b.protocol_tx.vout[0].target).asset_type = "SAL1";
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
// fix unlock window
boost::get<txout_to_key>(b.protocol_tx.vout[0].target).unlock_time = CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
EXPECT_TRUE(bc->validate_protocol_transaction(b, height, hf_version));
b.protocol_tx.vout.clear();
// add a stake tx
add_block(bc, 0, 1);
// progress chain to add audit tx so that they both mature at the same time
height = progress_chain(bc, STAKE_LOCK_PERIOD - AUDIT_HARD_FORKS.at(6).first - 1);
hf_version = bc->get_current_hard_fork_version();
// add an audit tx
add_block(bc, 1, 0);
// progress chain to mature height
height = progress_chain(bc, AUDIT_HARD_FORKS.at(6).first);
hf_version = bc->get_current_hard_fork_version();
// fail since protocol tx outputs(0) < audit txs(1) + yield txs(1)
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
// audit tx output
b.protocol_tx.vout.push_back(tx_out {AMOUNT_BURNT, txout_target_v{txout_to_key{
get_audit_tx().return_address_list[0],
"SAL1",
CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW
}}});
// fail since protocol tx outputs(1) < audit txs(1) + yield txs(1)
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
// stake tx output
b.protocol_tx.vout.push_back(tx_out {STAKE_PAYOUT, txout_target_v{txout_to_key{
get_stake_tx().return_address_list[0],
"SAL1",
CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW
}}});
// fail since protocol tx outputs[0] != stake_tx
EXPECT_FALSE(bc->validate_protocol_transaction(b, height, hf_version));
// swap the outputs
std::swap(b.protocol_tx.vout[0], b.protocol_tx.vout[1]);
EXPECT_TRUE(bc->validate_protocol_transaction(b, height, hf_version));
}
TEST(protocol_tx, prevalidate)
{
tx_memory_pool* tx_pool = nullptr;
Blockchain blockchain(*tx_pool);
block b;
// ******** Special cases for genesis block(height is 0) ********
uint64_t height = 0;
uint8_t hf_version = 1;
// should have version 1
b.protocol_tx.version = 2;
EXPECT_FALSE(blockchain.prevalidate_protocol_transaction(b, height, hf_version));
b.protocol_tx.version = 1;
// shouldn't have any inputs
b.protocol_tx.vin.push_back(txin_gen{height});
EXPECT_FALSE(blockchain.prevalidate_protocol_transaction(b, height, hf_version));
b.protocol_tx.vin.clear();
// shouldn't have any outputs
b.protocol_tx.vout.push_back(tx_out {0, txout_target_v{txout_to_key{}}});
EXPECT_FALSE(blockchain.prevalidate_protocol_transaction(b, height, hf_version));
b.protocol_tx.vout.clear();
// should now pass
EXPECT_TRUE(blockchain.prevalidate_protocol_transaction(b, height, hf_version));
// ******** Normal cases ********
height = 1;
hf_version = 1;
// should have version > 1
b.protocol_tx.version = 1;
EXPECT_FALSE(blockchain.prevalidate_protocol_transaction(b, height, hf_version));
b.protocol_tx.version = 2;
// should have only 1 input
b.protocol_tx.vin.push_back(txin_gen{height});
b.protocol_tx.vin.push_back(txin_gen{height});
EXPECT_FALSE(blockchain.prevalidate_protocol_transaction(b, height, hf_version));
b.protocol_tx.vin.clear();
// should have only input type txin_gen
b.protocol_tx.vin.push_back(txin_to_key {0, "SAL", {}, crypto::key_image{}});
EXPECT_FALSE(blockchain.prevalidate_protocol_transaction(b, height, hf_version));
b.protocol_tx.vin.clear();
b.protocol_tx.vin.push_back(txin_gen{height});
// should have rct type NULL
b.protocol_tx.rct_signatures.type = rct::RCTTypeSalviumOne;
EXPECT_FALSE(blockchain.prevalidate_protocol_transaction(b, height, hf_version));
b.protocol_tx.rct_signatures.type = rct::RCTTypeNull;
// txin_gen height must match block height
boost::get<txin_gen>(b.protocol_tx.vin[0]).height = height + 1;
EXPECT_FALSE(blockchain.prevalidate_protocol_transaction(b, height, hf_version));
boost::get<txin_gen>(b.protocol_tx.vin[0]).height = height;
// output amounts can't overflow
b.protocol_tx.vout.push_back(tx_out {UINT64_MAX, txout_target_v{txout_to_key{}}});
b.protocol_tx.vout.push_back(tx_out {1, txout_target_v{txout_to_key{}}});
EXPECT_FALSE(blockchain.prevalidate_protocol_transaction(b, height, hf_version));
b.protocol_tx.vout.clear();
b.protocol_tx.vout.push_back(tx_out {UINT64_MAX, txout_target_v{txout_to_key{}}});
}