From 97fd29a4aa1abd52d1937e99faf75cf1598086c4 Mon Sep 17 00:00:00 2001 From: Dorukhan Tokay Date: Sat, 8 May 2021 07:04:41 +0300 Subject: [PATCH] fee work, locks for utxo and mempool maps, cleanups and minor fixes --- README.md | 1 - tiny-lib/Chain.cpp | 46 ++++++++------- tiny-lib/ECDSA.cpp | 24 ++++---- tiny-lib/Mempool.cpp | 32 +++++++++-- tiny-lib/Mempool.hpp | 4 +- tiny-lib/NetClient.cpp | 6 +- tiny-lib/PoW.cpp | 57 +++++++++---------- tiny-lib/PoW.hpp | 6 +- tiny-lib/RIPEMD160.cpp | 2 +- tiny-lib/SHA256.cpp | 2 +- tiny-lib/SendMempoolMsg.cpp | 10 +++- tiny-lib/SendUTXOsMsg.cpp | 12 ++-- tiny-lib/Tx.cpp | 104 +++++++++++++++++----------------- tiny-lib/Tx.hpp | 10 ++-- tiny-lib/TxInfoMsg.cpp | 2 +- tiny-lib/UnspentTxOut.cpp | 66 ++++++++++++++++++--- tiny-lib/UnspentTxOut.hpp | 11 +++- tiny-lib/Utils.cpp | 2 +- tiny-lib/Wallet.cpp | 88 ++++++++++++++++++---------- tiny-lib/Wallet.hpp | 15 ++--- tiny-sandbox/main.cpp | 17 ++++-- tiny-test/BlockChainTests.cpp | 26 ++++----- 22 files changed, 335 insertions(+), 208 deletions(-) diff --git a/README.md b/README.md index a0f6a77..8a3eee0 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,6 @@ Windows 10 with Visual Studio 2019 for build environment and vcpkg for library m - Replace by fee - Transaction locking -- Mempool sorting by fee - Orphan blocks - Chainwork - Peer discovery diff --git a/tiny-lib/Chain.cpp b/tiny-lib/Chain.cpp index ffc9ef6..d32d935 100644 --- a/tiny-lib/Chain.cpp +++ b/tiny-lib/Chain.cpp @@ -54,7 +54,7 @@ int64_t Chain::GetMedianTimePast(uint32_t numLastBlocks) return 0; const uint32_t first_idx = ActiveChain.size() - numLastBlocks; - uint32_t median_idx = first_idx + (numLastBlocks / 2); + uint32_t median_idx = first_idx + numLastBlocks / 2; if (numLastBlocks % 2 == 0) median_idx -= 1; @@ -68,10 +68,10 @@ uint32_t Chain::ValidateBlock(const std::shared_ptr& block) const auto& txs = block->Txs; if (txs.empty()) - throw BlockValidationException("Transactions are empty"); + throw BlockValidationException("Transactions empty"); if (block->Timestamp - Utils::GetUnixTimestamp() > static_cast(NetParams::MAX_FUTURE_BLOCK_TIME_IN_SECS)) - throw BlockValidationException("Block timestamp is too far in future"); + throw BlockValidationException("Block timestamp too far in future"); BIGNUM* target_bn = HashChecker::TargetBitsToBN(block->Bits); if (!HashChecker::IsValid(block->Id(), target_bn)) @@ -98,17 +98,17 @@ uint32_t Chain::ValidateBlock(const std::shared_ptr& block) } catch (const TxValidationException&) { - LOG_ERROR("Transaction {} in block {} failed to validate", txs[i]->Id(), block->Id()); + LOG_ERROR("Transaction {} in block {} failed validation", txs[i]->Id(), block->Id()); - throw BlockValidationException(fmt::format("Transaction {} is invalid", txs[i]->Id()).c_str()); + throw BlockValidationException(fmt::format("Transaction {} invalid", txs[i]->Id()).c_str()); } } if (MerkleTree::GetRootOfTxs(txs)->Value != block->MerkleHash) - throw BlockValidationException("Merkle hash is invalid"); + throw BlockValidationException("Merkle hash invalid"); if (block->Timestamp <= GetMedianTimePast(11)) - throw BlockValidationException("Timestamp is too old"); + throw BlockValidationException("Timestamp too old"); uint32_t prev_block_chain_idx; if (block->PrevBlockHash.empty()) @@ -120,7 +120,7 @@ uint32_t Chain::ValidateBlock(const std::shared_ptr& block) auto [prev_block, prev_block_height, prev_block_chain_idx2] = LocateBlockInAllChains(block->PrevBlockHash); if (prev_block == nullptr) throw BlockValidationException( - fmt::format("Previous block {} is not found in any chain", block->PrevBlockHash).c_str(), block); + fmt::format("Previous block {} not found in any chain", block->PrevBlockHash).c_str(), block); if (prev_block_chain_idx2 != ActiveChainIdx) return prev_block_chain_idx2; @@ -131,7 +131,7 @@ uint32_t Chain::ValidateBlock(const std::shared_ptr& block) } if (PoW::GetNextWorkRequired(block->PrevBlockHash) != block->Bits) - throw BlockValidationException("Bits are incorrect"); + throw BlockValidationException("Bits incorrect"); std::vector nonCoinbaseTxs(block->Txs.begin() + 1, block->Txs.end()); Tx::ValidateRequest req; @@ -175,7 +175,7 @@ int64_t Chain::ConnectBlock(const std::shared_ptr& block, bool doingReorg } if (located_block != nullptr) { - LOG_TRACE("Ignore already seen block {}", blockId); + LOG_INFO("Ignore already seen block {}", blockId); return -1; } @@ -190,7 +190,7 @@ int64_t Chain::ConnectBlock(const std::shared_ptr& block, bool doingReorg LOG_ERROR("Block {} failed validation", blockId); if (ex.ToOrphan != nullptr) { - LOG_INFO("Saw orphan block {}", blockId); + LOG_INFO("Found orphan block {}", blockId); OrphanBlocks.push_back(ex.ToOrphan); } @@ -200,7 +200,7 @@ int64_t Chain::ConnectBlock(const std::shared_ptr& block, bool doingReorg if (chainIdx != ActiveChainIdx && SideBranches.size() < chainIdx) { - LOG_INFO("Creating a new side branch {} for block {}", chainIdx, blockId); + LOG_INFO("Creating a new side branch with idx {} for block {}", chainIdx, blockId); SideBranches.emplace_back(); } @@ -216,8 +216,12 @@ int64_t Chain::ConnectBlock(const std::shared_ptr& block, bool doingReorg { const auto txId = tx->Id(); - if (Mempool::Map.contains(txId)) - Mempool::Map.erase(txId); + { + std::lock_guard lock_mempool(Mempool::Mutex); + + if (Mempool::Map.contains(txId)) + Mempool::Map.erase(txId); + } if (!tx->IsCoinbase()) { @@ -233,11 +237,11 @@ int64_t Chain::ConnectBlock(const std::shared_ptr& block, bool doingReorg } } - if ((!doingReorg && ReorgIfNecessary()) || chainIdx == ActiveChainIdx) + if (!doingReorg && ReorgIfNecessary() || chainIdx == ActiveChainIdx) { PoW::MineInterrupt = true; - LOG_INFO("Block accepted with height {} and txs {}", ActiveChain.size() - 1, block->Txs.size()); + LOG_INFO("Block accepted at height {} with {} txs", ActiveChain.size() - 1, block->Txs.size()); } NetClient::SendMsgRandom(BlockInfoMsg(block)); @@ -259,7 +263,11 @@ std::shared_ptr Chain::DisconnectBlock(const std::shared_ptr& bloc { const auto txId = tx->Id(); - Mempool::Map[txId] = tx; + { + std::lock_guard lock_mempool(Mempool::Mutex); + + Mempool::Map[txId] = tx; + } for (const auto& txIn : tx->TxIns) { @@ -349,7 +357,7 @@ bool Chain::TryReorg(const std::vector>& branch, uint32_t SideBranches.erase(SideBranches.begin() + branchIdx - 1); SideBranches.push_back(oldActiveChain); - LOG_INFO("Chain reorg -> new height {}, tip {}", ActiveChain.size(), ActiveChain.back()->Id()); + LOG_INFO("Chain reorganized, new height {} with tip {}", ActiveChain.size(), ActiveChain.back()->Id()); return true; } @@ -359,7 +367,7 @@ void Chain::RollbackReorg(const std::vector>& oldActiveCh { std::lock_guard lock(Mutex); - LOG_INFO("Reorg of idx {} to active chain failed", branchIdx); + LOG_ERROR("Reorg of idx {} to active chain failed", branchIdx); DisconnectToFork(forkBlock); diff --git a/tiny-lib/ECDSA.cpp b/tiny-lib/ECDSA.cpp index 489276d..96985ed 100644 --- a/tiny-lib/ECDSA.cpp +++ b/tiny-lib/ECDSA.cpp @@ -12,7 +12,7 @@ std::pair, std::vector> ECDSA::Generate() { EC_KEY_free(ec_key); - return {std::vector(), std::vector()}; + return {{}, {}}; } const BIGNUM* priv_key = EC_KEY_get0_private_key(ec_key); @@ -21,7 +21,7 @@ std::pair, std::vector> ECDSA::Generate() { EC_KEY_free(ec_key); - return {std::vector(), std::vector()}; + return {{}, {}}; } const EC_GROUP* ec_group = EC_KEY_get0_group(ec_key); @@ -30,7 +30,7 @@ std::pair, std::vector> ECDSA::Generate() { EC_KEY_free(ec_key); - return {std::vector(), std::vector()}; + return {{}, {}}; } EC_KEY_free(ec_key); @@ -46,7 +46,7 @@ std::vector ECDSA::GetPubKeyFromPrivKey(const std::vector& pri { EC_KEY_free(ec_key); - return std::vector(); + return {}; } const EC_GROUP* ec_group = EC_KEY_get0_group(ec_key); @@ -55,7 +55,7 @@ std::vector ECDSA::GetPubKeyFromPrivKey(const std::vector& pri { EC_KEY_free(ec_key); - return std::vector(); + return {}; } EC_KEY_free(ec_key); @@ -71,7 +71,7 @@ std::vector ECDSA::SignMsg(const std::vector& msg, const std:: { EC_KEY_free(ec_key); - return std::vector(); + return {}; } std::vector sig(ECDSA_size(ec_key)); @@ -80,7 +80,7 @@ std::vector ECDSA::SignMsg(const std::vector& msg, const std:: { EC_KEY_free(ec_key); - return std::vector(); + return {}; } sig.resize(siglen); @@ -179,7 +179,7 @@ std::vector ECDSA::GetPubKeyFromPrivKey(EC_KEY* ec_key, const EC_GROUP* { BN_CTX_free(bn_ctx); - return std::vector(); + return {}; } if (!EC_KEY_set_public_key(ec_key, pub_key2)) { @@ -187,7 +187,7 @@ std::vector ECDSA::GetPubKeyFromPrivKey(EC_KEY* ec_key, const EC_GROUP* EC_POINT_free(pub_key2); - return std::vector(); + return {}; } if (!EC_KEY_check_key(ec_key)) { @@ -195,7 +195,7 @@ std::vector ECDSA::GetPubKeyFromPrivKey(EC_KEY* ec_key, const EC_GROUP* EC_POINT_free(pub_key2); - return std::vector(); + return {}; } pub_key = pub_key2; } @@ -206,7 +206,7 @@ std::vector ECDSA::GetPubKeyFromPrivKey(EC_KEY* ec_key, const EC_GROUP* BN_free(pub_key_bn); BN_CTX_free(bn_ctx); - return std::vector(); + return {}; } std::vector pub_key_buffer(BN_num_bytes(pub_key_bn)); if (!BN_bn2bin(pub_key_bn, pub_key_buffer.data())) @@ -214,7 +214,7 @@ std::vector ECDSA::GetPubKeyFromPrivKey(EC_KEY* ec_key, const EC_GROUP* BN_free(pub_key_bn); BN_CTX_free(bn_ctx); - return std::vector(); + return {}; } BN_free(pub_key_bn); BN_CTX_free(bn_ctx); diff --git a/tiny-lib/Mempool.cpp b/tiny-lib/Mempool.cpp index eca5103..d3a376c 100644 --- a/tiny-lib/Mempool.cpp +++ b/tiny-lib/Mempool.cpp @@ -2,27 +2,33 @@ #include "Mempool.hpp" #include +#include #include "BinaryBuffer.hpp" #include "Exceptions.hpp" #include "Log.hpp" #include "NetClient.hpp" #include "NetParams.hpp" +#include "PoW.hpp" #include "TxInfoMsg.hpp" std::unordered_map> Mempool::Map; std::vector> Mempool::OrphanedTxs; +std::recursive_mutex Mempool::Mutex; + std::shared_ptr Mempool::Find_UTXO_InMempool(const std::shared_ptr& txOutPoint) { + std::lock_guard lock(Mutex); + if (!Map.contains(txOutPoint->TxId)) return nullptr; const auto& tx = Map[txOutPoint->TxId]; if (tx->TxOuts.size() - 1 < txOutPoint->TxOutIdx) { - LOG_TRACE("Couldn't find UTXO in mempool for {}", txOutPoint->TxId); + LOG_ERROR("Unable to find UTXO in mempool for {}", txOutPoint->TxId); return nullptr; } @@ -34,10 +40,22 @@ std::shared_ptr Mempool::Find_UTXO_InMempool(const std::shared_ptr Mempool::SelectFromMempool(const std::shared_ptr& block) { + std::lock_guard lock(Mutex); + auto newBlock = std::make_shared(*block); + std::vector>> map_vector(Map.begin(), Map.end()); + std::ranges::sort(map_vector, + [](const std::pair>& a, + const std::pair>& b) -> bool + { + const auto& [a_txId, a_tx] = a; + const auto& [b_txId, b_tx] = b; + return PoW::CalculateFees(a_tx) < PoW::CalculateFees(b_tx); + }); + std::set addedToBlock; - for (const auto& txId : Map | std::ranges::views::keys) + for (const auto& txId : map_vector | std::views::keys) newBlock = TryAddToBlock(newBlock, txId, addedToBlock); return newBlock; @@ -45,6 +63,8 @@ std::shared_ptr Mempool::SelectFromMempool(const std::shared_ptr& void Mempool::AddTxToMempool(const std::shared_ptr& tx) { + std::lock_guard lock(Mutex); + const auto txId = tx->Id(); if (Map.contains(txId)) { @@ -74,7 +94,7 @@ void Mempool::AddTxToMempool(const std::shared_ptr& tx) Map[txId] = tx; - LOG_INFO("Transaction {} added to mempool", txId); + LOG_TRACE("Transaction {} added to mempool", txId); NetClient::SendMsgRandom(TxInfoMsg(tx)); } @@ -87,6 +107,8 @@ bool Mempool::CheckBlockSize(const std::shared_ptr& block) std::shared_ptr Mempool::TryAddToBlock(std::shared_ptr& block, const std::string& txId, std::set& addedToBlock) { + std::lock_guard lock(Mutex); + if (addedToBlock.contains(txId)) return block; @@ -105,7 +127,7 @@ std::shared_ptr Mempool::TryAddToBlock(std::shared_ptr& block, con const auto& inMempool = Find_UTXO_InMempool(toSpend); if (inMempool == nullptr) { - LOG_TRACE("Couldn't find UTXO for {}", txIn->ToSpend->TxId); + LOG_ERROR("Unable to find UTXO for {}", txIn->ToSpend->TxId); return nullptr; } @@ -113,7 +135,7 @@ std::shared_ptr Mempool::TryAddToBlock(std::shared_ptr& block, con block = TryAddToBlock(block, inMempool->TxOutPoint->TxId, addedToBlock); if (block == nullptr) { - LOG_TRACE("Couldn't add parent"); + LOG_ERROR("Unable to add parent"); return nullptr; } diff --git a/tiny-lib/Mempool.hpp b/tiny-lib/Mempool.hpp index c01e009..82ce747 100644 --- a/tiny-lib/Mempool.hpp +++ b/tiny-lib/Mempool.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -13,11 +14,12 @@ class Mempool { public: - //TODO: map mutex? static std::unordered_map> Map; static std::vector> OrphanedTxs; + static std::recursive_mutex Mutex; + static std::shared_ptr Find_UTXO_InMempool(const std::shared_ptr& txOutPoint); static std::shared_ptr SelectFromMempool(const std::shared_ptr& block); diff --git a/tiny-lib/NetClient.cpp b/tiny-lib/NetClient.cpp index cbe07e8..f70d9b4 100644 --- a/tiny-lib/NetClient.cpp +++ b/tiny-lib/NetClient.cpp @@ -168,7 +168,7 @@ void NetClient::HandleAccept(std::shared_ptr& con, const boost::syst { auto& soc = con->Socket; - LOG_INFO("Incoming connection from {}:{}", soc.remote_endpoint().address().to_string(), + LOG_TRACE("Incoming connection from {}:{}", soc.remote_endpoint().address().to_string(), soc.remote_endpoint().port()); soc.set_option(boost::asio::ip::tcp::no_delay(true)); @@ -203,7 +203,7 @@ void NetClient::HandleRead(std::shared_ptr& con, const boost::system if (bytes_transferred > Magic.size() && readBuffer.size() >= bytes_transferred) { BinaryBuffer buffer; - buffer.GrowTo(bytes_transferred - (Magic.size())); + buffer.GrowTo(bytes_transferred - Magic.size()); boost::asio::buffer_copy(boost::asio::buffer(buffer.GetWritableBuffer()), readBuffer.data()); HandleMsg(con, buffer); @@ -371,7 +371,7 @@ void NetClient::RemoveConnection(std::shared_ptr& con) if (soc.is_open()) { - LOG_INFO("Peer {}:{} disconnected", soc.remote_endpoint().address().to_string(), + LOG_TRACE("Peer {}:{} disconnected", soc.remote_endpoint().address().to_string(), soc.remote_endpoint().port()); soc.shutdown(boost::asio::socket_base::shutdown_both); diff --git a/tiny-lib/PoW.cpp b/tiny-lib/PoW.cpp index 6c19df4..2d34266 100644 --- a/tiny-lib/PoW.cpp +++ b/tiny-lib/PoW.cpp @@ -45,7 +45,7 @@ uint8_t PoW::GetNextWorkRequired(const std::string& prevBlockHash) std::shared_ptr PoW::AssembleAndSolveBlock(const std::string& payCoinbaseToAddress) { - return AssembleAndSolveBlock(payCoinbaseToAddress, std::vector>()); + return AssembleAndSolveBlock(payCoinbaseToAddress, {}); } std::shared_ptr PoW::AssembleAndSolveBlock(const std::string& payCoinbaseToAddress, @@ -61,7 +61,7 @@ std::shared_ptr PoW::AssembleAndSolveBlock(const std::string& payCoinbase if (block->Txs.empty()) block = Mempool::SelectFromMempool(block); - const auto fees = CalculateFees(block); + const uint64_t fees = CalculateFees(block); const auto coinbaseTx = Tx::CreateCoinbase(payCoinbaseToAddress, GetBlockSubsidy() + fees, Chain::ActiveChain.size()); block->Txs.insert(block->Txs.begin(), coinbaseTx); @@ -70,22 +70,11 @@ std::shared_ptr PoW::AssembleAndSolveBlock(const std::string& payCoinbase if (block->Serialize().GetSize() > NetParams::MAX_BLOCK_SERIALIZED_SIZE_IN_BYTES) throw std::exception("Transactions specified create a block too large"); + LOG_INFO("Start mining block {} with {} fees", block->Id(), fees); + return Mine(block); } -std::shared_ptr PoW::UTXO_FromBlock(const std::shared_ptr& block, const std::shared_ptr& txIn) -{ - for (const auto& tx : block->Txs) - { - if (tx->Id() == txIn->ToSpend->TxId) - { - return tx->TxOuts[txIn->ToSpend->TxOutIdx]; - } - } - - return nullptr; -} - std::shared_ptr PoW::Mine(const std::shared_ptr& block) { if (MineInterrupt) @@ -133,7 +122,7 @@ std::shared_ptr PoW::Mine(const std::shared_ptr& block) auto duration = Utils::GetUnixTimestamp() - start; if (duration == 0) duration = 1; - auto khs = (hash_count / duration) / 1000; + auto khs = hash_count / duration / 1000; LOG_INFO("Block found => {} s, {} KH/s, {}, {}", duration, khs, newBlock->Id(), newBlock->Nonce); return newBlock; @@ -176,6 +165,27 @@ void PoW::MineForever() } } +uint64_t PoW::CalculateFees(const std::shared_ptr& tx) +{ + uint64_t spent = 0; + for (const auto& txIn : tx->TxIns) + { + const auto utxo = UTXO::FindTxOutInMap(txIn); + if (utxo != nullptr) + { + spent += utxo->Value; + } + } + + uint64_t sent = 0; + for (const auto& txOut : tx->TxOuts) + { + sent += txOut->Value; + } + + return spent - sent; +} + void PoW::MineChunk(const std::shared_ptr& block, BIGNUM* target_bn, uint64_t start, uint64_t chunk_size, std::atomic_bool& found, std::atomic& found_nonce, std::atomic& hash_count) { @@ -196,19 +206,6 @@ void PoW::MineChunk(const std::shared_ptr& block, BIGNUM* target_bn, uint found_nonce = start + i; } -std::shared_ptr PoW::Find_UTXO(const std::shared_ptr& block, const std::shared_ptr& txIn) -{ - for (const auto& utxo : UTXO::Map | std::ranges::views::values) - { - if (txIn->ToSpend->TxId == utxo->TxOutPoint->TxId && txIn->ToSpend->TxOutIdx == utxo->TxOutPoint->TxOutIdx) - { - return utxo->TxOut; - } - } - - return UTXO_FromBlock(block, txIn); -} - uint64_t PoW::CalculateFees(const std::shared_ptr& block) { uint64_t fee = 0; @@ -218,7 +215,7 @@ uint64_t PoW::CalculateFees(const std::shared_ptr& block) uint64_t spent = 0; for (const auto& txIn : tx->TxIns) { - auto utxo = Find_UTXO(block, txIn); + const auto utxo = UTXO::FindTxOutInMapOrBlock(block, txIn); if (utxo != nullptr) { spent += utxo->Value; diff --git a/tiny-lib/PoW.hpp b/tiny-lib/PoW.hpp index 423ab18..e91e84a 100644 --- a/tiny-lib/PoW.hpp +++ b/tiny-lib/PoW.hpp @@ -26,15 +26,13 @@ class PoW static std::shared_ptr Mine(const std::shared_ptr& block); static void MineForever(); + static uint64_t CalculateFees(const std::shared_ptr& tx); + private: static void MineChunk(const std::shared_ptr& block, BIGNUM* target_bn, uint64_t start, uint64_t chunk_size, std::atomic_bool& found, std::atomic& found_nonce, std::atomic& hash_count); - static std::shared_ptr - UTXO_FromBlock(const std::shared_ptr& block, const std::shared_ptr& txIn); - static std::shared_ptr Find_UTXO(const std::shared_ptr& block, const std::shared_ptr& txIn); - static uint64_t CalculateFees(const std::shared_ptr& block); static uint64_t GetBlockSubsidy(); }; diff --git a/tiny-lib/RIPEMD160.cpp b/tiny-lib/RIPEMD160.cpp index 4d55741..3749b29 100644 --- a/tiny-lib/RIPEMD160.cpp +++ b/tiny-lib/RIPEMD160.cpp @@ -11,7 +11,7 @@ std::vector RIPEMD160::HashBinary(const std::vector& buffer) if (!RIPEMD160_Init(&ripemd160) || !RIPEMD160_Update(&ripemd160, buffer.data(), buffer.size()) || !RIPEMD160_Final( hash.data(), &ripemd160)) - return std::vector(); + return {}; return hash; } diff --git a/tiny-lib/SHA256.cpp b/tiny-lib/SHA256.cpp index f37f822..ebfcfbe 100644 --- a/tiny-lib/SHA256.cpp +++ b/tiny-lib/SHA256.cpp @@ -11,7 +11,7 @@ std::vector SHA256::HashBinary(const std::vector& buffer) if (!SHA256_Init(&sha256) || !SHA256_Update(&sha256, buffer.data(), buffer.size()) || !SHA256_Final( hash.data(), &sha256)) - return std::vector(); + return {}; return hash; } diff --git a/tiny-lib/SendMempoolMsg.cpp b/tiny-lib/SendMempoolMsg.cpp index cc4e953..bec1a5c 100644 --- a/tiny-lib/SendMempoolMsg.cpp +++ b/tiny-lib/SendMempoolMsg.cpp @@ -15,10 +15,14 @@ BinaryBuffer SendMempoolMsg::Serialize() const { BinaryBuffer buffer; - buffer.WriteSize(Mempool::Map.size()); - for (const auto& key : Mempool::Map | std::views::keys) { - buffer.Write(key); + std::lock_guard lock(Mempool::Mutex); + + buffer.WriteSize(Mempool::Map.size()); + for (const auto& key : Mempool::Map | std::views::keys) + { + buffer.Write(key); + } } return buffer; diff --git a/tiny-lib/SendUTXOsMsg.cpp b/tiny-lib/SendUTXOsMsg.cpp index bea65b1..fb90a78 100644 --- a/tiny-lib/SendUTXOsMsg.cpp +++ b/tiny-lib/SendUTXOsMsg.cpp @@ -14,11 +14,15 @@ BinaryBuffer SendUTXOsMsg::Serialize() const { BinaryBuffer buffer; - buffer.WriteSize(UTXO::Map.size()); - for (const auto& [key, value] : UTXO::Map) { - buffer.WriteRaw(key->Serialize().GetBuffer()); - buffer.WriteRaw(value->Serialize().GetBuffer()); + std::lock_guard lock(UTXO::Mutex); + + buffer.WriteSize(UTXO::Map.size()); + for (const auto& [key, value] : UTXO::Map) + { + buffer.WriteRaw(key->Serialize().GetBuffer()); + buffer.WriteRaw(value->Serialize().GetBuffer()); + } } return buffer; diff --git a/tiny-lib/Tx.cpp b/tiny-lib/Tx.cpp index 5988a19..cb16ed5 100644 --- a/tiny-lib/Tx.cpp +++ b/tiny-lib/Tx.cpp @@ -45,7 +45,58 @@ void Tx::ValidateBasics(bool coinbase /*= false*/) const totalSpent += tx_out->Value; if (totalSpent > NetParams::MAX_MONEY) - throw TxValidationException("Spend value is too high"); + throw TxValidationException("Spent value too high"); +} + +void Tx::Validate(const ValidateRequest& req) const +{ + ValidateBasics(req.AsCoinbase); + + uint64_t avaliableToSpend = 0; + for (uint32_t i = 0; i < TxIns.size(); i++) + { + const auto& txIn = TxIns[i]; + + auto utxo = UTXO::FindInMap(txIn->ToSpend); + if (utxo == nullptr) + { + if (!req.SiblingsInBlock.empty()) + { + utxo = UTXO::FindInList(txIn, req.SiblingsInBlock); + } + + if (req.Allow_UTXO_FromMempool) + { + utxo = Mempool::Find_UTXO_InMempool(txIn->ToSpend); + } + + if (utxo == nullptr) + throw TxValidationException( + fmt::format("Unable to find any UTXO for TxIn {}, orphaning transaction", i).c_str(), + std::make_shared(*this)); + } + + if (utxo->IsCoinbase && Chain::GetCurrentHeight() - utxo->Height < NetParams::COINBASE_MATURITY) + throw TxValidationException("Coinbase UTXO not ready for spending"); + + try + { + ValidateSignatureForSpend(txIn, utxo); + } + catch (const TxUnlockException&) + { + throw TxValidationException(fmt::format("TxIn not a valid spend of UTXO").c_str()); + } + + avaliableToSpend += utxo->TxOut->Value; + } + + uint64_t totalSpent = 0; + for (const auto& txOut : TxOuts) + totalSpent += txOut->Value; + + if (avaliableToSpend < totalSpent) + throw TxValidationException("Spent value more than available"); } BinaryBuffer Tx::Serialize() const @@ -139,57 +190,6 @@ std::shared_ptr Tx::CreateCoinbase(const std::string& payToAddr, uint64_t va return tx; } -void Tx::Validate(const ValidateRequest& req) const -{ - ValidateBasics(req.AsCoinbase); - - uint64_t avaliableToSpend = 0; - for (uint32_t i = 0; i < TxIns.size(); i++) - { - const auto& txIn = TxIns[i]; - - auto utxo = UTXO::FindInMap(txIn->ToSpend); - if (utxo == nullptr) - { - if (!req.SiblingsInBlock.empty()) - { - utxo = UTXO::FindInList(txIn, req.SiblingsInBlock); - } - - if (req.Allow_UTXO_FromMempool) - { - utxo = Mempool::Find_UTXO_InMempool(txIn->ToSpend); - } - - if (utxo == nullptr) - throw TxValidationException( - fmt::format("Couldn't not find any UTXO for TxIn {}, orphaning transaction", i).c_str(), - std::make_shared(*this)); - } - - if (utxo->IsCoinbase && (Chain::GetCurrentHeight() - utxo->Height) < NetParams::COINBASE_MATURITY) - throw TxValidationException("Coinbase UTXO is not ready for spending"); - - try - { - ValidateSignatureForSpend(txIn, utxo); - } - catch (const TxUnlockException&) - { - throw TxValidationException(fmt::format("TxIn is not a valid spend of UTXO").c_str()); - } - - avaliableToSpend += utxo->TxOut->Value; - } - - uint64_t totalSpent = 0; - for (const auto& txOut : TxOuts) - totalSpent += txOut->Value; - - if (avaliableToSpend < totalSpent) - throw TxValidationException("Spend value is more than available"); -} - bool Tx::operator==(const Tx& obj) const { if (this == &obj) diff --git a/tiny-lib/Tx.hpp b/tiny-lib/Tx.hpp index b7edb3f..8fa0226 100644 --- a/tiny-lib/Tx.hpp +++ b/tiny-lib/Tx.hpp @@ -30,11 +30,6 @@ class Tx : public ISerializable, public IDeserializable void ValidateBasics(bool coinbase = false) const; - BinaryBuffer Serialize() const override; - bool Deserialize(BinaryBuffer& buffer) override; - - static std::shared_ptr CreateCoinbase(const std::string& payToAddr, uint64_t value, int64_t height); - struct ValidateRequest { bool AsCoinbase = false; @@ -44,6 +39,11 @@ class Tx : public ISerializable, public IDeserializable void Validate(const ValidateRequest& req) const; + BinaryBuffer Serialize() const override; + bool Deserialize(BinaryBuffer& buffer) override; + + static std::shared_ptr CreateCoinbase(const std::string& payToAddr, uint64_t value, int64_t height); + bool operator==(const Tx& obj) const; private: diff --git a/tiny-lib/TxInfoMsg.cpp b/tiny-lib/TxInfoMsg.cpp index 0750666..896fdbd 100644 --- a/tiny-lib/TxInfoMsg.cpp +++ b/tiny-lib/TxInfoMsg.cpp @@ -11,7 +11,7 @@ TxInfoMsg::TxInfoMsg(const std::shared_ptr<::Tx>& tx) void TxInfoMsg::Handle(std::shared_ptr& con) { - LOG_INFO("Recieved transaction {} from peer {}:{}", Tx->Id(), con->Socket.remote_endpoint().address().to_string(), + LOG_TRACE("Recieved transaction {} from peer {}:{}", Tx->Id(), con->Socket.remote_endpoint().address().to_string(), con->Socket.remote_endpoint().port()); Mempool::AddTxToMempool(Tx); diff --git a/tiny-lib/UnspentTxOut.cpp b/tiny-lib/UnspentTxOut.cpp index afcf636..227a750 100644 --- a/tiny-lib/UnspentTxOut.cpp +++ b/tiny-lib/UnspentTxOut.cpp @@ -62,20 +62,26 @@ bool UnspentTxOut::Deserialize(BinaryBuffer& buffer) std::unordered_map, std::shared_ptr> UnspentTxOut::Map; +std::recursive_mutex UnspentTxOut::Mutex; + void UnspentTxOut::AddToMap(std::shared_ptr<::TxOut>& txOut, const std::string& txId, int64_t idx, bool isCoinbase, int64_t height) { + std::lock_guard lock(Mutex); + auto txOutPoint = std::make_shared<::TxOutPoint>(txId, idx); auto utxo = std::make_shared(txOut, txOutPoint, isCoinbase, height); - LOG_INFO("Adding TxOutPoint {} to UTXO map", utxo->TxOutPoint->TxId); + LOG_TRACE("Adding TxOutPoint {} to UTXO map", utxo->TxOutPoint->TxId); Map[utxo->TxOutPoint] = utxo; } void UnspentTxOut::RemoveFromMap(const std::string& txId, int64_t idx) { + std::lock_guard lock(Mutex); + const auto map_it = std::ranges::find_if(Map, [&txId, idx]( const std::pair< @@ -111,14 +117,17 @@ std::shared_ptr UnspentTxOut::FindInList(const std::shared_ptr UnspentTxOut::FindInMap(const std::shared_ptr<::TxOutPoint>& toSpend) { - auto map_it = std::ranges::find_if(Map, - [&toSpend]( - const std::pair, std::shared_ptr>& - p) - { - const auto& [txOutPoint, utxo] = p; - return *txOutPoint == *toSpend; - }); + std::lock_guard lock(Mutex); + + const auto map_it = std::ranges::find_if(Map, + [&toSpend]( + const std::pair< + std::shared_ptr<::TxOutPoint>, std::shared_ptr>& + p) + { + const auto& [txOutPoint, utxo] = p; + return *txOutPoint == *toSpend; + }); if (map_it != Map.end()) { return map_it->second; @@ -127,6 +136,45 @@ std::shared_ptr UnspentTxOut::FindInMap(const std::shared_ptr<::Tx return nullptr; } +std::shared_ptr UnspentTxOut::FindTxOutInBlock(const std::shared_ptr& block, + const std::shared_ptr& txIn) +{ + for (const auto& tx : block->Txs) + { + if (tx->Id() == txIn->ToSpend->TxId) + { + return tx->TxOuts[txIn->ToSpend->TxOutIdx]; + } + } + + return nullptr; +} + +std::shared_ptr UnspentTxOut::FindTxOutInMap(const std::shared_ptr& txIn) +{ + std::lock_guard lock(Mutex); + + for (const auto& utxo : Map | std::ranges::views::values) + { + if (txIn->ToSpend->TxId == utxo->TxOutPoint->TxId && txIn->ToSpend->TxOutIdx == utxo->TxOutPoint->TxOutIdx) + { + return utxo->TxOut; + } + } + + return nullptr; +} + +std::shared_ptr UnspentTxOut::FindTxOutInMapOrBlock(const std::shared_ptr& block, + const std::shared_ptr& txIn) +{ + auto utxo = FindTxOutInMap(txIn); + if (utxo != nullptr) + return utxo; + + return FindTxOutInBlock(block, txIn); +} + bool UnspentTxOut::operator==(const UnspentTxOut& obj) const { if (this == &obj) diff --git a/tiny-lib/UnspentTxOut.hpp b/tiny-lib/UnspentTxOut.hpp index 4a45a9e..f96047a 100644 --- a/tiny-lib/UnspentTxOut.hpp +++ b/tiny-lib/UnspentTxOut.hpp @@ -1,11 +1,13 @@ #pragma once #include #include +#include #include #include #include #include +#include "Block.hpp" #include "IDeserializable.hpp" #include "ISerializable.hpp" #include "Tx.hpp" @@ -31,9 +33,10 @@ class UnspentTxOut : public ISerializable, public IDeserializable BinaryBuffer Serialize() const override; bool Deserialize(BinaryBuffer& buffer) override; - //TODO: map mutex? static std::unordered_map, std::shared_ptr> Map; + static std::recursive_mutex Mutex; + static void AddToMap(std::shared_ptr<::TxOut>& txOut, const std::string& txId, int64_t idx, bool isCoinbase, int64_t height); static void RemoveFromMap(const std::string& txId, int64_t idx); @@ -42,6 +45,12 @@ class UnspentTxOut : public ISerializable, public IDeserializable const std::vector>& txs); static std::shared_ptr FindInMap(const std::shared_ptr<::TxOutPoint>& toSpend); + static std::shared_ptr<::TxOut> FindTxOutInBlock(const std::shared_ptr& block, + const std::shared_ptr& txIn); + static std::shared_ptr<::TxOut> FindTxOutInMap(const std::shared_ptr& txIn); + static std::shared_ptr<::TxOut> FindTxOutInMapOrBlock(const std::shared_ptr& block, + const std::shared_ptr& txIn); + bool operator==(const UnspentTxOut& obj) const; private: diff --git a/tiny-lib/Utils.cpp b/tiny-lib/Utils.cpp index 9640a04..5203157 100644 --- a/tiny-lib/Utils.cpp +++ b/tiny-lib/Utils.cpp @@ -60,5 +60,5 @@ bool Utils::IsLittleEndianCast() { const uint32_t i = 1; - return (reinterpret_cast(&i)[0] == i); + return reinterpret_cast(&i)[0] == i; } diff --git a/tiny-lib/Wallet.cpp b/tiny-lib/Wallet.cpp index 4e7ed77..c5dd892 100644 --- a/tiny-lib/Wallet.cpp +++ b/tiny-lib/Wallet.cpp @@ -82,7 +82,7 @@ void Wallet::PrintWalletAddress(const std::string& walletPath) { const auto [privKey, pubKey, address] = GetWallet(walletPath); - LOG_INFO("{} belongs to address is {}", walletPath, address); + LOG_INFO("Wallet {} belongs to address {}", walletPath, address); } std::tuple, std::vector, std::string> Wallet::InitWallet(const std::string& walletPath) @@ -109,22 +109,21 @@ std::tuple, std::vector, std::string> Wallet::Init std::shared_ptr Wallet::BuildTxIn(const std::vector& privKey, const std::shared_ptr& txOutPoint, - const std::shared_ptr& txOut) + const std::vector>& txOuts) { int32_t sequence = 0; auto pubKey = ECDSA::GetPubKeyFromPrivKey(privKey); - const auto spend_msg = MsgSerializer::BuildSpendMsg(txOutPoint, pubKey, sequence, - std::vector{txOut}); + const auto spend_msg = MsgSerializer::BuildSpendMsg(txOutPoint, pubKey, sequence, txOuts); auto unlock_sig = ECDSA::SignMsg(spend_msg, privKey); return std::make_shared(txOutPoint, unlock_sig, pubKey, sequence); } -std::shared_ptr Wallet::SendValue_Miner(uint64_t value, const std::string& address, +std::shared_ptr Wallet::SendValue_Miner(uint64_t value, uint64_t fee, const std::string& address, const std::vector& privKey) { - auto tx = BuildTx_Miner(value, address, privKey); + auto tx = BuildTx_Miner(value, fee, address, privKey); if (tx == nullptr) return nullptr; LOG_INFO("Built transaction {}, adding to mempool", tx->Id()); @@ -134,9 +133,10 @@ std::shared_ptr Wallet::SendValue_Miner(uint64_t value, const std::string& a return tx; } -std::shared_ptr Wallet::SendValue(uint64_t value, const std::string& address, const std::vector& privKey) +std::shared_ptr Wallet::SendValue(uint64_t value, uint64_t fee, const std::string& address, + const std::vector& privKey) { - auto tx = BuildTx(value, address, privKey); + auto tx = BuildTx(value, fee, address, privKey); if (tx == nullptr) return nullptr; LOG_INFO("Built transaction {}, broadcasting", tx->Id()); @@ -152,13 +152,17 @@ Wallet::TxStatusResponse Wallet::GetTxStatus_Miner(const std::string& txId) { TxStatusResponse ret; - for (const auto& tx : Mempool::Map | std::views::keys) { - if (tx == txId) + std::lock_guard lock(Mempool::Mutex); + + for (const auto& tx : Mempool::Map | std::views::keys) { - ret.Status = TxStatus::Mempool; + if (tx == txId) + { + ret.Status = TxStatus::Mempool; - return ret; + return ret; + } } } @@ -318,8 +322,9 @@ void Wallet::PrintBalance(const std::string& address) LOG_INFO("Address {} holds {} coins", address, balance); } -std::shared_ptr Wallet::BuildTxFromUTXOs(std::vector>& utxos, uint64_t value, - const std::string& address, const std::vector& privKey) +std::shared_ptr Wallet::BuildTxFromUTXOs(std::vector>& utxos, uint64_t value, uint64_t fee, + const std::string& address, const std::string& changeAddress, + const std::vector& privKey) { std::ranges::sort(utxos, [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool @@ -332,33 +337,49 @@ std::shared_ptr Wallet::BuildTxFromUTXOs(std::vector>& return a->Height < b->Height; }); std::unordered_set> selected_utxos; + uint64_t in_sum = 0; + const uint32_t total_size_est = 300; + const uint64_t total_fee_est = total_size_est * fee; for (const auto& coin : utxos) { - if (!selected_utxos.contains(coin)) + selected_utxos.insert(selected_utxos.end(), coin); + for (const auto& selected_coin : selected_utxos) { - selected_utxos.insert(selected_utxos.end(), coin); + in_sum += selected_coin->TxOut->Value; } - uint64_t sum = 0; - for (const auto& selected_coin : selected_utxos) + if (in_sum <= value + total_fee_est) { - sum += selected_coin->TxOut->Value; + in_sum = 0; } - if (sum > value) + else { break; } } + if (in_sum == 0) + { + LOG_ERROR("Not enough coins"); + + return nullptr; + } const auto txOut = std::make_shared(value, address); + uint64_t change = in_sum - value - total_fee_est; + const auto txOut_change = std::make_shared(change, changeAddress); + std::vector txOuts{txOut, txOut_change}; std::vector> txIns; + txIns.reserve(selected_utxos.size()); for (const auto& selected_coin : selected_utxos) { - txIns.emplace_back(BuildTxIn(privKey, selected_coin->TxOutPoint, txOut)); + txIns.emplace_back(BuildTxIn(privKey, selected_coin->TxOutPoint, txOuts)); } - auto tx = std::make_shared(txIns, std::vector{txOut}, -1); + auto tx = std::make_shared(txIns, txOuts, -1); + const uint32_t tx_size = tx->Serialize().GetBuffer().size(); + const uint32_t real_fee = total_fee_est / tx_size; + LOG_INFO("Built transaction {} with {} coins/byte fee", tx->Id(), real_fee); return tx; } -std::shared_ptr Wallet::BuildTx_Miner(uint64_t value, const std::string& address, +std::shared_ptr Wallet::BuildTx_Miner(uint64_t value, uint64_t fee, const std::string& address, const std::vector& privKey) { const auto pubKey = ECDSA::GetPubKeyFromPrivKey(privKey); @@ -371,10 +392,11 @@ std::shared_ptr Wallet::BuildTx_Miner(uint64_t value, const std::string& add return nullptr; } - return BuildTxFromUTXOs(my_coins, value, address, privKey); + return BuildTxFromUTXOs(my_coins, value, fee, address, myAddress, privKey); } -std::shared_ptr Wallet::BuildTx(uint64_t value, const std::string& address, const std::vector& privKey) +std::shared_ptr Wallet::BuildTx(uint64_t value, uint64_t fee, const std::string& address, + const std::vector& privKey) { const auto pubKey = ECDSA::GetPubKeyFromPrivKey(privKey); const auto myAddress = PubKeyToAddress(pubKey); @@ -385,17 +407,21 @@ std::shared_ptr Wallet::BuildTx(uint64_t value, const std::string& address, return nullptr; } - return BuildTxFromUTXOs(my_coins, value, address, privKey); + return BuildTxFromUTXOs(my_coins, value, fee, address, myAddress, privKey); } std::vector> Wallet::FindUTXOsForAddress_Miner(const std::string& address) { std::vector> utxos; - for (const auto& v : UTXO::Map | std::views::values) { - if (v->TxOut->ToAddress == address) + std::lock_guard lock(UTXO::Mutex); + + for (const auto& v : UTXO::Map | std::views::values) { - utxos.push_back(v); + if (v->TxOut->ToAddress == address) + { + utxos.push_back(v); + } } } return utxos; @@ -410,7 +436,7 @@ std::vector> Wallet::FindUTXOsForAddress(const std::string { LOG_ERROR("No connection to ask UTXO set"); - return std::vector>(); + return {}; } const auto start = Utils::GetUnixTimestamp(); @@ -420,7 +446,7 @@ std::vector> Wallet::FindUTXOsForAddress(const std::string { LOG_ERROR("Timeout on GetUTXOsMsg"); - return std::vector>(); + return {}; } std::this_thread::sleep_for(std::chrono::milliseconds(16)); } diff --git a/tiny-lib/Wallet.hpp b/tiny-lib/Wallet.hpp index a8715b4..a555408 100644 --- a/tiny-lib/Wallet.hpp +++ b/tiny-lib/Wallet.hpp @@ -29,10 +29,10 @@ class Wallet static std::shared_ptr BuildTxIn(const std::vector& privKey, const std::shared_ptr& txOutPoint, - const std::shared_ptr& txOut); - static std::shared_ptr SendValue_Miner(uint64_t value, const std::string& address, + const std::vector>& txOuts); + static std::shared_ptr SendValue_Miner(uint64_t value, uint64_t fee, const std::string& address, const std::vector& privKey); - static std::shared_ptr SendValue(uint64_t value, const std::string& address, + static std::shared_ptr SendValue(uint64_t value, uint64_t fee, const std::string& address, const std::vector& privKey); struct TxStatusResponse @@ -56,12 +56,13 @@ class Wallet static std::string WalletPath; static std::shared_ptr BuildTxFromUTXOs(std::vector>& utxos, uint64_t value, - const std::string& address, - const std::vector& privKey); + uint64_t fee, const std::string& address, + const std::string& changeAddress, const std::vector& privKey); - static std::shared_ptr BuildTx_Miner(uint64_t value, const std::string& address, + static std::shared_ptr BuildTx_Miner(uint64_t value, uint64_t fee, const std::string& address, const std::vector& privKey); - static std::shared_ptr BuildTx(uint64_t value, const std::string& address, const std::vector& privKey); + static std::shared_ptr BuildTx(uint64_t value, uint64_t fee, const std::string& address, + const std::vector& privKey); static std::vector> FindUTXOsForAddress_Miner(const std::string& address); static std::vector> FindUTXOsForAddress(const std::string& address); diff --git a/tiny-sandbox/main.cpp b/tiny-sandbox/main.cpp index 1a6f77d..7b9fc3d 100644 --- a/tiny-sandbox/main.cpp +++ b/tiny-sandbox/main.cpp @@ -128,16 +128,25 @@ int main(int argc, char** argv) { command.erase(0, send.length()); std::vector send_args; - split(send_args, command, boost::is_any_of(" ")); - if (send_args.size() != 2) + boost::split(send_args, command, boost::is_any_of(" ")); + if (send_args.size() != 2 && send_args.size() != 3) { - LOG_ERROR("Send command requires 2 arguments, receiver address and send value"); + LOG_ERROR( + "Send command requires 2 arguments, receiver address, send value and optionally fee per byte"); continue; } const auto& send_address = send_args[0]; const auto& send_value = send_args[1]; - Wallet::SendValue(std::stoull(send_value), send_address, privKey); + if (send_args.size() == 3) + { + const auto& send_fee = send_args[2]; + Wallet::SendValue(std::stoull(send_value), std::stoull(send_fee), send_address, privKey); + } + else + { + Wallet::SendValue(std::stoull(send_value), 100, send_address, privKey); + } } else if (command.starts_with(tx_status)) { diff --git a/tiny-test/BlockChainTests.cpp b/tiny-test/BlockChainTests.cpp index c58b725..7ea8b16 100644 --- a/tiny-test/BlockChainTests.cpp +++ b/tiny-test/BlockChainTests.cpp @@ -151,9 +151,9 @@ TEST(BlockChainTest, Reorg) for (const auto& block : chain1) ASSERT_EQ(Chain::ConnectBlock(block), Chain::ActiveChainIdx); - Chain::SideBranches = std::vector>>(); - Mempool::Map = std::unordered_map>(); - UTXO::Map = std::unordered_map, std::shared_ptr>(); + Chain::SideBranches.clear(); + Mempool::Map.clear(); + UTXO::Map.clear(); for (const auto& block : Chain::ActiveChain) { @@ -321,9 +321,9 @@ TEST(BlockChainTest_LongRunning, DependentTxsInSingleBlock) const auto& utxo1 = UTXO::Map.begin()->second; auto txOut1 = std::make_shared(901, utxo1->TxOut->ToAddress); - auto txIn1 = Wallet::BuildTxIn(priv_key, utxo1->TxOutPoint, txOut1); - auto tx1 = std::make_shared(std::vector{txIn1}, - std::vector{txOut1}, -1); + std::vector txOuts1{txOut1}; + auto txIn1 = Wallet::BuildTxIn(priv_key, utxo1->TxOutPoint, txOuts1); + auto tx1 = std::make_shared(std::vector{txIn1}, txOuts1, -1); ASSERT_THROW( { @@ -333,7 +333,7 @@ TEST(BlockChainTest_LongRunning, DependentTxsInSingleBlock) } catch (const TxValidationException& ex) { - ASSERT_STREQ("Coinbase UTXO is not ready for spending", ex.what()); + ASSERT_STREQ("Coinbase UTXO not ready for spending", ex.what()); throw; } }, @@ -345,10 +345,10 @@ TEST(BlockChainTest_LongRunning, DependentTxsInSingleBlock) ASSERT_TRUE(Mempool::Map.contains(tx1->Id())); auto txOut2 = std::make_shared(9001, txOut1->ToAddress); + std::vector txOuts2{txOut2}; auto txOutPoint2 = std::make_shared(tx1->Id(), 0); - auto txIn2 = Wallet::BuildTxIn(priv_key, txOutPoint2, txOut2); - auto tx2 = std::make_shared(std::vector{txIn2}, - std::vector{txOut2}, -1); + auto txIn2 = Wallet::BuildTxIn(priv_key, txOutPoint2, txOuts2); + auto tx2 = std::make_shared(std::vector{txIn2}, txOuts2, -1); Mempool::AddTxToMempool(tx2); ASSERT_FALSE(Mempool::Map.contains(tx2->Id())); @@ -361,14 +361,14 @@ TEST(BlockChainTest_LongRunning, DependentTxsInSingleBlock) } catch (const TxValidationException& ex) { - ASSERT_STREQ("Spend value is more than available", ex.what()); + ASSERT_STREQ("Spend value more than available", ex.what()); throw; } }, TxValidationException); txOut2->Value = 901; - txIn2 = Wallet::BuildTxIn(priv_key, txOutPoint2, txOut2); + txIn2 = Wallet::BuildTxIn(priv_key, txOutPoint2, txOuts2); tx2->TxIns[0] = txIn2; Mempool::AddTxToMempool(tx2); @@ -429,7 +429,7 @@ TEST(BlockChainTest_LongRunning, MinerTransaction) } ASSERT_GT(Wallet::GetBalance_Miner(miner_address), 0); - auto tx = Wallet::SendValue_Miner(first_block->Txs.front()->TxOuts.front()->Value / 2, receiver_address, + auto tx = Wallet::SendValue_Miner(first_block->Txs.front()->TxOuts.front()->Value / 2, 100, receiver_address, miner_privKey); ASSERT_TRUE(tx != nullptr); ASSERT_EQ(Wallet::GetTxStatus_Miner(tx->Id()).Status, TxStatus::Mempool);