Skip to content
Permalink
Browse files

lite block fixes (#730)

* [P2P] only ask the publisher of a lite block for missing transactions

* [P2P] persist pending lite block request

* [CORE] added interface for quering single transaction details

* [P2P] lite blocks now query the blockchain and the pool for missing transactions

* [P2P] refactored lite block handling into seperated method

* [P2P] transaction notification now evaluates pending lite block requests

* [P2P] sanity check in add lite block for pending lite blocks

* [P2P] replaced boost::optional with std::optional

* [LOG] typo fixes

* [CORE] replace boost::optional with std::optional

* [P2P] notify missing transactions will now check for pending lite blocks

If we acquired missing transactions for a lite block we will be notified by new transactions. We now check if that is a result of a previous lite block request and if so we pass the transactions into the lite block push routine with additional transactions that are not locally available but provided by the publisher.

* [P2P] review application, see #730 (review)
  • Loading branch information...
michael-herwig authored and brandonlehmann committed Feb 24, 2019
1 parent aa0c629 commit a317b740b2123ed66a625bd183c54587e4f19232
@@ -1,4 +1,5 @@
.DS_Store
.build/
build/
/tags
.idea
@@ -867,6 +867,18 @@ std::string Core::getPaymentIDFromExtra(const std::vector<uint8_t> &extra)
return std::string();
}

std::optional<BinaryArray> Core::getTransaction(const Crypto::Hash& hash) const {
throwIfNotInitialized();
auto segment = findSegmentContainingTransaction(hash);
if(segment != nullptr) {
return segment->getRawTransactions({hash})[0];
} else if(transactionPool->checkIfTransactionPresent(hash)) {
return transactionPool->getTransaction(hash).getTransactionBinaryArray();
} else {
return std::nullopt;
}
}

