#include #include "cstdlib" #include #include #include #include #include #include #include #include "thread" #include "CwWalletListener.h" #if __APPLE__ // Fix for randomx on ios void __clear_cache(void* start, void* end) { } #include "../External/ios/include/wallet2_api.h" #elif __linux__ #include "../External/linux/include/wallet2_api.h" #include #else #include "../External/android/include/wallet2_api.h" #endif #if defined(__GNUC__) #define FUNCTION_VISABILITY_ATTRIBUTE __attribute__((visibility("default"))) __attribute__((used)) #endif using namespace std::chrono_literals; #ifdef __cplusplus extern "C" { #endif struct Utf8Box { char *value; Utf8Box(char *_value) { value = _value; } }; struct SubaddressRow { uint64_t id; char *address; char *label; SubaddressRow(std::size_t _id, char *_address, char *_label) { id = static_cast(_id); address = _address; label = _label; } }; struct AccountRow { uint64_t id; char *label; AccountRow(std::size_t _id, char *_label) { id = static_cast(_id); label = _label; } }; struct MoneroWalletListener : Monero::WalletListener { uint64_t m_height; bool m_need_to_refresh; bool m_new_transaction; MoneroWalletListener() { m_height = 0; m_need_to_refresh = false; m_new_transaction = false; } void moneySpent(const std::string &txId, uint64_t amount) { m_new_transaction = true; } void moneyReceived(const std::string &txId, uint64_t amount) { m_new_transaction = true; } void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) { m_new_transaction = true; } void newBlock(uint64_t height) { m_height = height; } void updated() { m_new_transaction = true; } void refreshed() { m_need_to_refresh = true; } void resetNeedToRefresh() { m_need_to_refresh = false; } bool isNeedToRefresh() { return m_need_to_refresh; } bool isNewTransactionExist() { return m_new_transaction; } void resetIsNewTransactionExist() { m_new_transaction = false; } uint64_t height() { return m_height; } }; struct TransactionInfoRow { uint64_t amount; uint64_t fee; uint64_t blockHeight; uint64_t confirmations; uint32_t subaddrAccount; int8_t direction; int8_t isPending; uint32_t subaddrIndex; char *hash; char *paymentId; int64_t datetime; TransactionInfoRow(Monero::TransactionInfo *transaction) { amount = transaction->amount(); fee = transaction->fee(); blockHeight = transaction->blockHeight(); subaddrAccount = transaction->subaddrAccount(); std::set subIndex = transaction->subaddrIndex(); std::set::iterator it = subIndex.begin(); subaddrIndex = *it; confirmations = transaction->confirmations(); datetime = static_cast(transaction->timestamp()); direction = transaction->direction(); isPending = static_cast(transaction->isPending()); std::string *hash_str = new std::string(transaction->hash()); hash = strdup(hash_str->c_str()); paymentId = strdup(transaction->paymentId().c_str()); } }; struct PendingTransactionRaw { uint64_t amount; uint64_t fee; char *hash; char *hex; char *txKey; Monero::PendingTransaction *transaction; PendingTransactionRaw(Monero::PendingTransaction *_transaction) { transaction = _transaction; amount = _transaction->amount(); fee = _transaction->fee(); hash = strdup(_transaction->txid()[0].c_str()); hex = strdup(_transaction->hex()[0].c_str()); txKey = strdup(_transaction->txKey()[0].c_str()); } }; struct CoinsInfoRow { uint64_t blockHeight; char *hash; uint64_t internalOutputIndex; uint64_t globalOutputIndex; bool spent; bool frozen; uint64_t spentHeight; uint64_t amount; bool rct; bool keyImageKnown; uint64_t pkIndex; uint32_t subaddrIndex; uint32_t subaddrAccount; char *address; char *addressLabel; char *keyImage; uint64_t unlockTime; bool unlocked; char *pubKey; bool coinbase; char *description; CoinsInfoRow(Monero::CoinsInfo *coinsInfo) { blockHeight = coinsInfo->blockHeight(); std::string *hash_str = new std::string(coinsInfo->hash()); hash = strdup(hash_str->c_str()); internalOutputIndex = coinsInfo->internalOutputIndex(); globalOutputIndex = coinsInfo->globalOutputIndex(); spent = coinsInfo->spent(); frozen = coinsInfo->frozen(); spentHeight = coinsInfo->spentHeight(); amount = coinsInfo->amount(); rct = coinsInfo->rct(); keyImageKnown = coinsInfo->keyImageKnown(); pkIndex = coinsInfo->pkIndex(); subaddrIndex = coinsInfo->subaddrIndex(); subaddrAccount = coinsInfo->subaddrAccount(); address = strdup(coinsInfo->address().c_str()) ; addressLabel = strdup(coinsInfo->addressLabel().c_str()); keyImage = strdup(coinsInfo->keyImage().c_str()); unlockTime = coinsInfo->unlockTime(); unlocked = coinsInfo->unlocked(); pubKey = strdup(coinsInfo->pubKey().c_str()); coinbase = coinsInfo->coinbase(); description = strdup(coinsInfo->description().c_str()); } void setUnlocked(bool unlocked); }; Monero::Coins *m_coins; Monero::Wallet *m_wallet; Monero::TransactionHistory *m_transaction_history; MoneroWalletListener *m_listener; Monero::Subaddress *m_subaddress; Monero::SubaddressAccount *m_account; uint64_t m_last_known_wallet_height; uint64_t m_cached_syncing_blockchain_height = 0; std::list m_coins_info; std::mutex store_lock; bool is_storing = false; FUNCTION_VISABILITY_ATTRIBUTE void change_current_wallet(Monero::Wallet *wallet) { m_wallet = wallet; m_listener = nullptr; if (wallet != nullptr) { m_transaction_history = wallet->history(); } else { m_transaction_history = nullptr; } if (wallet != nullptr) { m_account = wallet->subaddressAccount(); } else { m_account = nullptr; } if (wallet != nullptr) { m_subaddress = wallet->subaddress(); } else { m_subaddress = nullptr; } m_coins_info = std::list(); if (wallet != nullptr) { m_coins = wallet->coins(); } else { m_coins = nullptr; } } Monero::Wallet *get_current_wallet() { return m_wallet; } FUNCTION_VISABILITY_ATTRIBUTE bool create_wallet(char *path, char *password, char *language, int32_t networkType, char *error) { Monero::NetworkType _networkType = static_cast(networkType); Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager(); Monero::Wallet *wallet = walletManager->createWallet(path, password, language, _networkType); int status; std::string errorString; wallet->statusWithErrorString(status, errorString); if (wallet->status() != Monero::Wallet::Status_Ok) { error = strdup(wallet->errorString().c_str()); return false; } change_current_wallet(wallet); return true; } FUNCTION_VISABILITY_ATTRIBUTE bool restore_wallet_from_seed(char *path, char *password, char *seed, int32_t networkType, uint64_t restoreHeight, char *error) { Monero::NetworkType _networkType = static_cast(networkType); Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->recoveryWallet( std::string(path), std::string(password), std::string(seed), _networkType, (uint64_t)restoreHeight); int status; std::string errorString; wallet->statusWithErrorString(status, errorString); if (status != Monero::Wallet::Status_Ok || !errorString.empty()) { error = strdup(errorString.c_str()); return false; } change_current_wallet(wallet); return true; } FUNCTION_VISABILITY_ATTRIBUTE bool restore_wallet_from_keys(char *path, char *password, char *language, char *address, char *viewKey, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error) { Monero::NetworkType _networkType = static_cast(networkType); Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->createWalletFromKeys( std::string(path), std::string(password), std::string(language), _networkType, (uint64_t)restoreHeight, std::string(address), std::string(viewKey), std::string(spendKey)); int status; std::string errorString; wallet->statusWithErrorString(status, errorString); if (status != Monero::Wallet::Status_Ok || !errorString.empty()) { error = strdup(errorString.c_str()); return false; } change_current_wallet(wallet); return true; } FUNCTION_VISABILITY_ATTRIBUTE bool load_wallet(char *path, char *password, int32_t nettype) { (void) nice(19); Monero::NetworkType networkType = static_cast(nettype); Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager(); Monero::Wallet *wallet = walletManager->openWallet(std::string(path), std::string(password), networkType); int status; std::string errorString; wallet->statusWithErrorString(status, errorString); change_current_wallet(wallet); return !(status != Monero::Wallet::Status_Ok || !errorString.empty()); } FUNCTION_VISABILITY_ATTRIBUTE char *error_string() { return strdup(get_current_wallet()->errorString().c_str()); } FUNCTION_VISABILITY_ATTRIBUTE bool is_wallet_exist(char *path) { return Monero::WalletManagerFactory::getWalletManager()->walletExists(std::string(path)); } FUNCTION_VISABILITY_ATTRIBUTE void close_current_wallet() { Monero::WalletManagerFactory::getWalletManager()->closeWallet(get_current_wallet()); change_current_wallet(nullptr); } FUNCTION_VISABILITY_ATTRIBUTE char *get_filename() { return strdup(get_current_wallet()->filename().c_str()); } FUNCTION_VISABILITY_ATTRIBUTE char *secret_view_key() { return strdup(get_current_wallet()->secretViewKey().c_str()); } FUNCTION_VISABILITY_ATTRIBUTE char *public_view_key() { return strdup(get_current_wallet()->publicViewKey().c_str()); } FUNCTION_VISABILITY_ATTRIBUTE char *secret_spend_key() { return strdup(get_current_wallet()->secretSpendKey().c_str()); } FUNCTION_VISABILITY_ATTRIBUTE char *public_spend_key() { return strdup(get_current_wallet()->publicSpendKey().c_str()); } FUNCTION_VISABILITY_ATTRIBUTE char *get_address(uint32_t account_index, uint32_t address_index) { return strdup(get_current_wallet()->address(account_index, address_index).c_str()); } FUNCTION_VISABILITY_ATTRIBUTE const char *seed() { return strdup(get_current_wallet()->seed().c_str()); } FUNCTION_VISABILITY_ATTRIBUTE uint64_t get_full_balance(uint32_t account_index) { return get_current_wallet()->balance(account_index); } FUNCTION_VISABILITY_ATTRIBUTE uint64_t get_unlocked_balance(uint32_t account_index) { return get_current_wallet()->unlockedBalance(account_index); } FUNCTION_VISABILITY_ATTRIBUTE uint64_t get_current_height() { return get_current_wallet()->blockChainHeight(); } FUNCTION_VISABILITY_ATTRIBUTE uint64_t get_node_height() { return get_current_wallet()->daemonBlockChainHeight(); } FUNCTION_VISABILITY_ATTRIBUTE bool connect_to_node(char *error) { (void) nice(19); bool is_connected = get_current_wallet()->connectToDaemon(); if (!is_connected) { error = strdup(get_current_wallet()->errorString().c_str()); } return is_connected; } FUNCTION_VISABILITY_ATTRIBUTE bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *socksProxyAddress, char *error) { (void) nice(19); Monero::Wallet *wallet = get_current_wallet(); std::string _login = ""; std::string _password = ""; std::string _socksProxyAddress = ""; if (login != nullptr) { _login = std::string(login); } if (password != nullptr) { _password = std::string(password); } if (socksProxyAddress != nullptr) { _socksProxyAddress = std::string(socksProxyAddress); } bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet, _socksProxyAddress); if (!inited) { error = strdup(wallet->errorString().c_str()); } else if (!wallet->connectToDaemon()) { error = strdup(wallet->errorString().c_str()); } return inited; } FUNCTION_VISABILITY_ATTRIBUTE bool is_connected() { return get_current_wallet()->connected(); } FUNCTION_VISABILITY_ATTRIBUTE void start_refresh() { get_current_wallet()->refreshAsync(); get_current_wallet()->startRefresh(); } FUNCTION_VISABILITY_ATTRIBUTE void set_refresh_from_block_height(uint64_t height) { get_current_wallet()->setRefreshFromBlockHeight(height); } FUNCTION_VISABILITY_ATTRIBUTE void set_recovering_from_seed(bool is_recovery) { get_current_wallet()->setRecoveringFromSeed(is_recovery); } FUNCTION_VISABILITY_ATTRIBUTE void store(char *path) { store_lock.lock(); if (is_storing) { return; } is_storing = true; get_current_wallet()->store(std::string(path)); is_storing = false; store_lock.unlock(); } FUNCTION_VISABILITY_ATTRIBUTE bool set_password(char *password, Utf8Box &error) { bool is_changed = get_current_wallet()->setPassword(std::string(password)); if (!is_changed) { error = Utf8Box(strdup(get_current_wallet()->errorString().c_str())); } return is_changed; } FUNCTION_VISABILITY_ATTRIBUTE bool transaction_create(char *address, char *payment_id, char *amount, uint8_t priority_raw, uint32_t subaddr_account, char **preferred_inputs, uint32_t preferred_inputs_size, Utf8Box &error, PendingTransactionRaw &pendingTransaction) { (void) nice(19); std::set _preferred_inputs; for (int i = 0; i < preferred_inputs_size; i++) { _preferred_inputs.insert(std::string(*preferred_inputs)); preferred_inputs++; } auto priority = static_cast(priority_raw); std::string _payment_id; Monero::PendingTransaction *transaction; if (payment_id != nullptr) { _payment_id = std::string(payment_id); } if (amount != nullptr) { uint64_t _amount = Monero::Wallet::amountFromString(std::string(amount)); transaction = m_wallet->createTransaction(std::string(address), _payment_id, _amount, m_wallet->defaultMixin(), priority, subaddr_account, {}, _preferred_inputs); } else { transaction = m_wallet->createTransaction(std::string(address), _payment_id, Monero::optional(), m_wallet->defaultMixin(), priority, subaddr_account, {}, _preferred_inputs); } int status = transaction->status(); if (status == Monero::PendingTransaction::Status::Status_Error || status == Monero::PendingTransaction::Status::Status_Critical) { error = Utf8Box(strdup(transaction->errorString().c_str())); return false; } if (m_listener != nullptr) { m_listener->m_new_transaction = true; } pendingTransaction = PendingTransactionRaw(transaction); return true; } FUNCTION_VISABILITY_ATTRIBUTE bool transaction_create_mult_dest(char **addresses, char *payment_id, char **amounts, uint32_t size, uint8_t priority_raw, uint32_t subaddr_account, char **preferred_inputs, uint32_t preferred_inputs_size, Utf8Box &error, PendingTransactionRaw &pendingTransaction) { (void) nice(19); std::vector _addresses; std::vector _amounts; for (int i = 0; i < size; i++) { _addresses.push_back(std::string(*addresses)); _amounts.push_back(Monero::Wallet::amountFromString(std::string(*amounts))); addresses++; amounts++; } std::set _preferred_inputs; for (int i = 0; i < preferred_inputs_size; i++) { _preferred_inputs.insert(std::string(*preferred_inputs)); preferred_inputs++; } auto priority = static_cast(priority_raw); std::string _payment_id; Monero::PendingTransaction *transaction; if (payment_id != nullptr) { _payment_id = std::string(payment_id); } transaction = m_wallet->createTransactionMultDest(_addresses, _payment_id, _amounts, m_wallet->defaultMixin(), priority, subaddr_account); int status = transaction->status(); if (status == Monero::PendingTransaction::Status::Status_Error || status == Monero::PendingTransaction::Status::Status_Critical) { error = Utf8Box(strdup(transaction->errorString().c_str())); return false; } if (m_listener != nullptr) { m_listener->m_new_transaction = true; } pendingTransaction = PendingTransactionRaw(transaction); return true; } FUNCTION_VISABILITY_ATTRIBUTE bool transaction_commit(PendingTransactionRaw *transaction, Utf8Box &error) { bool committed = transaction->transaction->commit(); if (!committed) { error = Utf8Box(strdup(transaction->transaction->errorString().c_str())); } else if (m_listener != nullptr) { m_listener->m_new_transaction = true; } return committed; } FUNCTION_VISABILITY_ATTRIBUTE uint64_t get_node_height_or_update(uint64_t base_eight) { if (m_cached_syncing_blockchain_height < base_eight) { m_cached_syncing_blockchain_height = base_eight; } return m_cached_syncing_blockchain_height; } FUNCTION_VISABILITY_ATTRIBUTE uint64_t get_syncing_height() { if (m_listener == nullptr) { return 0; } uint64_t height = m_listener->height(); if (height <= 1) { return 0; } if (height != m_last_known_wallet_height) { m_last_known_wallet_height = height; } return height; } FUNCTION_VISABILITY_ATTRIBUTE uint64_t is_needed_to_refresh() { if (m_listener == nullptr) { return false; } bool should_refresh = m_listener->isNeedToRefresh(); if (should_refresh) { m_listener->resetNeedToRefresh(); } return should_refresh; } FUNCTION_VISABILITY_ATTRIBUTE uint8_t is_new_transaction_exist() { if (m_listener == nullptr) { return false; } bool is_new_transaction_exist = m_listener->isNewTransactionExist(); if (is_new_transaction_exist) { m_listener->resetIsNewTransactionExist(); } return is_new_transaction_exist; } FUNCTION_VISABILITY_ATTRIBUTE void set_listener() { m_last_known_wallet_height = 0; if (m_listener != nullptr) { free(m_listener); } m_listener = new MoneroWalletListener(); get_current_wallet()->setListener(m_listener); } FUNCTION_VISABILITY_ATTRIBUTE int64_t *subaddrress_get_all() { std::vector _subaddresses = m_subaddress->getAll(); size_t size = _subaddresses.size(); int64_t *subaddresses = (int64_t *)malloc(size * sizeof(int64_t)); for (int i = 0; i < size; i++) { Monero::SubaddressRow *row = _subaddresses[i]; SubaddressRow *_row = new SubaddressRow(row->getRowId(), strdup(row->getAddress().c_str()), strdup(row->getLabel().c_str())); subaddresses[i] = reinterpret_cast(_row); } return subaddresses; } FUNCTION_VISABILITY_ATTRIBUTE int32_t subaddrress_size() { std::vector _subaddresses = m_subaddress->getAll(); return _subaddresses.size(); } FUNCTION_VISABILITY_ATTRIBUTE void subaddress_add_row(uint32_t accountIndex, char *label) { m_subaddress->addRow(accountIndex, std::string(label)); } FUNCTION_VISABILITY_ATTRIBUTE void subaddress_set_label(uint32_t accountIndex, uint32_t addressIndex, char *label) { m_subaddress->setLabel(accountIndex, addressIndex, std::string(label)); } FUNCTION_VISABILITY_ATTRIBUTE void subaddress_refresh(uint32_t accountIndex) { m_subaddress->refresh(accountIndex); } FUNCTION_VISABILITY_ATTRIBUTE int32_t account_size() { std::vector _accocunts = m_account->getAll(); return _accocunts.size(); } FUNCTION_VISABILITY_ATTRIBUTE int64_t *account_get_all() { std::vector _accocunts = m_account->getAll(); size_t size = _accocunts.size(); int64_t *accocunts = (int64_t *)malloc(size * sizeof(int64_t)); for (int i = 0; i < size; i++) { Monero::SubaddressAccountRow *row = _accocunts[i]; AccountRow *_row = new AccountRow(row->getRowId(), strdup(row->getLabel().c_str())); accocunts[i] = reinterpret_cast(_row); } return accocunts; } FUNCTION_VISABILITY_ATTRIBUTE void account_add_row(char *label) { m_account->addRow(std::string(label)); } FUNCTION_VISABILITY_ATTRIBUTE void account_set_label_row(uint32_t account_index, char *label) { m_account->setLabel(account_index, label); } FUNCTION_VISABILITY_ATTRIBUTE void account_refresh() { m_account->refresh(); } FUNCTION_VISABILITY_ATTRIBUTE int64_t *transactions_get_all() { std::vector transactions = m_transaction_history->getAll(); size_t size = transactions.size(); int64_t *transactionAddresses = (int64_t *)malloc(size * sizeof(int64_t)); for (int i = 0; i < size; i++) { Monero::TransactionInfo *row = transactions[i]; TransactionInfoRow *tx = new TransactionInfoRow(row); transactionAddresses[i] = reinterpret_cast(tx); } return transactionAddresses; } FUNCTION_VISABILITY_ATTRIBUTE void transactions_refresh() { m_transaction_history->refresh(); } FUNCTION_VISABILITY_ATTRIBUTE int64_t transactions_count() { return m_transaction_history->count(); } FUNCTION_VISABILITY_ATTRIBUTE int LedgerExchange( unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len) { return -1; } FUNCTION_VISABILITY_ATTRIBUTE int LedgerFind(char *buffer, size_t len) { return -1; } FUNCTION_VISABILITY_ATTRIBUTE void on_startup() { Monero::Utils::onStartup(); Monero::WalletManagerFactory::setLogLevel(0); } FUNCTION_VISABILITY_ATTRIBUTE void rescan_blockchain() { m_wallet->rescanBlockchainAsync(); } FUNCTION_VISABILITY_ATTRIBUTE char * get_tx_key(char * txId) { return strdup(m_wallet->getTxKey(std::string(txId)).c_str()); } FUNCTION_VISABILITY_ATTRIBUTE char *get_subaddress_label(uint32_t accountIndex, uint32_t addressIndex) { return strdup(get_current_wallet()->getSubaddressLabel(accountIndex, addressIndex).c_str()); } FUNCTION_VISABILITY_ATTRIBUTE void set_trusted_daemon(bool arg) { m_wallet->setTrustedDaemon(arg); } FUNCTION_VISABILITY_ATTRIBUTE bool trusted_daemon() { return m_wallet->trustedDaemon(); } FUNCTION_VISABILITY_ATTRIBUTE CoinsInfoRow* coin(int index) { if (index >= 0 && index < m_coins_info.size()) { std::list::iterator it = m_coins_info.begin(); std::advance(it, index); Monero::CoinsInfo* element = *it; std::cout << "Element at index " << index << ": " << element << std::endl; return new CoinsInfoRow(element); } else { std::cout << "Invalid index." << std::endl; return nullptr; // Return a default value (nullptr) for invalid index } } FUNCTION_VISABILITY_ATTRIBUTE void refresh_coins(uint32_t accountIndex) { m_coins_info.clear(); m_coins->refresh(); for (const auto i : m_coins->getAll()) { if (i->subaddrAccount() == accountIndex && !(i->spent())) { m_coins_info.push_back(i); } } } FUNCTION_VISABILITY_ATTRIBUTE uint64_t coins_count() { return m_coins_info.size(); } CoinsInfoRow** coins_from_account(uint32_t accountIndex) { std::vector matchingCoins; for (int i = 0; i < coins_count(); i++) { CoinsInfoRow* coinInfo = coin(i); if (coinInfo->subaddrAccount == accountIndex) { matchingCoins.push_back(coinInfo); } } CoinsInfoRow** result = new CoinsInfoRow*[matchingCoins.size()]; std::copy(matchingCoins.begin(), matchingCoins.end(), result); return result; } CoinsInfoRow** coins_from_txid(const char* txid, size_t* count) { std::vector matchingCoins; for (int i = 0; i < coins_count(); i++) { CoinsInfoRow* coinInfo = coin(i); if (std::string(coinInfo->hash) == txid) { matchingCoins.push_back(coinInfo); } } *count = matchingCoins.size(); CoinsInfoRow** result = new CoinsInfoRow*[*count]; std::copy(matchingCoins.begin(), matchingCoins.end(), result); return result; } CoinsInfoRow** coins_from_key_image(const char** keyimages, size_t keyimageCount, size_t* count) { std::vector matchingCoins; for (int i = 0; i < coins_count(); i++) { CoinsInfoRow* coinsInfoRow = coin(i); for (size_t j = 0; j < keyimageCount; j++) { if (coinsInfoRow->keyImageKnown && std::string(coinsInfoRow->keyImage) == keyimages[j]) { matchingCoins.push_back(coinsInfoRow); break; } } } *count = matchingCoins.size(); CoinsInfoRow** result = new CoinsInfoRow*[*count]; std::copy(matchingCoins.begin(), matchingCoins.end(), result); return result; } #ifdef __cplusplus } #endif