diff --git a/src/avalanche/processor.h b/src/avalanche/processor.h --- a/src/avalanche/processor.h +++ b/src/avalanche/processor.h @@ -82,6 +82,7 @@ using BlockVoteMap = std::map; using ProofVoteMap = std::map; +using TxVoteMap = std::map; struct query_timeout {}; @@ -105,6 +106,11 @@ */ RWCollection proofVoteRecords; + /** + * Transactions to run avalanche on. + */ + RWCollection txVoteRecords; + /** * Keep track of peers and queries sent. */ @@ -184,10 +190,13 @@ bool addBlockToReconcile(const CBlockIndex *pindex); void addProofToReconcile(const ProofRef &proof); + bool addTxToReconcile(const CTransactionRef &tx); bool isAccepted(const CBlockIndex *pindex) const; bool isAccepted(const ProofRef &proof) const; + bool isAccepted(const CTransactionRef &tx) const; int getConfidence(const CBlockIndex *pindex) const; int getConfidence(const ProofRef &proof) const; + int getConfidence(const CTransactionRef &tx) const; // TODO: Refactor the API to remove the dependency on avalanche/protocol.h void sendResponse(CNode *pfrom, Response response) const; diff --git a/src/avalanche/processor.cpp b/src/avalanche/processor.cpp --- a/src/avalanche/processor.cpp +++ b/src/avalanche/processor.cpp @@ -309,6 +309,12 @@ std::make_pair(proof, VoteRecord(isAccepted))); } +bool Processor::addTxToReconcile(const CTransactionRef &tx) { + return txVoteRecords.getWriteView() + ->insert(std::make_pair(tx, VoteRecord(true))) + .second; +} + bool Processor::isAccepted(const CBlockIndex *pindex) const { auto r = blockVoteRecords.getReadView(); auto it = r->find(pindex); @@ -329,6 +335,16 @@ return it->second.isAccepted(); } +bool Processor::isAccepted(const CTransactionRef &tx) const { + auto r = txVoteRecords.getReadView(); + auto it = r->find(tx); + if (it == r.end()) { + return false; + } + + return it->second.isAccepted(); +} + int Processor::getConfidence(const CBlockIndex *pindex) const { auto r = blockVoteRecords.getReadView(); auto it = r->find(pindex); @@ -349,6 +365,16 @@ return it->second.getConfidence(); } +int Processor::getConfidence(const CTransactionRef &tx) const { + auto r = txVoteRecords.getReadView(); + auto it = r->find(tx); + if (it == r.end()) { + return -1; + } + + return it->second.getConfidence(); +} + namespace { /** * When using TCP, we need to sign all messages as the transport layer is diff --git a/src/avalanche/test/processor_tests.cpp b/src/avalanche/test/processor_tests.cpp --- a/src/avalanche/test/processor_tests.cpp +++ b/src/avalanche/test/processor_tests.cpp @@ -1203,6 +1203,31 @@ BOOST_CHECK_EQUAL(m_processor->getConfidence(proofB), 0); } +BOOST_AUTO_TEST_CASE(transaction_record) { + CTransactionRef txA = MakeTransactionRef(CMutableTransaction()); + CTransactionRef txB = MakeTransactionRef(CMutableTransaction()); + + // Neither transaction is known. + BOOST_CHECK(!m_processor->isAccepted(txA)); + BOOST_CHECK(!m_processor->isAccepted(txB)); + BOOST_CHECK_EQUAL(m_processor->getConfidence(txA), -1); + BOOST_CHECK_EQUAL(m_processor->getConfidence(txB), -1); + + // Add one tx; it should now be accepted but the other tx is still unknown. + m_processor->addTxToReconcile(txA); + BOOST_CHECK(m_processor->isAccepted(txA)); + BOOST_CHECK(!m_processor->isAccepted(txB)); + BOOST_CHECK_EQUAL(m_processor->getConfidence(txA), 0); + BOOST_CHECK_EQUAL(m_processor->getConfidence(txB), -1); + + // Add the other tx and know both are accepted + m_processor->addTxToReconcile(txB); + BOOST_CHECK(m_processor->isAccepted(txA)); + BOOST_CHECK(m_processor->isAccepted(txB)); + BOOST_CHECK_EQUAL(m_processor->getConfidence(txA), 0); + BOOST_CHECK_EQUAL(m_processor->getConfidence(txB), 0); +} + BOOST_AUTO_TEST_CASE(quorum_detection) { // Set min quorum parameters for our test int minStake = 4'000'000;