void Core::getTransactions(const std::vector<Crypto::Hash>& transactionHashes, std::vector<BinaryArray>& transactions,
std::vector<Crypto::Hash>& missedHashes) const {
assert(!chainsLeaves.empty());
@@ -76,6 +76,7 @@ class Core : public ICore, public ICoreInformation {
std::unordered_set<Crypto::Hash> &transactionsUnknown) const override;

virtual bool hasTransaction(const Crypto::Hash& transactionHash) const override;
virtual std::optional<BinaryArray> getTransaction(const Crypto::Hash& transactionHash) const override;
virtual void getTransactions(const std::vector<Crypto::Hash>& transactionHashes, std::vector<BinaryArray>& transactions, std::vector<Crypto::Hash>& missedHashes) const override;

virtual uint64_t getBlockDifficulty(uint32_t blockIndex) const override;
@@ -8,6 +8,7 @@
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <optional>
#include <CryptoNote.h>

#include "AddBlockErrors.h"
@@ -73,6 +74,12 @@ class ICore {
std::unordered_set<Crypto::Hash> &transactionsUnknown) const = 0;

virtual bool hasTransaction(const Crypto::Hash& transactionHash) const = 0;
/*!
* \brief getTransaction Queries a single transaction details blob from the chain or transaction pool
* \param hash The hash of the transaction
* \return The binary blob of the queried transaction, or none if the transaction does not exist.
*/
virtual std::optional<BinaryArray> getTransaction(const Crypto::Hash& hash) const = 0;
virtual void getTransactions(const std::vector<Crypto::Hash>& transactionHashes,
std::vector<BinaryArray>& transactions,
std::vector<Crypto::Hash>& missedHashes) const = 0;
@@ -413,18 +413,30 @@ int CryptoNoteProtocolHandler::handle_notify_new_transactions(int command, NOTIF
if (context.m_state != CryptoNoteConnectionContext::state_normal)
return 1;

for (auto tx_blob_it = arg.txs.begin(); tx_blob_it != arg.txs.end();) {
if (!m_core.addTransactionToPool(*tx_blob_it)) {
logger(Logging::DEBUGGING) << context << "Tx verification failed";
tx_blob_it = arg.txs.erase(tx_blob_it);
} else {
++tx_blob_it;
}
}
if(context.m_pending_lite_block.has_value()) {
logger(Logging::TRACE) << context << " Pending lite block detected, handling request as missing lite block transactions response";
return doPushLiteBlock(context.m_pending_lite_block->request, context, std::move(arg.txs));
} else {
const auto it = std::remove_if(arg.txs.begin(), arg.txs.end(), [this, &context](const auto &tx)
{
bool failed = !this->m_core.addTransactionToPool(tx);

if (failed)
{
this->logger(Logging::DEBUGGING) << context << "Tx verification failed";
}

return failed;
});
if(it != arg.txs.end())
{
arg.txs.erase(it, arg.txs.end());
}

if (arg.txs.size()) {
//TODO: add announce usage here
relay_post_notify<NOTIFY_NEW_TRANSACTIONS>(*m_p2p, arg, &context.m_connection_id);
if (arg.txs.size() > 0) {
//TODO: add announce usage here
relay_post_notify<NOTIFY_NEW_TRANSACTIONS>(*m_p2p, arg, &context.m_connection_id);
}
}

return true;
@@ -567,6 +579,121 @@ int CryptoNoteProtocolHandler::processObjects(CryptoNoteConnectionContext& conte
return 0;
}

int CryptoNoteProtocolHandler::doPushLiteBlock(NOTIFY_NEW_LITE_BLOCK::request arg, CryptoNoteConnectionContext &context, std::vector<BinaryArray> missingTxs)
{
BlockTemplate newBlockTemplate;
if(!fromBinaryArray(newBlockTemplate, arg.blockTemplate)) { // deserialize blockTemplate
logger(Logging::WARNING) << context << "Deserialization of Block Template failed, dropping connection" ;
context.m_state = CryptoNoteConnectionContext::state_shutdown;
return 1;
}

std::unordered_map<Crypto::Hash, BinaryArray> provided_txs;
provided_txs.reserve(missingTxs.size());
for(const auto& iMissingTx : missingTxs) {
CachedTransaction i_provided_transaction{iMissingTx};
provided_txs[getBinaryArrayHash(iMissingTx)] = iMissingTx;
}

std::vector<BinaryArray> have_txs;
std::vector<Crypto::Hash> need_txs;

if(context.m_pending_lite_block.has_value()) {
for(const auto& requestedTxHash : context.m_pending_lite_block->missed_transactions) {
if(provided_txs.find(requestedTxHash) == provided_txs.end()) {
logger(Logging::DEBUGGING) << context << "Peer didn't provide a missing transaction, previously "
"acquired for a lite block, dropping connection.";
context.m_pending_lite_block = std::nullopt;
context.m_state = CryptoNoteConnectionContext::state_shutdown;
return 1;
}
}
}

/*
* here we are finding out which txs are
* present in the pool and which are not
* further we check for transactions in
* the blockchain to accept alternative
* blocks.
*/
for (const auto& transactionHash: newBlockTemplate.transactionHashes) {
auto providedSearch = provided_txs.find(transactionHash);
if(providedSearch != provided_txs.end()) {
have_txs.push_back(providedSearch->second);
}
else {
const auto transactionBlob = m_core.getTransaction(transactionHash);
if (transactionBlob.has_value()) {
have_txs.push_back(*transactionBlob);
} else {
need_txs.push_back(transactionHash);
}
}
}

/*
* if all txs are present then continue adding the
* block to DB and relaying the lite-block to other peers
*
* if not request the missing txs from the sender
* of the lite-block request
*/
if (need_txs.empty()) {
context.m_pending_lite_block = std::nullopt;
auto result = m_core.addBlock(RawBlock{arg.blockTemplate, have_txs});
if (result == error::AddBlockErrorCondition::BLOCK_ADDED) {
if (result == error::AddBlockErrorCode::ADDED_TO_ALTERNATIVE_AND_SWITCHED) {
++arg.hop;
//TODO: Add here announce protocol usage
relay_post_notify<NOTIFY_NEW_LITE_BLOCK>(*m_p2p, arg, &context.m_connection_id);
// relay_block(arg, context);
requestMissingPoolTransactions(context);
} else if (result == error::AddBlockErrorCode::ADDED_TO_MAIN) {
++arg.hop;
//TODO: Add here announce protocol usage
relay_post_notify<NOTIFY_NEW_LITE_BLOCK>(*m_p2p, arg, &context.m_connection_id);
// relay_block(arg, context);
} else if (result == error::AddBlockErrorCode::ADDED_TO_ALTERNATIVE) {
logger(Logging::TRACE) << context << "Block added as alternative";
} else {
logger(Logging::TRACE) << context << "Block already exists";
}
} else if (result == error::AddBlockErrorCondition::BLOCK_REJECTED) {
context.m_state = CryptoNoteConnectionContext::state_synchronizing;
NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>();
r.block_ids = m_core.buildSparseChain();
logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size();
post_notify<NOTIFY_REQUEST_CHAIN>(*m_p2p, r, context);
} else {
logger(Logging::DEBUGGING) << context << "Block verification failed, dropping connection: " << result.message();
context.m_state = CryptoNoteConnectionContext::state_shutdown;
}
}
else {
if(context.m_pending_lite_block.has_value()) {
context.m_pending_lite_block = std::nullopt;
logger(Logging::DEBUGGING) << context << " Peer has a pending lite block but didn't provide all necessary transactions, dropping the connection.";
context.m_state= CryptoNoteConnectionContext::state_shutdown;
}
else {
NOTIFY_MISSING_TXS::request req;
req.current_blockchain_height = arg.current_blockchain_height;
req.blockHash = CachedBlock(newBlockTemplate).getBlockHash();
req.missing_txs = std::move(need_txs);
context.m_pending_lite_block = PendingLiteBlock{arg, {req.missing_txs.begin(), req.missing_txs.end()}};

if (!post_notify<NOTIFY_MISSING_TXS>(*m_p2p, req, context)) {
logger(Logging::DEBUGGING)
<< context << "Lite block is missing transactions but the publisher is not reachable, dropping connection.";
context.m_state = CryptoNoteConnectionContext::state_shutdown;
}
}
}

return 1;
}

int CryptoNoteProtocolHandler::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, CryptoNoteConnectionContext& context) {
logger(Logging::TRACE) << context << "NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << arg.block_ids.size();

@@ -729,78 +856,7 @@ int CryptoNoteProtocolHandler::handle_notify_new_lite_block(int command, NOTIFY_
return 1;
}

BlockTemplate newBlockTemplate;
if(!fromBinaryArray(newBlockTemplate, arg.blockTemplate)) { // deserialize blockTemplate
logger(Logging::WARNING) << context << "Deserialization of Block Template failed, dropping connection" ;
context.m_state = CryptoNoteConnectionContext::state_shutdown;
return 1;
}


std::vector<BinaryArray> have_txs;
std::vector<Crypto::Hash> need_txs;

/*
* here we are finding out which txs are
* present in the pool and which are not
*/
for (const auto transactionHash: newBlockTemplate.transactionHashes) {
const auto [found, transactionBlob] = m_core.getPoolTransaction(transactionHash);
if (found) {
have_txs.push_back(transactionBlob);
}
else {
need_txs.push_back(transactionHash);
}
}

/*
* if all txs are present then continue adding the
* block to DB and relaying the lite-block to other peers
*
* if not request the missing txs from the sender
* of the lite-block request
*/
if (need_txs.empty()) {
auto result = m_core.addBlock(RawBlock{arg.blockTemplate, have_txs});
if (result == error::AddBlockErrorCondition::BLOCK_ADDED) {
if (result == error::AddBlockErrorCode::ADDED_TO_ALTERNATIVE_AND_SWITCHED) {
++arg.hop;
//TODO: Add here announce protocol usage
relay_post_notify<NOTIFY_NEW_LITE_BLOCK>(*m_p2p, arg, &context.m_connection_id);
// relay_block(arg, context);
requestMissingPoolTransactions(context);
} else if (result == error::AddBlockErrorCode::ADDED_TO_MAIN) {
++arg.hop;
//TODO: Add here announce protocol usage
relay_post_notify<NOTIFY_NEW_LITE_BLOCK>(*m_p2p, arg, &context.m_connection_id);
// relay_block(arg, context);
} else if (result == error::AddBlockErrorCode::ADDED_TO_ALTERNATIVE) {
logger(Logging::TRACE) << context << "Block added as alternative";
} else {
logger(Logging::TRACE) << context << "Block already exists";
}
} else if (result == error::AddBlockErrorCondition::BLOCK_REJECTED) {
context.m_state = CryptoNoteConnectionContext::state_synchronizing;
NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>();
r.block_ids = m_core.buildSparseChain();
logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size();
post_notify<NOTIFY_REQUEST_CHAIN>(*m_p2p, r, context);
} else {
logger(Logging::DEBUGGING) << context << "Block verification failed, dropping connection: " << result.message();
context.m_state = CryptoNoteConnectionContext::state_shutdown;
}
}
else {
NOTIFY_MISSING_TXS::request req;
req.current_blockchain_height = arg.current_blockchain_height;
req.blockHash = CachedBlock(newBlockTemplate).getBlockHash();
req.missing_txs = std::move(need_txs);

relay_post_notify<NOTIFY_MISSING_TXS>(*m_p2p, req);
}

return 1;
return doPushLiteBlock(std::move(arg), context, {});
}

int CryptoNoteProtocolHandler::handle_notify_missing_txs(int command, NOTIFY_MISSING_TXS::request& arg,
@@ -84,6 +84,9 @@ namespace CryptoNote
int processObjects(CryptoNoteConnectionContext& context, std::vector<RawBlock>&& rawBlocks, const std::vector<CachedBlock>& cachedBlocks);
Logging::LoggerRef logger;

private:
int doPushLiteBlock(NOTIFY_NEW_LITE_BLOCK::request block, CryptoNoteConnectionContext& context, std::vector<BinaryArray> missingTxs);

private:

System::Dispatcher& m_dispatcher;
@@ -20,11 +20,14 @@
#include <list>
#include <ostream>
#include <unordered_set>
#include <optional>

#include <boost/uuid/uuid.hpp>
#include "Common/StringTools.h"
#include "crypto/hash.h"

#include "P2p/PendingLiteBlock.h"

namespace CryptoNote {

struct CryptoNoteConnectionContext {
@@ -46,6 +49,7 @@ struct CryptoNoteConnectionContext {
};

state m_state = state_befor_handshake;
std::optional<PendingLiteBlock> m_pending_lite_block;
std::list<Crypto::Hash> m_needed_objects;
std::unordered_set<Crypto::Hash> m_requested_objects;
uint32_t m_remote_blockchain_height = 0;
@@ -0,0 +1,16 @@
// Copyright (c) 2018, The TurtleCoin Developers
//
// Please see the included LICENSE file for more information.

#pragma once

#include <unordered_set>

#include "CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h"

namespace CryptoNote {
struct PendingLiteBlock {
NOTIFY_NEW_LITE_BLOCK_request request;
std::unordered_set<Crypto::Hash> missed_transactions;
};
} // namespace CryptoNote

0 comments on commit a317b74

Please sign in to comment.
You can’t perform that action at this time.