Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UIP-0024 Implementation (CTOR/LTOR) #440

Merged
merged 40 commits into from
Jan 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a5ad596
Use OTI algo in CChainState::RollforwardBlock
Dec 17, 2018
8a5e4c6
Add CTOR check in ContextualCheckBlock
Dec 17, 2018
35a6e24
Sort txns in BlockAssembler::CreateNewBlock
Dec 17, 2018
07b39a1
Split CChainState::DisconnectBlock's loop
Dec 19, 2018
731b0a5
Add tx duplication check
Dec 19, 2018
af3fd32
Extract spending-related logic
Dec 20, 2018
ce005d5
Add braces to if statements
Dec 20, 2018
bc2b37b
Use TTOR to insert tx in mempool after reorg
Jan 9, 2019
6204587
Fix bug in CChainState::DisconnectBlock
Jan 10, 2019
1c711ee
Add comment to BlockAssembler::CreateNewBlock
Jan 11, 2019
e3f3f64
Take into account endianness in uintxxx.Compare
Jan 16, 2019
1c08aa3
Make comment more clear in src/miner.cpp
Jan 16, 2019
2e174b6
Remove unused code
Jan 16, 2019
ad9cc83
Fix reject reason in validation.cpp
Jan 16, 2019
8eaebdb
Enforce LTOR in some functional tests steps
Jan 16, 2019
6f14212
Fix functional tests
Jan 16, 2019
f19764d
Update/fix copyright headers
Jan 16, 2019
7bf6616
Avoid using Boost adaptors::reverse
Jan 16, 2019
68270bb
Use swap instead of move in addForBlock
Jan 17, 2019
d28806b
Apply Unit-e style guidelines
Jan 17, 2019
e1d02f8
Avoid caching iterators
Jan 17, 2019
41f94e4
Undo style fix to ease upstream rebase
Jan 17, 2019
de3cd6d
Add LTOR related tests
Jan 18, 2019
e5e066e
Use ++ptx instead of ptx++
Jan 18, 2019
962b5eb
Add missing references to new files
Jan 18, 2019
8614e82
Rename addForBlock to AddForBlock
Jan 18, 2019
05082e4
Fix feature_ltor.py flakyness
Jan 18, 2019
df7964c
Use CompareLexicographicall instead of Compare
Jan 21, 2019
98c9ff5
Improve naming & comments
Jan 21, 2019
7a1473c
Add unit test for LoadFromBlockInTopologicalOrder
Jan 22, 2019
26b8b37
Apply minor performance improvements
Jan 22, 2019
e2b8e6c
Apply minor random fixes
Jan 22, 2019
f0037ad
Add default methods to SaltedTxidHasher
Jan 22, 2019
7c5619a
Remove TxId
Jan 22, 2019
8bd71f2
Use static_cast instead of C-style cast
Jan 22, 2019
c132614
Add test for binary_blob.CompareLexicographically
Jan 23, 2019
3e37bd5
Rename CompareLexicographically to CompareAsNumber
Jan 23, 2019
95b6c20
Simplify CompareAsNumber
Jan 23, 2019
b16522e
Fix typo
Jan 23, 2019
025789a
Add comment on why we call disconnectpool.clear()
Jan 24, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/hash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ uint64_t CSipHasher::Finalize() const
return v0 ^ v1 ^ v2 ^ v3;
}

uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val)
uint64_t SipHashUint256(const uint64_t k0, const uint64_t k1, const uint256& val)
castarco marked this conversation as resolved.
Show resolved Hide resolved
{
/* Specialized implementation for efficiency */
uint64_t d = val.GetUint64(0);
Expand Down
14 changes: 14 additions & 0 deletions src/miner.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2017 The Bitcoin Core developers
// Copyright (c) 2018 The Bitcoin ABC developers
// Copyright (c) 2018-2019 The Unit-e developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

Expand Down Expand Up @@ -156,6 +158,18 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
int nDescendantsUpdated = 0;
addPackageTxs(nPackagesSelected, nDescendantsUpdated);

// LTOR/CTOR: We ensure that all transactions (except the 0th, coinbase) are
// sorted in lexicographical order. Notice that we don't sort
// blocktemplate->vTxFees nor blocktemplate->vTxSigOpsCost because they are
// not used at all. These two vectors are just a "residue" from Bitcoin's
// PoW mining pools code.
std::sort(
std::begin(pblock->vtx) + 1, std::end(pblock->vtx),
[](const CTransactionRef &a, const CTransactionRef &b) -> bool {
return a->GetHash().CompareAsNumber(b->GetHash()) < 0;
}
);

int64_t nTime1 = GetTimeMicros();

nLastBlockTx = nBlockTx;
Expand Down
90 changes: 90 additions & 0 deletions src/test/mempool_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,4 +571,94 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
SetMockTime(0);
}

