Skip to content

Commit

Permalink
fixup! [unit test] orphanage: multiple announcers, eviction
Browse files Browse the repository at this point in the history
  • Loading branch information
glozow committed Sep 13, 2023
1 parent 2a8158e commit 0fa6dbd
Showing 1 changed file with 109 additions and 6 deletions.
115 changes: 109 additions & 6 deletions src/test/orphanage_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ class TxOrphanageTest : public TxOrphanage
}
};

static void MakeNewKeyWithFastRandomContext(CKey& key)
static void MakeNewKeyWithFastRandomContext(CKey& key, FastRandomContext& rand_ctx = g_insecure_rand_ctx)
{
std::vector<unsigned char> keydata;
keydata = g_insecure_rand_ctx.randbytes(32);
keydata = rand_ctx.randbytes(32);
key.Set(keydata.data(), keydata.data() + keydata.size(), /*fCompressedIn=*/true);
assert(key.IsValid());
}
Expand All @@ -66,6 +66,31 @@ static CTransactionRef MakeLargeOrphan()
return MakeTransactionRef(tx);
}

static CTransactionRef MakeTransactionSpending(const std::vector<COutPoint>& outpoints, FastRandomContext& det_rand)
{
CKey key;
MakeNewKeyWithFastRandomContext(key, det_rand);
CMutableTransaction tx;
for (const auto& outpoint : outpoints) {
tx.vin.emplace_back(CTxIn(outpoint));
}
tx.vout.resize(1);
tx.vout[0].nValue = CENT;
tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
return MakeTransactionRef(tx);
}

// Make another (not necessarily valid) tx with the same txid but different wtxid.
static CTransactionRef MakeMutation(const CTransactionRef& ptx)
{
CMutableTransaction tx(*ptx);
tx.vin[0].scriptWitness.stack.push_back({1});
auto mutated_tx = MakeTransactionRef(tx);
assert(ptx->GetHash() == mutated_tx->GetHash());
assert(ptx->GetWitnessHash() != mutated_tx->GetWitnessHash());
return mutated_tx;
}

BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
{
// This test had non-deterministic coverage due to
Expand Down Expand Up @@ -178,6 +203,57 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_count);
BOOST_CHECK_EQUAL(orphanage.TotalOrphanBytes(), expected_total_size);
}

BOOST_AUTO_TEST_CASE(process_block)
{
FastRandomContext det_rand{true};
TxOrphanageTest orphanage;

// Create outpoints that will be spent by transactions in the block
std::vector<COutPoint> outpoints;
const uint32_t num_outpoints{6};
outpoints.reserve(num_outpoints);
for (uint32_t i{0}; i < num_outpoints; ++i) {
// All the hashes should be different, but change the n just in case.
outpoints.emplace_back(COutPoint{det_rand.rand256(), i});
}

CBlock block;
const NodeId node{0};

auto bo_tx_same_txid = MakeTransactionSpending({outpoints.at(0)}, det_rand);
BOOST_CHECK(orphanage.AddTx(bo_tx_same_txid, node, {}));
block.vtx.emplace_back(bo_tx_same_txid);

// 2 transactions with the same txid but different witness
auto b_tx_same_txid_diff_witness = MakeTransactionSpending({outpoints.at(1)}, det_rand);
block.vtx.emplace_back(b_tx_same_txid_diff_witness);

auto o_tx_same_txid_diff_witness = MakeMutation(b_tx_same_txid_diff_witness);
BOOST_CHECK(orphanage.AddTx(o_tx_same_txid_diff_witness, node, {}));

// 2 different transactions that spend the same input.
auto b_tx_conflict = MakeTransactionSpending({outpoints.at(2)}, det_rand);
block.vtx.emplace_back(b_tx_conflict);

auto o_tx_conflict = MakeTransactionSpending({outpoints.at(2)}, det_rand);
BOOST_CHECK(orphanage.AddTx(o_tx_conflict, node, {}));

// 2 different transactions that have 1 overlapping input.
auto b_tx_conflict_partial = MakeTransactionSpending({outpoints.at(3), outpoints.at(4)}, det_rand);
block.vtx.emplace_back(b_tx_conflict_partial);

auto o_tx_conflict_partial_2 = MakeTransactionSpending({outpoints.at(4), outpoints.at(5)}, det_rand);
BOOST_CHECK(orphanage.AddTx(o_tx_conflict_partial_2, node, {}));

const auto removed = orphanage.EraseForBlock(block);
BOOST_CHECK_EQUAL(orphanage.Size(), 0);
for (const auto& expected_removed : {bo_tx_same_txid, o_tx_same_txid_diff_witness, o_tx_conflict, o_tx_conflict_partial_2}) {
const auto& expected_removed_wtxid = expected_removed->GetWitnessHash();
BOOST_CHECK(std::find_if(removed.begin(), removed.end(), [&](const auto& wtxid) { return wtxid == expected_removed_wtxid; }) != removed.end());
}
}

