Changeset View
Changeset View
Standalone View
Standalone View
src/avalanche/test/processor_tests.cpp
Show First 20 Lines • Show All 359 Lines • ▼ Show 20 Lines | struct TxProvider { | ||||
std::vector<avalanche::VoteItemUpdate> updates; | std::vector<avalanche::VoteItemUpdate> updates; | ||||
uint32_t invType; | uint32_t invType; | ||||
TxProvider(AvalancheTestingSetup *_fixture) | TxProvider(AvalancheTestingSetup *_fixture) | ||||
: fixture(_fixture), invType(MSG_TX) {} | : fixture(_fixture), invType(MSG_TX) {} | ||||
CTransactionRef buildVoteItem() const { | CTransactionRef buildVoteItem() const { | ||||
auto rng = FastRandomContext(); | |||||
CMutableTransaction mtx; | CMutableTransaction mtx; | ||||
mtx.nVersion = 2; | mtx.nVersion = 2; | ||||
mtx.vin.emplace_back(COutPoint{TxId(FastRandomContext().rand256()), 0}); | mtx.vin.emplace_back(COutPoint{TxId(rng.rand256()), 0}); | ||||
mtx.vout.emplace_back(1 * COIN, CScript() << OP_TRUE); | mtx.vout.emplace_back(10 * COIN, CScript() << OP_TRUE); | ||||
CTransactionRef tx = MakeTransactionRef(std::move(mtx)); | CTransactionRef tx = MakeTransactionRef(std::move(mtx)); | ||||
TestMemPoolEntryHelper mempoolEntryHelper; | TestMemPoolEntryHelper mempoolEntryHelper; | ||||
auto entry = mempoolEntryHelper.FromTx(tx); | auto entry = mempoolEntryHelper.Fee(int64_t(rng.randrange(10)) * COIN) | ||||
.FromTx(tx); | |||||
CTxMemPool *mempool = Assert(fixture->m_node.mempool.get()); | CTxMemPool *mempool = Assert(fixture->m_node.mempool.get()); | ||||
{ | { | ||||
LOCK2(cs_main, mempool->cs); | LOCK2(cs_main, mempool->cs); | ||||
mempool->addUnchecked(entry); | mempool->addUnchecked(entry); | ||||
BOOST_CHECK(mempool->exists(tx->GetId())); | BOOST_CHECK(mempool->exists(tx->GetId())); | ||||
} | } | ||||
return tx; | return tx; | ||||
} | } | ||||
uint256 getVoteItemId(const CTransactionRef &tx) const { | uint256 getVoteItemId(const CTransactionRef &tx) const { | ||||
return tx->GetId(); | return tx->GetId(); | ||||
} | } | ||||
std::vector<Vote> buildVotesForItems(uint32_t error, | std::vector<Vote> buildVotesForItems(uint32_t error, | ||||
std::vector<CTransactionRef> &&items) { | std::vector<CTransactionRef> &&items) { | ||||
size_t numItems = items.size(); | size_t numItems = items.size(); | ||||
std::vector<Vote> votes; | std::vector<Vote> votes; | ||||
votes.reserve(numItems); | votes.reserve(numItems); | ||||
// Transactions are sorted by TxId | CTxMemPool *mempool = Assert(fixture->m_node.mempool.get()); | ||||
{ | |||||
LOCK(mempool->cs); | |||||
// Transactions are sorted by modified fee rate as long as they are | |||||
// in the mempool. Let's keep it simple here and assume it's the | |||||
// case. | |||||
std::sort(items.begin(), items.end(), | std::sort(items.begin(), items.end(), | ||||
[](const CTransactionRef &lhs, const CTransactionRef &rhs) { | [mempool](const CTransactionRef &lhs, | ||||
return lhs->GetId() < rhs->GetId(); | const CTransactionRef &rhs) | ||||
EXCLUSIVE_LOCKS_REQUIRED(mempool->cs) { | |||||
auto lhsIter = mempool->GetIter(lhs->GetId()); | |||||
auto rhsIter = mempool->GetIter(rhs->GetId()); | |||||
BOOST_CHECK(lhsIter); | |||||
BOOST_CHECK(rhsIter); | |||||
return CompareTxMemPoolEntryByModifiedFeeRate{}( | |||||
**lhsIter, **rhsIter); | |||||
}); | }); | ||||
} | |||||
for (auto &item : items) { | for (auto &item : items) { | ||||
votes.emplace_back(error, item->GetId()); | votes.emplace_back(error, item->GetId()); | ||||
} | } | ||||
return votes; | return votes; | ||||
} | } | ||||
void invalidateItem(const CTransactionRef &tx) { | void invalidateItem(const CTransactionRef &tx) { | ||||
▲ Show 20 Lines • Show All 1,548 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE(vote_map_comparator) { | ||||
std::vector<CBlockIndex> indexes; | std::vector<CBlockIndex> indexes; | ||||
for (size_t i = 1; i <= numberElementsEachType; i++) { | for (size_t i = 1; i <= numberElementsEachType; i++) { | ||||
CBlockIndex index; | CBlockIndex index; | ||||
index.nChainWork = i; | index.nChainWork = i; | ||||
indexes.emplace_back(std::move(index)); | indexes.emplace_back(std::move(index)); | ||||
} | } | ||||
Shuffle(indexes.begin(), indexes.end(), rng); | Shuffle(indexes.begin(), indexes.end(), rng); | ||||
auto allItems = std::make_tuple(std::move(proofs), std::move(indexes)); | CTxMemPool *mempool = Assert(m_node.mempool.get()); | ||||
TestMemPoolEntryHelper mempoolEntryHelper; | |||||
std::vector<CTransactionRef> txs; | |||||
for (size_t i = 1; i <= numberElementsEachType; i++) { | |||||
CMutableTransaction mtx; | |||||
mtx.nVersion = 2; | |||||
mtx.vin.emplace_back(COutPoint{TxId(rng.rand256()), 0}); | |||||
mtx.vout.emplace_back(1000 * COIN, CScript() << OP_TRUE); | |||||
CTransactionRef tx = MakeTransactionRef(std::move(mtx)); | |||||
auto entry = mempoolEntryHelper.Fee(int64_t(i) * COIN).FromTx(tx); | |||||
{ | |||||
LOCK2(cs_main, mempool->cs); | |||||
mempool->addUnchecked(entry); | |||||
BOOST_CHECK(mempool->exists(tx->GetId())); | |||||
} | |||||
txs.emplace_back(std::move(tx)); | |||||
} | |||||
auto allItems = | |||||
std::make_tuple(std::move(proofs), std::move(indexes), std::move(txs)); | |||||
static const size_t numTypes = std::tuple_size<decltype(allItems)>::value; | static const size_t numTypes = std::tuple_size<decltype(allItems)>::value; | ||||
RWCollection<VoteMap> voteMap; | RWCollection<VoteMap> voteMap(VoteMap(m_node.mempool.get())); | ||||
{ | { | ||||
auto writeView = voteMap.getWriteView(); | auto writeView = voteMap.getWriteView(); | ||||
for (size_t i = 0; i < numberElementsEachType; i++) { | for (size_t i = 0; i < numberElementsEachType; i++) { | ||||
// Randomize the insert order at each loop increment | // Randomize the insert order at each loop increment | ||||
const size_t firstType = rng.randrange(numTypes); | const size_t firstType = rng.randrange(numTypes); | ||||
for (size_t j = 0; j < numTypes; j++) { | for (size_t j = 0; j < numTypes; j++) { | ||||
switch ((firstType + j) % numTypes) { | switch ((firstType + j) % numTypes) { | ||||
// ProofRef | // ProofRef | ||||
case 0: | case 0: | ||||
writeView->insert(std::make_pair( | writeView->insert(std::make_pair( | ||||
std::get<0>(allItems)[i], VoteRecord(true))); | std::get<0>(allItems)[i], VoteRecord(true))); | ||||
break; | break; | ||||
// CBlockIndex * | // CBlockIndex * | ||||
case 1: | case 1: | ||||
writeView->insert(std::make_pair( | writeView->insert(std::make_pair( | ||||
&std::get<1>(allItems)[i], VoteRecord(true))); | &std::get<1>(allItems)[i], VoteRecord(true))); | ||||
break; | break; | ||||
// CTransactionRef | |||||
case 2: | |||||
writeView->insert(std::make_pair( | |||||
std::get<2>(allItems)[i], VoteRecord(true))); | |||||
break; | |||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
{ | { | ||||
// Check ordering | // Check ordering | ||||
auto readView = voteMap.getReadView(); | auto readView = voteMap.getReadView(); | ||||
auto it = readView.begin(); | auto it = readView.begin(); | ||||
// The first batch of items is the proofs ordered by score (descending) | // The first batch of items is the proofs ordered by score | ||||
// (descending) | |||||
uint32_t lastScore = std::numeric_limits<uint32_t>::max(); | uint32_t lastScore = std::numeric_limits<uint32_t>::max(); | ||||
for (size_t i = 0; i < numberElementsEachType; i++) { | for (size_t i = 0; i < numberElementsEachType; i++) { | ||||
BOOST_CHECK(std::holds_alternative<const ProofRef>(it->first)); | BOOST_CHECK(std::holds_alternative<const ProofRef>(it->first)); | ||||
uint32_t currentScore = | uint32_t currentScore = | ||||
std::get<const ProofRef>(it->first)->getScore(); | std::get<const ProofRef>(it->first)->getScore(); | ||||
BOOST_CHECK_LT(currentScore, lastScore); | BOOST_CHECK_LT(currentScore, lastScore); | ||||
lastScore = currentScore; | lastScore = currentScore; | ||||
Show All 10 Lines | RWCollection<VoteMap> voteMap(VoteMap(m_node.mempool.get())); | ||||
arith_uint256 currentWork = | arith_uint256 currentWork = | ||||
std::get<const CBlockIndex *>(it->first)->nChainWork; | std::get<const CBlockIndex *>(it->first)->nChainWork; | ||||
BOOST_CHECK(currentWork < lastWork); | BOOST_CHECK(currentWork < lastWork); | ||||
lastWork = currentWork; | lastWork = currentWork; | ||||
it++; | it++; | ||||
} | } | ||||
// The last batch of items is the txs ordered by modified fee rate | |||||
CFeeRate lastFeeRate{MAX_MONEY}; | |||||
{ | |||||
LOCK(mempool->cs); | |||||
for (size_t i = 0; i < numberElementsEachType; i++) { | |||||
BOOST_CHECK( | |||||
std::holds_alternative<const CTransactionRef>(it->first)); | |||||
auto iter = mempool->GetIter( | |||||
std::get<const CTransactionRef>(it->first)->GetId()); | |||||
BOOST_CHECK(iter.has_value()); | |||||
CFeeRate currentFeeRate = (*iter)->GetModifiedFeeRate(); | |||||
BOOST_CHECK(currentFeeRate < lastFeeRate); | |||||
lastFeeRate = currentFeeRate; | |||||
it++; | |||||
} | |||||
} | |||||
BOOST_CHECK(it == readView.end()); | BOOST_CHECK(it == readView.end()); | ||||
} | } | ||||
} | } | ||||
BOOST_AUTO_TEST_CASE(vote_map_tx_comparator) { | |||||
CTxMemPool *mempool = Assert(m_node.mempool.get()); | |||||
TestMemPoolEntryHelper mempoolEntryHelper; | |||||
TxProvider provider(this); | |||||
std::vector<CTransactionRef> txs; | |||||
for (size_t i = 0; i < 5; i++) { | |||||
txs.emplace_back(provider.buildVoteItem()); | |||||
} | |||||
{ | |||||
// When there is no mempool, the txs are sorted by txid | |||||
RWCollection<VoteMap> voteMap(VoteMap(nullptr)); | |||||
{ | |||||
auto writeView = voteMap.getWriteView(); | |||||
for (const auto &tx : txs) { | |||||
writeView->insert(std::make_pair(tx, VoteRecord(true))); | |||||
} | |||||
} | |||||
auto readView = voteMap.getReadView(); | |||||
TxId lastTxId{uint256::ZERO}; | |||||
for (const auto &[item, vote] : readView) { | |||||
auto tx = std::get<const CTransactionRef>(item); | |||||
BOOST_CHECK_GT(tx->GetId(), lastTxId); | |||||
lastTxId = tx->GetId(); | |||||
} | |||||
} | |||||
// Remove the 5 first txs from the mempool, and add 5 more | |||||
mempool->clear(); | |||||
for (size_t i = 0; i < 5; i++) { | |||||
txs.emplace_back(provider.buildVoteItem()); | |||||
} | |||||
{ | |||||
RWCollection<VoteMap> voteMap((VoteMap(mempool))); | |||||
{ | |||||
auto writeView = voteMap.getWriteView(); | |||||
for (const auto &tx : txs) { | |||||
writeView->insert(std::make_pair(tx, VoteRecord(true))); | |||||
} | |||||
} | |||||
auto readView = voteMap.getReadView(); | |||||
auto it = readView.begin(); | |||||
LOCK(mempool->cs); | |||||
// The first 5 txs are sorted by fee | |||||
CFeeRate lastFeeRate{MAX_MONEY}; | |||||
for (size_t i = 0; i < 5; i++) { | |||||
auto tx = std::get<const CTransactionRef>(it->first); | |||||
auto iter = mempool->GetIter(tx->GetId()); | |||||
BOOST_CHECK(iter.has_value()); | |||||
BOOST_CHECK((*iter)->GetModifiedFeeRate() <= lastFeeRate); | |||||
lastFeeRate = (*iter)->GetModifiedFeeRate(); | |||||
it++; | |||||
} | |||||
// The last 5 txs are sorted by txid | |||||
TxId lastTxId{uint256::ZERO}; | |||||
for (size_t i = 0; i < 5; i++) { | |||||
auto tx = std::get<const CTransactionRef>(it->first); | |||||
BOOST_CHECK(!mempool->exists(tx->GetId())); | |||||
BOOST_CHECK_GT(tx->GetId(), lastTxId); | |||||
lastTxId = tx->GetId(); | |||||
it++; | |||||
} | |||||
} | |||||
} | |||||
BOOST_AUTO_TEST_CASE(block_reconcile_initial_vote) { | BOOST_AUTO_TEST_CASE(block_reconcile_initial_vote) { | ||||
const auto &config = GetConfig(); | const auto &config = GetConfig(); | ||||
auto &chainman = Assert(m_node.chainman); | auto &chainman = Assert(m_node.chainman); | ||||
Chainstate &chainstate = chainman->ActiveChainstate(); | Chainstate &chainstate = chainman->ActiveChainstate(); | ||||
const auto block = std::make_shared<const CBlock>( | const auto block = std::make_shared<const CBlock>( | ||||
this->CreateBlock({}, CScript(), chainstate)); | this->CreateBlock({}, CScript(), chainstate)); | ||||
const BlockHash blockhash = block->GetHash(); | const BlockHash blockhash = block->GetHash(); | ||||
▲ Show 20 Lines • Show All 41 Lines • Show Last 20 Lines |