BOOST_AUTO_TEST_CASE(DisconnectionTopologicalOrderTest)
{
std::vector<CTransactionRef> vtx;
vtx.reserve(13);

CMutableTransaction first_mtx;
first_mtx.vout.resize(2);
first_mtx.vout[0].scriptPubKey = CScript();
first_mtx.vout[0].nValue = 10 * UNIT;
first_mtx.vout[1].scriptPubKey = CScript();
first_mtx.vout[1].nValue = 10 * UNIT;
auto first_tx_ref = MakeTransactionRef(std::move(first_mtx));
vtx.push_back(first_tx_ref);
castarco marked this conversation as resolved.
Show resolved Hide resolved


// TX1 TX3
// TX0 TX2 ···
// TX1' TX3'
for (unsigned int i=0; i < 8; ++i) {
if (i % 2 == 0) {
CMutableTransaction mtx_a;
CMutableTransaction mtx_b;

mtx_a.vin.resize(1);
mtx_a.vin[0].prevout = COutPoint(vtx.back()->GetHash(), 0);
mtx_a.vin[0].scriptSig = CScript();
mtx_a.vout.resize(1);
mtx_a.vout[0].nValue = 10 * UNIT;
mtx_a.vout[0].scriptPubKey = CScript();

mtx_b.vin.resize(1);
mtx_b.vin[0].prevout = COutPoint(vtx.back()->GetHash(), 1);
mtx_b.vin[0].scriptSig = CScript();
mtx_b.vout.resize(1);
mtx_b.vout[0].nValue = 10 * UNIT;
mtx_b.vout[0].scriptPubKey = CScript();

vtx.emplace_back(MakeTransactionRef(std::move(mtx_a)));
vtx.emplace_back(MakeTransactionRef(std::move(mtx_b)));
} else {
CMutableTransaction mtx;

mtx.vin.resize(2);
auto prev_ptx = vtx.rbegin();
mtx.vin[0].prevout = COutPoint((*prev_ptx)->GetHash(), 0);
mtx.vin[0].scriptSig = CScript();
++prev_ptx;
mtx.vin[1].prevout = COutPoint((*prev_ptx)->GetHash(), 0);
mtx.vin[1].scriptSig = CScript();
mtx.vout.resize(2);
mtx.vout[0].scriptPubKey = CScript();
mtx.vout[0].nValue = 10 * UNIT;
mtx.vout[1].scriptPubKey = CScript();
mtx.vout[1].nValue = 10 * UNIT;

vtx.emplace_back(MakeTransactionRef(std::move(mtx)));
}
}

// We sort transactions in lexicographical order to remove the previous
// topological order.
std::sort(
std::begin(vtx) + 1, std::end(vtx),
[](const CTransactionRef &a, const CTransactionRef &b) -> bool {
return a->GetHash().CompareAsNumber(b->GetHash()) < 0;
}
);

DisconnectedBlockTransactions disconnectpool;
disconnectpool.LoadFromBlockInTopologicalOrder(vtx); // System-Under-Test

std::unordered_set<uint256, SaltedTxidHasher> processed_tx_hashes;
processed_tx_hashes.insert(first_tx_ref->GetHash());

for (
auto it = disconnectpool.queuedTx.get<insertion_order>().rbegin();
it != disconnectpool.queuedTx.get<insertion_order>().rend();
++it
) {
CTransactionRef tx_ref = (*it);
for (CTxIn tx_in : tx_ref->vin) {
// We check that transactions have been ordered topologically
BOOST_CHECK(processed_tx_hashes.count(tx_in.prevout.hash) > 0);
}
processed_tx_hashes.insert(tx_ref->GetHash());
}

disconnectpool.clear();
}