BOOST_AUTO_TEST_CASE(multiple_announcers)
{
const NodeId node0{0};
Expand All @@ -187,31 +263,50 @@ BOOST_AUTO_TEST_CASE(multiple_announcers)
size_t expected_node0_size{0};
size_t expected_node1_size{0};
TxOrphanageTest orphanage;
// Check that accounting for bytes per peer is accurate.

// Check accounting per peer.
// Check that EraseForPeer works with multiple announcers.
{
auto ptx{MakeLargeOrphan()};
const auto tx_size = ptx->GetTotalSize();
orphanage.AddTx(ptx, node0, {});
BOOST_CHECK(orphanage.AddTx(ptx, node0, {}));
BOOST_CHECK(orphanage.HaveTx(GenTxid::Txid(ptx->GetHash())));
BOOST_CHECK(orphanage.HaveTx(GenTxid::Wtxid(ptx->GetWitnessHash())));
expected_total_size += tx_size;
expected_total_count += 1;
expected_node0_size += tx_size;
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
BOOST_CHECK_EQUAL(orphanage.TotalOrphanBytes(), expected_total_size);
BOOST_CHECK_EQUAL(orphanage.BytesFromPeer(node0), expected_node0_size);
BOOST_CHECK_EQUAL(orphanage.BytesFromPeer(node1), expected_node1_size);

// Adding again should do nothing.
orphanage.AddTx(ptx, node0, {});
BOOST_CHECK(!orphanage.AddTx(ptx, node0, {}));
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
BOOST_CHECK_EQUAL(orphanage.TotalOrphanBytes(), expected_total_size);
BOOST_CHECK_EQUAL(orphanage.BytesFromPeer(node0), expected_node0_size);
BOOST_CHECK_EQUAL(orphanage.BytesFromPeer(node1), expected_node1_size);

// Adding another tx with the same txid but different witness should not work.
auto ptx_mutated{MakeMutation(ptx)};
BOOST_CHECK(!orphanage.AddTx(ptx_mutated, node0, {}));
BOOST_CHECK(!orphanage.HaveTx(GenTxid::Wtxid(ptx_mutated->GetWitnessHash())));

// It's too late to add parent_txids through AddTx.
BOOST_CHECK(!orphanage.AddTx(ptx, node0, {ptx->vin.at(0).prevout.hash}));
// Parent txids is empty because the tx exists but no parent_txids were provided.
BOOST_CHECK(orphanage.GetParentTxids(ptx->GetWitnessHash())->empty());
// Parent txids is std::nullopt because the tx doesn't exist.
BOOST_CHECK(!orphanage.GetParentTxids(ptx_mutated->GetWitnessHash()).has_value());

// Adding existing tx for another peer should change that peer's bytes, but not total bytes.
orphanage.AddTx(ptx, node1, {});
BOOST_CHECK(!orphanage.AddTx(ptx, node1, {}));
expected_node1_size += tx_size;
BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
BOOST_CHECK_EQUAL(orphanage.TotalOrphanBytes(), expected_total_size);
BOOST_CHECK_EQUAL(orphanage.BytesFromPeer(node0), expected_node0_size);
BOOST_CHECK_EQUAL(orphanage.BytesFromPeer(node1), expected_node1_size);

// if EraseForPeer is called for an orphan with multiple announcers, the orphanage should only
// decrement the number of bytes for that peer.
orphanage.EraseForPeer(node0);
Expand All @@ -221,6 +316,7 @@ BOOST_AUTO_TEST_CASE(multiple_announcers)
BOOST_CHECK_EQUAL(orphanage.TotalOrphanBytes(), expected_total_size);
BOOST_CHECK_EQUAL(orphanage.BytesFromPeer(node0), expected_node0_size);
BOOST_CHECK_EQUAL(orphanage.BytesFromPeer(node1), expected_node1_size);

// EraseForPeer should delete the orphan if it's the only announcer left.
orphanage.EraseForPeer(node1);
expected_total_count -= 1;
Expand All @@ -232,6 +328,13 @@ BOOST_AUTO_TEST_CASE(multiple_announcers)
BOOST_CHECK_EQUAL(orphanage.BytesFromPeer(node1), expected_node1_size);
BOOST_CHECK(!orphanage.HaveTx(GenTxid::Txid(ptx->GetHash())));
}

// EraseOrphanOfPeer

// Check that erasure for blocks, expiration erases for all peers.
{

}
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 0fa6dbd

Please sign in to comment.