Compare commits

..

6 Commits

Author SHA1 Message Date
Some Random Crypto Guy
aa64124c28 fix for supply_info command with SAL supply going negative 2025-02-14 13:28:22 +00:00
Some Random Crypto Guy
bc7db51f03 various fixes ready for next HF 2025-02-11 14:35:57 +00:00
Some Random Crypto Guy
6889321361 fixed possible throw() when dust in index=0; bumped version number 2025-02-11 12:18:08 +00:00
Some Random Crypto Guy
3cb473132e Merge branch 'main' of https://github.com/salvium/salvium 2025-02-10 13:21:47 +00:00
akildemir
b298f542a6 add audit tx support for wallet api (#14) 2025-02-10 13:20:42 +00:00
Some Random Crypto Guy
eb9f799b8b fixed syncing message error level; added thread library for auditing 2025-02-10 13:11:52 +00:00
16 changed files with 213 additions and 56 deletions

View File

@@ -1,4 +1,4 @@
# Salvium Zero v0.9.1
# Salvium Zero v0.9.3
Copyright (c) 2023-2024, Salvium
Portions Copyright (c) 2014-2023, The Monero Project
@@ -172,7 +172,7 @@ invokes cmake commands as needed.
```bash
cd salvium
git checkout v0.9.1
git checkout v0.9.3
make
```
@@ -251,7 +251,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
```bash
git clone https://github.com/salvium/salvium
cd salvium
git checkout v0.9.1
git checkout v0.9.3
```
* Build:
@@ -370,10 +370,10 @@ application.
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.1'. 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.3'. If you don't care about the version and just want binaries from master, skip this step:
```bash
git checkout v0.9.1
git checkout v0.9.3
```
* If you are on a 64-bit system, run:

View File

@@ -3778,6 +3778,11 @@ std::map<std::string,uint64_t> BlockchainLMDB::get_circulating_supply() const
//amount += m_coinbase;
}
if (amount < 0) {
// Negative number can't be converted to a 64-bit UINT, so return 0, but retain -ve number privately
LOG_PRINT_L2("BlockchainLMDB::" << __func__ << " - supply of " << currency_label << " is negative (" << amount << ") but outputting zero");
amount = 0;
}
circulating_supply[currency_label] = amount.convert_to<uint64_t>();
}

View File