BOOST_AUTO_TEST_SUITE_END()
23 changes: 23 additions & 0 deletions src/test/uint256_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,29 @@ BOOST_AUTO_TEST_CASE( comparison ) // <= >= < >
BOOST_CHECK( R2S < MaxS );
}

BOOST_AUTO_TEST_CASE( number_comparison )
{
const uint160 R1Sp = uint160(std::vector<unsigned char>(R1Array + 12, R1Array + 32));
const uint160 R2Sp = uint160(std::vector<unsigned char>(R2Array + 12, R2Array + 32));
uint160 nums160[] = {ZeroS, OneS, R1Sp, R2Sp, MaxS};
uint256 nums256[] = {ZeroL, OneL, R1L, R2L, MaxL};

for (size_t i=0; i < 5; ++i) {
for (size_t j=0; j < 5; ++j) {
if (i == j) {
BOOST_CHECK(nums160[i].CompareAsNumber(nums160[i]) == 0);
BOOST_CHECK(nums256[i].CompareAsNumber(nums256[i]) == 0);
} else if (i > j) {
BOOST_CHECK(nums160[i].CompareAsNumber(nums160[j]) > 0);
BOOST_CHECK(nums256[i].CompareAsNumber(nums256[j]) > 0);
} else {
BOOST_CHECK(nums160[i].CompareAsNumber(nums160[j]) < 0);
BOOST_CHECK(nums256[i].CompareAsNumber(nums256[j]) < 0);
}
}
}
}

BOOST_AUTO_TEST_CASE( methods ) // GetHex SetHex begin() end() size() GetLow64 GetSerializeSize, Serialize, Unserialize
{
BOOST_CHECK(R1L.GetHex() == R1L.ToString());
Expand Down
85 changes: 80 additions & 5 deletions src/txmempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -564,18 +564,27 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
{
LOCK(cs);
std::vector<const CTxMemPoolEntry*> entries;
for (const auto& tx : vtx)
{
uint256 hash = tx->GetHash();

DisconnectedBlockTransactions disconnectpool;
disconnectpool.LoadFromBlockInTopologicalOrder(vtx);

// We take advantage from LoadFromBlockInTopologicalOrder sorting txns for
// us to ensure that transactions are removed from the mempool in a
// "correct" order, so the mempool is always consistent.
const auto& txns_queue = disconnectpool.GetQueuedTx().get<insertion_order>();
for (auto ptx = txns_queue.rbegin(); ptx != txns_queue.rend(); ++ptx) {
const uint256& hash = (*ptx)->GetHash();

indexed_transaction_set::iterator i = mapTx.find(hash);
if (i != mapTx.end())
entries.push_back(&*i);
}

// Before the txs in the new block have been removed from the mempool, update policy estimates
if (minerPolicyEstimator) {minerPolicyEstimator->processBlock(nBlockHeight, entries);}
for (const auto& tx : vtx)
{

for (auto ptx = txns_queue.rbegin(); ptx != txns_queue.rend(); ++ptx) {
cmihai marked this conversation as resolved.
Show resolved Hide resolved
const auto& tx = *ptx;
txiter it = mapTx.find(tx->GetHash());
if (it != mapTx.end()) {
setEntries stage;
Expand All @@ -585,6 +594,15 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
removeConflicts(*tx);
ClearPrioritisation(tx->GetHash());
}

// Although one would expect that this call is not really needed, the
// DisconnectedBlockTransactions struct has been designed to assert in its
// destructor that all tx have been removed from its queue before, and it
// does not do that by itself, in order to enforce some system-broad
// properties. Here we are using this struct in a different way, so we have
// to take care on our own.
disconnectpool.clear();
castarco marked this conversation as resolved.
Show resolved Hide resolved

lastRollingFeeUpdate = GetTime();
blockSinceLastRollingFeeBump = true;
}
Expand Down Expand Up @@ -1089,3 +1107,60 @@ bool CTxMemPool::TransactionWithinChainLimit(const uint256& txid, size_t chainLi
}

SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}

void DisconnectedBlockTransactions::LoadFromBlockInTopologicalOrder(
const std::vector<CTransactionRef> &vtx
) {
// Save transactions to re-add to mempool at end of reorg
for (const auto &tx : vtx) {
const auto it = queuedTx.find(tx->GetHash());
if (it != queuedTx.end()) {
continue;
}

// Queue transaction to be re-inserted into the mempool
addTransaction(tx);

// Fill in the set of parents.
std::unordered_set<uint256, SaltedTxidHasher> parents;
for (const CTxIn &in : tx->vin) {
parents.insert(in.prevout.hash);
}

// In order to make sure we keep things in topological order, we check
// if we already know of the parent of the current transaction. If so,
// we remove them from the set and then add them back.
while (!parents.empty()) {
std::unordered_set<uint256, SaltedTxidHasher> worklist;
castarco marked this conversation as resolved.
Show resolved Hide resolved
parents.swap(worklist);

for (const uint256 &txid : worklist) {
// If we do not have that txid in the set, nothing needs to be
// done.
auto pit = queuedTx.find(txid);
if (pit == queuedTx.end()) {
continue;
}

// We have parent in our set, we reinsert them at the right
// position
const CTransactionRef ptx = *pit;
queuedTx.erase(pit);
queuedTx.insert(ptx);

// And we make sure ancestors are covered.
for (const CTxIn &in : ptx->vin) {
parents.insert(in.prevout.hash);
}
}
}

}

while (DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE * 1000) {
// Drop the earliest entry, and remove its children from the mempool.
auto it = queuedTx.get<insertion_order>().begin();
mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
removeEntry(it);
}
}
18 changes: 17 additions & 1 deletion src/txmempool.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,11 +354,19 @@ class SaltedTxidHasher
{
private:
/** Salt */
const uint64_t k0, k1;
frolosofsky marked this conversation as resolved.
Show resolved Hide resolved
uint64_t k0, k1;

public:
SaltedTxidHasher();
castarco marked this conversation as resolved.
Show resolved Hide resolved

SaltedTxidHasher(const SaltedTxidHasher&) = default;
SaltedTxidHasher(SaltedTxidHasher&&) = default;

SaltedTxidHasher& operator=(const SaltedTxidHasher&) = default;
SaltedTxidHasher& operator=(SaltedTxidHasher&&) = default;

~SaltedTxidHasher() = default;

size_t operator()(const uint256& txid) const {
return SipHashUint256(k0, k1, txid);
}
Expand Down Expand Up @@ -768,12 +776,20 @@ struct DisconnectedBlockTransactions {
return memusage::MallocUsage(sizeof(CTransactionRef) + 6 * sizeof(void*)) * queuedTx.size() + cachedInnerUsage;
}

const indexed_disconnected_transactions &GetQueuedTx() const {
return queuedTx;
}

void addTransaction(const CTransactionRef& tx)
{
queuedTx.insert(tx);
cachedInnerUsage += RecursiveDynamicUsage(tx);
}

// Add entries for a block while reconstructing the topological ordering so
// they can be added back to the mempool simply.
void LoadFromBlockInTopologicalOrder(const std::vector<CTransactionRef> &vtx);

// Remove entries based on txid_index, and update memory usage.
void removeForBlock(const std::vector<CTransactionRef>& vtx)
{
Expand Down
17 changes: 17 additions & 0 deletions src/uint256.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2017 The Bitcoin Core developers
// Copyright (c) 2018 The Bitcoin ABC developers
// Copyright (c) 2018-2019 The Unit-e developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

Expand Down Expand Up @@ -45,6 +47,21 @@ class base_blob

inline int Compare(const base_blob& other) const { return memcmp(data, other.data, sizeof(data)); }

inline int CompareAsNumber(const base_blob& other) const {
for (int i = WIDTH - 1; i >= 0; --i) {
uint8_t a = data[i];
uint8_t b = other.data[i];
if (a > b) {
return 1;
}
if (a < b) {
return -1;
}
}

return 0;
}

friend inline bool operator==(const base_blob& a, const base_blob& b) { return a.Compare(b) == 0; }
friend inline bool operator!=(const base_blob& a, const base_blob& b) { return a.Compare(b) != 0; }
friend inline bool operator<(const base_blob& a, const base_blob& b) { return a.Compare(b) < 0; }
Expand Down
Loading