@@ -142,17 +142,23 @@ set(blockchain_scanner_private_headers)
monero_private_headers(blockchain_scanner
${blockchain_scanner_private_headers})
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp")
set(blockchain_audit_sources
blockchain_audit.cpp
)
set(blockchain_audit_private_headers)
monero_private_headers(blockchain_audit
${blockchain_audit_private_headers})
if (BUILD_TAG)
else()
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp")
set(blockchain_audit_sources
blockchain_audit.cpp
threadpool_boost.cpp
)
set(blockchain_audit_private_headers
threadpool_boost.h
)
monero_private_headers(blockchain_audit
${blockchain_audit_private_headers})
else()
message(STATUS "blockchain_audit.cpp not found - not building the audit tool")
endif()
endif()
monero_add_executable(blockchain_import
@@ -324,30 +330,36 @@ set_property(TARGET blockchain_scanner
OUTPUT_NAME "salvium-blockchain-scanner")
install(TARGETS blockchain_scanner DESTINATION bin)
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp")
monero_add_executable(blockchain_audit
${blockchain_audit_sources}
${blockchain_audit_private_headers})
if (BUILD_TAG)
else()
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp")
monero_add_executable(blockchain_audit
${blockchain_audit_sources}
${blockchain_audit_private_headers})
target_link_libraries(blockchain_audit
PRIVATE
wallet
crypto
cncrypto
cryptonote_core
blockchain_db
version
epee
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
target_include_directories(blockchain_audit PRIVATE /usr/include/mysql-cppconn/jdbc)
target_link_libraries(blockchain_audit
PRIVATE
wallet
crypto
cncrypto
cryptonote_core
blockchain_db
version
epee
mysqlcppconn
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
set_property(TARGET blockchain_audit
PROPERTY
OUTPUT_NAME "salvium-blockchain-audit")
install(TARGETS blockchain_audit DESTINATION bin)
set_property(TARGET blockchain_audit
PROPERTY
OUTPUT_NAME "salvium-blockchain-audit")
install(TARGETS blockchain_audit DESTINATION bin)
endif()
endif()
monero_add_executable(blockchain_stats

View File

@@ -0,0 +1,44 @@
#include <iostream>
#include "threadpool_boost.h"
ThreadPool::ThreadPool(size_t numThreads)
: workGuard(boost::asio::make_work_guard(ioService)) {
for (size_t i = 0; i < numThreads; ++i) {
workers.emplace_back([this]() {
std::cerr << "Thread started" << std::endl;
ioService.run();
std::cerr << "Thread finished" << std::endl;
});
}
}
void ThreadPool::enqueue(std::function<void()> task) {
ioService.post([task]() {
try {
task(); // Run the task
} catch (const std::exception& e) {
std::cerr << "Exception in thread pool task: " << e.what() << std::endl;
} catch (...) {
std::cerr << "Unknown exception in thread pool task!" << std::endl;
}
});
}
bool ThreadPool::isStopping() const {
return ioService.stopped(); // Check if io_context has stopped
}
void ThreadPool::waitForCompletion() {
std::cout << "Waiting for completion...\n";
workGuard.reset(); // Allow ioService to stop when no more tasks
ioService.run(); // Ensure no threads are left hanging
for (auto &worker : workers) {
if (worker.joinable()) worker.join();
}
std::cout << "All threads joined.\n";
}
ThreadPool::~ThreadPool() {
waitForCompletion();
}

View File

@@ -0,0 +1,24 @@
#ifndef THREADPOOL_BOOST_H
#define THREADPOOL_BOOST_H
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <functional>
#include <vector>
class ThreadPool {
public:
explicit ThreadPool(size_t numThreads);
~ThreadPool();
void enqueue(std::function<void()> task);
bool isStopping() const;
void waitForCompletion();
private:
boost::asio::io_service ioService;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> workGuard;
std::vector<boost::thread> workers;
};
#endif // THREADPOOL_BOOST_H

View File

@@ -231,6 +231,8 @@
#define HF_VERSION_AUDIT1 6
#define HF_VERSION_SALVIUM_ONE_PROOFS 6
#define HF_VERSION_AUDIT1_PAUSE 7
#define HF_VERSION_REQUIRE_VIEW_TAGS 255
#define HF_VERSION_ENABLE_CONVERT 255
#define HF_VERSION_ENABLE_ORACLE 255

View File

@@ -1505,6 +1505,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
case HF_VERSION_ENFORCE_FULL_PROOFS:
case HF_VERSION_SHUTDOWN_USER_TXS:
case HF_VERSION_SALVIUM_ONE_PROOFS:
case HF_VERSION_AUDIT1_PAUSE:
if (b.miner_tx.amount_burnt > 0) {
CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.amount_burnt > money_in_use, false, "miner transaction is overflowed by amount_burnt");
money_in_use += b.miner_tx.amount_burnt;
@@ -1550,10 +1551,13 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
}
// if nothing is created by this TX - check no money is included
size_t vout_size = b.protocol_tx.vout.size();
CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 1, false, "coinbase protocol transaction in the block has no inputs");
CHECK_AND_ASSERT_MES(vout_size != 0, true, "coinbase protocol transaction in the block has no outputs");
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?
uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD;
if (height <= stake_lock_period) {
@@ -4084,7 +4088,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
const rct::rctSig &rv = tx.rct_signatures;
// Check that after full proofs are enabled, the RCT version is set to enforce full proofs
if (hf_version == HF_VERSION_SALVIUM_ONE_PROOFS) {
if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeSalviumOne) {
MERROR_VER("Unsupported rct type (full proofs (with audit data) are required): " << rv.type);
return false;
@@ -4511,7 +4515,9 @@ bool Blockchain::calculate_audit_payouts(const uint64_t start_height, std::vecto
}
// Build a blacklist of staking TXs _not_ to pay out for
const std::set<std::string> txs_blacklist = {};
const std::set<std::string> txs_blacklist = {
"017a79539e69ce16e91d9aa2267c102f336678c41636567c1129e3e72149499a"
};
// Get the ABI information for the 21,600 blocks that the matured TX(s), we can calculate audit
for (const auto& entry: audit_entries) {

View File

@@ -448,6 +448,7 @@ namespace cryptonote
case HF_VERSION_ENFORCE_FULL_PROOFS:
case HF_VERSION_SHUTDOWN_USER_TXS:
case HF_VERSION_SALVIUM_ONE_PROOFS:
case HF_VERSION_AUDIT1_PAUSE:
// SRCG: subtract 20% that will be rewarded to staking users
CHECK_AND_ASSERT_MES(tx.amount_burnt == 0, false, "while creating outs: amount_burnt is nonzero");
tx.amount_burnt = amount / 5;

View File

@@ -49,6 +49,9 @@ const hardfork_t mainnet_hard_forks[] = {
// version 6 starts from block 154750, which is on or around the 4th of February, 2025. Fork time finalised on 2025-01-31. No fork voting occurs for the v6 fork.
{ 6, 154750, 0, 1738336000 },
// version 7 starts from block 161900, which is on or around the 14th of February, 2025. Fork time finalised on 2025-02-04. No fork voting occurs for the v7 fork.
{ 7, 161900, 0, 1739264400 },
};
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
const uint64_t mainnet_hard_fork_version_1_till = ((uint64_t)-1);
@@ -69,8 +72,11 @@ const hardfork_t testnet_hard_forks[] = {
// version 5 (TX shutdown) starts from block 800
{ 5, 800, 0, 1734607005 },
// version 6 (audit) starts from block 815
// version 6 (audit 1) starts from block 815
{ 6, 815, 0, 1734608000 },
// version 7 (audit 1 pause) starts from block 900
{ 7, 900, 0, 1739264400 },
};
const size_t num_testnet_hard_forks = sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]);
const uint64_t testnet_hard_fork_version_1_till = ((uint64_t)-1);

View File

@@ -6981,12 +6981,22 @@ bool simple_wallet::transfer_main(
// Skip this wallet if there is no balance unlocked to audit
if (unlocked_balance_per_subaddr.count(subaddr_index) == 0) continue;
std::set<uint32_t> subaddr_indices_single;
subaddr_indices_single.insert(subaddr_index);
uint64_t unlock_block = get_config(m_wallet->nettype()).AUDIT_LOCK_PERIOD;
std::vector<tools::wallet2::pending_tx> ptx_vector_audit = m_wallet->create_transactions_all(0, cryptonote::transaction_type::AUDIT, source_asset, m_wallet->get_subaddress({m_current_subaddress_account, subaddr_index}), (subaddr_index>0), 1, fake_outs_count, unlock_block, priority, extra, m_current_subaddress_account, subaddr_indices_single);
ptx_vector.insert(ptx_vector.end(), ptx_vector_audit.begin(), ptx_vector_audit.end());
try {
std::set<uint32_t> subaddr_indices_single;
subaddr_indices_single.insert(subaddr_index);
uint64_t unlock_block = get_config(m_wallet->nettype()).AUDIT_LOCK_PERIOD;
std::vector<tools::wallet2::pending_tx> ptx_vector_audit = m_wallet->create_transactions_all(0, cryptonote::transaction_type::AUDIT, source_asset, m_wallet->get_subaddress({m_current_subaddress_account, subaddr_index}), (subaddr_index>0), 1, fake_outs_count, unlock_block, priority, extra, m_current_subaddress_account, subaddr_indices_single);
ptx_vector.insert(ptx_vector.end(), ptx_vector_audit.begin(), ptx_vector_audit.end());
} catch (const std::exception &e) {
// Let's skip this wallet - we have already reported the error
if (unlocked_balance_per_subaddr[subaddr_index].first < 250000000) {
fail_msg_writer() << boost::format(tr("Subaddress index %u has insufficient funds (%s) to pay for audit")) % subaddr_index % print_money(unlocked_balance_per_subaddr[subaddr_index].first);
}
}
}
} else {

View File

@@ -1,5 +1,5 @@
#define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@"
#define DEF_SALVIUM_VERSION "0.9.1"
#define DEF_SALVIUM_VERSION "0.9.3"
#define DEF_MONERO_VERSION_TAG "release"
#define DEF_MONERO_VERSION "0.18.3.3"
#define DEF_MONERO_RELEASE_NAME "Zero"

View File

@@ -1444,6 +1444,34 @@ PendingTransaction *WalletImpl::createStakeTransaction(uint64_t amount, uint32_t
return createTransactionMultDest(Monero::transaction_type::STAKE, std::vector<string> {dst_addr}, payment_id, amount ? (std::vector<uint64_t> {amount}) : (optional<std::vector<uint64_t>>()), mixin_count, asset_type, is_return, priority, subaddr_account, subaddr_indices);
}
PendingTransaction *WalletImpl::createAuditTransaction(
uint32_t mixin_count,
PendingTransaction::Priority priority,
uint32_t subaddr_account,
std::set<uint32_t> subaddr_indices
) {
// Need to populate {dst_entr, payment_id, asset_type, is_return}
const string dst_addr = m_wallet->get_subaddress_as_str({subaddr_account, 0});//MY LOCAL (SUB)ADDRESS
const string payment_id = "";
const string asset_type = "SAL";
const bool is_return = false;
LOG_ERROR("createAuditTransaction: called");
return createTransactionMultDest(
Monero::transaction_type::AUDIT,
std::vector<string> {dst_addr},
payment_id,
(optional<std::vector<uint64_t>>()),
mixin_count,
asset_type,
is_return,
priority,
subaddr_account,
subaddr_indices
);
}
// TODO:
// 1 - properly handle payment id (add another menthod with explicit 'payment_id' param)
// 2 - check / design how "Transaction" can be single interface
@@ -1536,11 +1564,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
fake_outs_count = m_wallet->adjust_mixin(mixin_count);
if (amount) {
transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, "SAL", "SAL", converted_tx_type, fake_outs_count, 0 /* unlock_time */,
transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, asset_type, asset_type, converted_tx_type, fake_outs_count, 0 /* unlock_time */,
adjusted_priority,
extra, subaddr_account, subaddr_indices);
} else {
transaction->m_pending_tx = m_wallet->create_transactions_all(0, converted_tx_type, "SAL", info.address, info.is_subaddress, 1, fake_outs_count, 0 /* unlock_time */,
transaction->m_pending_tx = m_wallet->create_transactions_all(0, converted_tx_type, asset_type, info.address, info.is_subaddress, 1, fake_outs_count, 0 /* unlock_time */,
adjusted_priority,
extra, subaddr_account, subaddr_indices);
}

View File

@@ -158,6 +158,10 @@ public:
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) override;
PendingTransaction * createAuditTransaction(uint32_t mixin_count,
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) override;
PendingTransaction * createTransactionMultDest(const transaction_type &tx_type,
const std::vector<std::string> &dst_addr, const std::string &payment_id,
optional<std::vector<uint64_t>> amount, uint32_t mixin_count,

View File

@@ -58,7 +58,8 @@ enum transaction_type : uint8_t {
BURN = 5,
STAKE = 6,
RETURN = 7,
MAX = 7
AUDIT = 8,
MAX = 8
};
namespace Utils {
@@ -97,7 +98,7 @@ struct YieldInfo
virtual uint64_t yield() const = 0;
virtual uint64_t yield_per_stake() const = 0;
virtual std::string period() const = 0;
virtual std::vector<std::tuple<size_t, std::string, uint64_t, uint64_t>> payouts() const = 0;
virtual std::vector<std::tuple<size_t, std::string, std::string, uint64_t, uint64_t>> payouts() const = 0;
};
@@ -878,6 +879,20 @@ struct Wallet
uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) = 0;
/*!
* \brief createAuditTransaction creates audit transaction.
* \param mixin_count mixin count. if 0 passed, wallet will use default value
* \param subaddr_indices set of subaddress indices to use for transfer or sweeping. if set empty, all are chosen when sweeping, and one or more are automatically chosen when transferring. after execution, returns the set of actually used indices
* \param priority
* \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
* after object returned
*/
virtual PendingTransaction * createAuditTransaction(uint32_t mixin_count,
PendingTransaction::Priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) = 0;
/*!
* \brief createTransactionMultDest creates transaction with multiple destinations. if dst_addr is an integrated address, payment_id is ignored
* \param tx_type the type of transaction being created

View File

@@ -123,7 +123,7 @@ namespace Monero {
return m_yield_per_stake;
}
std::vector<std::tuple<size_t, std::string, uint64_t, uint64_t>> YieldInfoImpl::payouts() const
std::vector<std::tuple<size_t, std::string, std::string, uint64_t, uint64_t>> YieldInfoImpl::payouts() const
{
return m_payouts;
}

View File

@@ -53,7 +53,7 @@ public:
uint64_t yield() const override;
uint64_t yield_per_stake() const override;
std::string period() const override;
std::vector<std::tuple<size_t, std::string, uint64_t, uint64_t>> payouts() const override;
std::vector<std::tuple<size_t, std::string, std::string, uint64_t, uint64_t>> payouts() const override;
private:
friend class WalletImpl;
@@ -68,7 +68,7 @@ private:
uint64_t m_yield_per_stake;
uint64_t m_num_entries;
std::string m_period;
std::vector<std::tuple<size_t, std::string, uint64_t, uint64_t>> m_payouts;
std::vector<std::tuple<size_t, std::string, std::string, uint64_t, uint64_t>> m_payouts;
};
}