Skip to content

Conversation

sipa
Copy link
Member

@sipa sipa commented Aug 3, 2016

Fixes #8433 and builds upon #8392.

This adds a file mempool.dat to the data directory, which is created every 10 minutes, and at shutdown. At startup, it is loaded after loading/reindexing/activation of blocks (in the background).

It only grabs a mempool lock while copying and sorting mempool shared pointers (similar to responding to a mempool BIP35 message), which takes 50-100ms here for 1 GB mempool. Loading from disk seems to take up to dozens of seconds but happens in the background.

@fanquake
Copy link
Member

fanquake commented Aug 3, 2016

We'll need to add mempool.dat to doc/files.md

@dcousens
Copy link
Contributor

dcousens commented Aug 3, 2016

Why not behind a command line flag?

@laanwj
Copy link
Member

laanwj commented Aug 3, 2016

Concept ACK.

Why not behind a command line flag?

Agreed. It can be enabled by default.
But people may want to disable this functionality, the per-10 minute dump could interfere with benchmarks and such for example.

@@ -6794,6 +6793,107 @@ ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::D
return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache);
}

bool LoadMempool(void)
{
FILE* filestr = fopen((GetDataDir() / "mempool.dat").c_str(), "r");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.string().c_str()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work fine?

Copy link
Contributor

@paveljanik paveljanik Aug 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nono, travis failed because of this (https://travis-ci.org/bitcoin/bitcoin/jobs/149377465#L2149).

Copy link
Contributor

@dcousens dcousens Aug 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC the fopen parameters are restrict (or often are in the impl), and assumed to be there for the lifetime of the file pointer... I don't think this temporary holds those variants and might cause UB?

I could be wrong.
edit: that may only be in the C standard, not C++

I suspect it is completely irrelevant, seeing how prevalent this pattern is throughout the code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

restrict means the argument is assumed not to alias any other parameter, I think?

@paveljanik
Copy link
Contributor

Concept ACK

000000e0  7a 8e 6e 84 83 ab c4 ca  20 fb 9b 88 ac 00 00 00  |z.n..... .......|
000000f0  00 00 00 00 00 0f 6a 0d  48 61 6c 6c 6f 55 62 69  |......j.HalloUbi|
00000100  72 63 68 74 21 00 00 00  00 0d 9a a1 57 00 00 00  |rcht!.......W...|

As we dump raw txes from the mempool, this will conflict with current status of "antivirus-compliant" storage and dumping mempool.dat can stop the bitcoind from running, ie. new DoS vector.

@paveljanik
Copy link
Contributor

2016-08-03 07:29:00 Dumped mempool: 1.9e-05s to copy, 0.005925s to dump

@laanwj
Copy link
Member

laanwj commented Aug 3, 2016

As we dump raw txes from the mempool, this will conflict with current status of "antivirus-compliant" storage and dumping mempool.dat can stop the bitcoind from running, ie. new DoS vector.

A fair point. Though a missing, or corrupted mempool.dat should never cause bitcoind to stop running. At most this will lose the mempool contents (at next start) due to the file being quarantined.

@dcousens
Copy link
Contributor

dcousens commented Aug 3, 2016

More XOR?

@pstratem
Copy link
Contributor

pstratem commented Aug 3, 2016

Indeed this probably should be XOR'd with a random key.

@paveljanik
Copy link
Contributor

At most this will lose the mempool contents due to the file being quarantined.

AV can be configured to stop bitcoind with a dialog: “This app is writing a virus to your disk. OK | Error”. This is outside of bitcoind

@laanwj laanwj added the Mempool label Aug 3, 2016
@jonasschnelli
Copy link
Contributor

Concept ACK and the code size is surprisingly small!

The only thing that I miss (not directly related to this PR) is an opportunity to keep a history of some major mempool data (feerate, size, tx rate, etc.). Another option would be keeping ~24*6 dumps per day that would could be stored in file-diff-deltas to allow generating any types of statistics.

@morcos
Copy link
Contributor

morcos commented Aug 3, 2016

I don't really see a huge benefit in doing this. It seems like unless you are immediately restarting, you'll be wasting a lot of time trying to load already confirmed transactions or low fee spam. That said I don't object to making it a command line argument, but I would suggest it default to off.

What's the rationale for running it every 10 minutes? You can copy it from one running node to start another or to handle crashes?

@paveljanik
Copy link
Contributor

I think it should be off by default. And when it is, we can even skip XORing it, I think as it is expert level feature anyway.

Or the other way: create a RPC to dump mempool and that's all. But this way we loose the save-on-restart feature which is nice (which can be worked around by dumpmempool and restart).

@sipa
Copy link
Member Author

sipa commented Aug 3, 2016

@morcos The reason for dumping every 10 minutes is so that prioritization data is not lost in case of a crash.

But perhaps we should have separate files for that. prioritization.dat which is continuously dumped (maybe only after mapDeltas changed?), and mempool.dat which is only saved at shutdown?

@maflcko
Copy link
Member

maflcko commented Aug 3, 2016

wasting a lot of time trying to load already confirmed transactions or low fee spam

Could it help to save the current block height to the mempool.dat and then discard mempool.dat on start when current height > 3 + height when mempool was saved?

@jgarzik
Copy link
Contributor

jgarzik commented Aug 3, 2016

This has split into two threads, really:

  1. mempool persistence across restarts
  2. persisting prio deltas across restarts & crashes
    2.1) side note - other state such as wallet locked coins (lockunspent) should be persisted also

Persisting bits of state like prio deltas is useful, and seems applicable to a continuous-log or database solution, that can be restored at startup.

As for mempool restore...

Standard rhetorical questions: Who will really use mempool restore, and how often?

  • Historically, users really do not like features that pause shutdown for a long time. We spent time optimizing bitcoind shutdown in the past.
  • Dump on shutdown does not provide crash protection (as @sipa noted)
  • Alternatives to dump-on-shutdown involve a lot of disk traffic (logging or db soln) at runtime, and introduce additional I/O stress points for remote attackers to exploit.
  • Therefore, the only use case for dump-on-shutdown is a rapid node restart -- if restart is not rapid, much of the data will be stale, only reloading slow-confirming transactions uninteresting to miners.
  • Someone restarting their node tends to be an expert-only or dev-only operation; normal field operation - unattended operation or standard user session - will never see this feature.

mempool restore seems likely to be a little used, expert-only feature.

@dcousens
Copy link
Contributor

dcousens commented Aug 3, 2016

mempool restore seems likely to be a little used, expert-only feature.

Somewhat agreed, and as a regular 'power' user of bitcoind, I personally already have my own RPC calls equivalent to dumpmempool for this.

@gmaxwell
Copy link
Contributor

gmaxwell commented Aug 4, 2016

@jgarzik This is functionality directly requested by miners, the absence of it creates an operational burden because nodes then end up making small blocks after being restarted.

persisting prio deltas across restarts & crashes

This isn't useful without the transactions themselves-- peers will not re-relay transactions they previously sent. So you could save priorities, but all of them would be almost completely useless.

users really do not like features that pause shutdown for a long time.

Indeed, if it takes a long time that is an issue. But this should be writing no more than about 150 MB of data, which should take under a second. If it's taking a long time then that needs to be addressed.

One solution if that is an issue is to simply not write it at shutdown, but to only use the regular snapshots.

and introduce additional I/O stress points for remote attackers to exploit

I don't follow how remote attackers interact here; it wouldn't make sense to have any of this behavior be remotely triggerable.

if restart is not rapid, much of the data will be stale,

It takes several hours for a full mempool to lose its utility.

I suppose it would be reasonable to not even bother trying to load it if it's more than three days old.

Mempool sync is also a bandwidth concern for future mempool sync efforts-- if it's lost on restart, than every quick restart would waste bandwidth.

Similarly for block transmission-- latency and bandwidth usage will be adversely effected if mempool is lost on restart. It takes about 12 hours in my tests for a node to recover its compact block reconstruction performance after restart, and it can take several seconds to accept a block due to the empty signature cache.

These pressures against restart are an operational burden, because then you're pressured to try to find ways to avoid restarting-- e.g. running extra nodes, or avoiding changing settings.

Separately, the cold mempool means that users and developers spend time in a non-representative state after they try changing settings; which makes it much harder to iterate around settings changes.

( Sorry if some of these points are redundant with #8433 )

@petertodd
Copy link
Contributor

Concept ACK, and a very useful feature!

Though I'd suggest making it off by default for now so that nodes that don't need this don't have the risk of a bug creating a restart loop - less risk if we start in a clean state by default.

@pstratem
Copy link
Contributor

pstratem commented Aug 4, 2016

@petertodd With the exception of the priority deltas the transactions are going through normal mempool accept logic.

I can't see this causing a restart loop unless there's a bug in the priority handling logic or a remotely exploitable DoS issue anyways.

@dcousens
Copy link
Contributor

dcousens commented Aug 4, 2016

I can't see this causing a restart loop unless there's a bug in the priority handling logic or a remotely exploitable DoS issue anyways.

Which may only be trigger-able under particular circumstance, of which may be captured in the current mempool state?

@petertodd
Copy link
Contributor

@pstratem Hmm, that's a good point. Also, as long as we're only saving the mempool in a normal, user-triggered, shutdown all transactions in the saved mempool should be fine.

I'll take back my objection.

@luke-jr
Copy link
Member

luke-jr commented Aug 4, 2016

It seems like unless you are immediately restarting, you'll be wasting a lot of time trying to load already confirmed transactions or low fee spam.

Eloipool does a save/restore state when it restarts, and one thing it does is write a timestamp early on in the dump. This way, when loading from the dump, it can see if the data is irrelevant and ignore the file. Perhaps that would make sense here too?

Any reason to write out the entire thing every interval, rather than just having another LevelDB database?

@jgarzik
Copy link
Contributor

jgarzik commented Aug 4, 2016

@gmaxwell Those concerns seem to be met by remote mempool download also, which @morcos suggested elsewhere in the thread -- and indeed was one original purpose of BIP 35 mempool message -- maintaining miner's mempools and not losing lucrative miner fees due to restart.

Remote download over LAN is also potentially faster, something Google noticed when seeing increased performance through remote page caches: http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/43886.pdf

Remote download also gets you fresh transactions which the db/log will not have - fuller blocks, more miners fees.

and introduce additional I/O stress points for remote attackers to exploit

I don't follow how remote attackers interact here; it wouldn't make sense to have any of this
behavior be remotely triggerable.

The relevant portion that answers your confusion was elided: "Alternatives to dump-on-shutdown [...]" This refers to, e.g. periodic dumps, streaming transactions to a leveldb or WAL log, which occur at runtime not shutdown.

@gmaxwell
Copy link
Contributor

gmaxwell commented Aug 4, 2016

Those concerns seem to be met by remote mempool download also, which @morcos suggested elsewhere in the thread -- and indeed was one original purpose of BIP 35 mempool message -- maintaining miner's mempools and not losing lucrative miner fees due to restart.

Preservation of the mempool is a prerequisite for mempool sync. Otherwise you'd have 150 MB of transfer (burning resources of remote nodes) each time someone restarts to twiddle some configure. I think the mempool message is unsuitable for synchronization generally, due to overhead.

[and while the latency concerns related to compact blocks would be addressed by a bulk download, the bandwidth savings would be lost]

Remote download over LAN is also potentially faster, something Google

In Google datacenters not on our P2P network; in any case I believe the load is bottlenecked on any ordinary host. (and were it faster, it wouldn't matter-- resource usage needs to be heavily weighed).

and introduce additional I/O stress points for remote attackers to exploit

I don't follow how remote attackers interact here; it wouldn't make sense to have any of this behavior be remotely triggerable.

The relevant portion that answers your confusion was elided: "Alternatives to dump-on-shutdown [...]" This refers to, e.g. periodic dumps, streaming transactions to a leveldb or WAL log, which occur at runtime not shutdown.

There isn't any leveldb involved here; but regardless-- I still don't see where the "remote attackers" part that I was asking about comes in.

@petertodd
Copy link
Contributor

Notable how remote mempool load on startup could be increase the effects of a DoS attack due to all the extra bandwidth wasted as nodes restart after crashing; it'd be especially harmful if a remote crash exploit could be found if people ever setup nodes to restart automatically.

On 3 August 2016 21:56:34 GMT-07:00, Gregory Maxwell notifications@github.com wrote:

Those concerns seem to be met by remote mempool download also, which
@morcos suggested elsewhere in the thread -- and indeed was one
original purpose of BIP 35 mempool message -- maintaining miner's
mempools and not losing lucrative miner fees due to restart.

Preservation of the mempool is a prerequisite for mempool sync.
Otherwise you'd have 150 MB of transfer (burning resources of remote
nodes) each time someone restarts to twiddle some configure. I think
the mempool message is unsuitable for synchronization generally, due to
overhead.

Remote download over LAN is also potentially faster, something Google

In Google datacenters not on our P2P network; in any case I believe the
load is bottlenecked on any ordinary host. (and were it faster, it
wouldn't matter-- resource usage needs to be heavily weighed).

and introduce additional I/O stress points for remote attackers to
exploit

I don't follow how remote attackers interact here; it wouldn't make
sense to have any of this behavior be remotely triggerable.

The relevant portion that answers your confusion was elided:
"Alternatives to dump-on-shutdown [...]" This refers to, e.g. periodic
dumps, streaming transactions to a leveldb or WAL log, which occur at
runtime not shutdown.

There isn't any leveldb involved here; but regardless-- I still don't
see where the "remote attackers" part that I was asking about comes in.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub:
#8448 (comment)

@sipa
Copy link
Member Author

sipa commented Aug 4, 2016

  • Make it off by default: seems reasonable; not every node has a requirement to have a well-calibrated mempool immediately at startup. It does help for BIP152 block relay, but many nodes also likely won't restart sufficiently fast to benefit.
  • XORing a key: sigh, ok
  • Checking how old the mempool is: height won't work, as at the time you restart, you won't have learned about blocks that have been found in the mean time. Using time would work, but meh... loading is fast and in the background
  • Using BIP35 instead: inefficient and unpredictable for resources (you don't know the size of a remote mempool, and can't tell them to not send 20 gigabytes worth of txids), and won't work for prioritized transactions. I do think we should explore network-based mempool syncing independently, though.
  • Separating dumping priority data and mempool data: the problem is that for prioritized transactions, we need both, and there is no guaranteed way to load these from anywhere else. What about periodically dumping but only for prioritized transactions, and dumping the full mempool at shutdown (when configured).
  • Keeping historical record: much more complicated and can better be done in other ways (separate statistics module that persists to disk).

@sipa
Copy link
Member Author

sipa commented Oct 31, 2016

Rebased with the following changes:

  • Several nits addressed (missing lock, add version constant, no big screamy errors on failure).
  • Periodic dump is gone; it only happens at shutdown now.
  • Expired entries are skipped on load (making a load of a week-old mempool.dat instantaneous).

XORed dump is not implemented, this can be done later. As the regular dump seemed controversial, I've removed it now (it can always be added back later).

file.fclose();
RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat");
int64_t last = GetTimeMicros();
LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*0.000001, (last-mid)*0.000001);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This results in

2016-11-01 08:30:08 Dumped mempool: 1.9e-05s to copy, 0.006452s to dump

here on testnet. What about using ms instead?

@paveljanik
Copy link
Contributor

ACK 582068a

XOR dumping nit can be ignored now, because we are dumping mempool on shutdown only.

@maflcko maflcko added this to the 0.14.0 milestone Nov 1, 2016
@laanwj laanwj merged commit 582068a into bitcoin:master Nov 2, 2016
laanwj added a commit that referenced this pull request Nov 2, 2016
582068a Add mempool.dat to doc/files.md (Pieter Wuille)
3f78562 Add DumpMempool and LoadMempool (Pieter Wuille)
ced7c94 Add AcceptToMemoryPoolWithTime function (Pieter Wuille)
c3efb58 Add feedelta to TxMempoolInfo (Pieter Wuille)
@morcos
Copy link
Contributor

morcos commented Nov 11, 2016

@sipa sorry for commenting on this late, but could we have made it a bit more efficient by storing mapDeltas before the transactions, so then we don't have to do 2 loops of prioritiseTransaction? not sure if its worth it to change now....

@paveljanik
Copy link
Contributor

@morcos This is not in released version, so I think if there is a benefit, we can still change it. And of course, we can safely ignore this file every time...

luke-jr pushed a commit to bitcoinknots/bitcoin that referenced this pull request Dec 21, 2016
luke-jr pushed a commit to bitcoinknots/bitcoin that referenced this pull request Dec 21, 2016
luke-jr pushed a commit to bitcoinknots/bitcoin that referenced this pull request Dec 21, 2016
luke-jr pushed a commit to bitcoinknots/bitcoin that referenced this pull request Dec 21, 2016
@sipa sipa mentioned this pull request Jan 10, 2017
18 tasks
codablock pushed a commit to codablock/dash that referenced this pull request Jan 13, 2018
582068a Add mempool.dat to doc/files.md (Pieter Wuille)
3f78562 Add DumpMempool and LoadMempool (Pieter Wuille)
ced7c94 Add AcceptToMemoryPoolWithTime function (Pieter Wuille)
c3efb58 Add feedelta to TxMempoolInfo (Pieter Wuille)
andvgal pushed a commit to energicryptocurrency/gen2-energi that referenced this pull request Jan 6, 2019
582068a Add mempool.dat to doc/files.md (Pieter Wuille)
3f78562 Add DumpMempool and LoadMempool (Pieter Wuille)
ced7c94 Add AcceptToMemoryPoolWithTime function (Pieter Wuille)
c3efb58 Add feedelta to TxMempoolInfo (Pieter Wuille)
CryptoCentric pushed a commit to absolute-community/absolute that referenced this pull request Feb 15, 2019
582068a Add mempool.dat to doc/files.md (Pieter Wuille)
3f78562 Add DumpMempool and LoadMempool (Pieter Wuille)
ced7c94 Add AcceptToMemoryPoolWithTime function (Pieter Wuille)
c3efb58 Add feedelta to TxMempoolInfo (Pieter Wuille)
furszy added a commit to PIVX-Project/PIVX that referenced this pull request Jan 23, 2021
9b9c616 Fix missing zapwallettxes mode in wallet_hd.py functional test (furszy)
d6d0ad9 [logs] fix zapwallettxes startup logs (John Newbery)
006c503 [wallet] fix zapwallettxes interaction with persistent mempool (John Newbery)
c6d45c6 Adapting and connecting mempool_persist.py functional test to the test runner. (furszy)
4f26a4e Control mempool persistence using a command line parameter. (John Newbery)
5d949de [Qt] Do proper shutdown (Jonas Schnelli)
e60da98 Allow shutdown during LoadMempool, dump only when necessary (Jonas Schnelli)
c0a0e81 Moving TxMempoolInfo tx member to CTransactionRef (furszy)
f0c2255 Add mempool.dat to doc/files.md (furszy)
8e52226 Add DumpMempool and LoadMempool (Pieter Wuille)
44c635d Add AcceptToMemoryPoolWithTime function (Pieter Wuille)
6bbc6a9 Add feedelta to TxMempoolInfo (Pieter Wuille)
9979f3d [mempool] move removed and conflicts transaction ref list to vector. (furszy)
4f672c2 Make removed and conflicted arguments optional to remove (Pieter Wuille)
e51c4b8 Bypass removeRecursive in removeForReorg (Pieter Wuille)
54cf7c0 Get rid of CTxMempool::lookup() entirely (furszy)
35bc2a9 Finished the switch CTransaction storage in mempool to CTransactionRef and introduced the timeLastMempoolReq coming from btc#8080. (furszy)
d10583b An adapted version of btc@b5599147533103efea896a1fc4ff51f2d3ad5808 (furszy)
cb4fc6c An adapted version of btc@ed7068302c7490e8061cb3a558a0f83a465beeea (furszy)
9645775 Split up and optimize transaction and block inv queues (furszy)
68bc68f Don't do mempool lookups for "mempool" command without a filter (furszy)
7624823 mapNextTx: use pointer as key, simplify value (furszy)
191c62e Return mempool queries in dependency order (Pieter Wuille)
23c9f3e Eliminate TX trickle bypass, sort TX invs for privacy and priority. (furszy)
6ebfd17 tiny test fix for mempool_tests (Alex Morcos)
8c0016e Check all ancestor state in CTxMemPool::check() (furszy)
91c6096 Add ancestor feerate index to mempool (Suhas Daftuar)
64e84e2 Add ancestor tracking to mempool  This implements caching of ancestor state to each mempool entry, similar to descendant tracking, but also including caching sigops-with-ancestors (as that metric will be helpful to future code that implements better transaction selection in CreatenewBlock). (furszy)
8325bb4 Fix mempool limiting for PrioritiseTransaction Redo the feerate index to be based on mining score, rather than fee. (furszy)
1fa40ac Remove work limit in UpdateForDescendants()  The work limit served to prevent the descendant walking algorithm from doing too much work by marking the parent transaction as dirty. However to implement ancestor tracking, it's not possible to similarly mark those descendant transactions as dirty without having to calculate them to begin with.  This commit removes the work limit altogether. With appropriate chain limits (-limitdescendantcount) the concern about doing too much work inside this function should be mitigated. (furszy)
ba32375 Rename CTxMemPool::remove -> removeRecursive  remove is no longer called non-recursively, so simplify the logic and eliminate an unnecessary parameter (furszy)
c30fa16 CTxMemPool::removeForBlock now uses RemoveStaged (furszy)

Pull request description:

  Ending up 2020 with a large PR :).

  Included a good number of performance and privacy improvements over the mempool and inv processing/sending areas + added mempool cache dump/load.
  Almost finishing with #1726 work, getting closer to 9725, and getting closer to be able to decouple the message processing thread <-> wallet and the wallet <-> GUI locks dependencies (which will be another long story.. but well, step by step).

  The final goal is clear, a much faster syncing process using pivx-qt (a good speed up for pivxd as well), smoother visual navigation when big wallets are syncing, less thread synchronization issues, among all of the improvements that will be coming with the backports adaptations.

  Adapted the following PRs to our sources:

  bitcoin#7062 —> only eb30666.
  bitcoin#7174 —> complete.
  bitcoin#7562 —> only c5d746a.
  bitcoin#7594 —> complete.
  bitcoin#7840 —> complete.
  bitcoin#7997 —> complete.
  bitcoin#8080 —> complete
  bitcoin#8126 —> except e9b4780 (we don't have the mapRelay) and c2a4724 (we don't have the relay expiration vector).
  bitcoin#8448 —> complete
  bitcoin#8515 —> complete
  bitcoin#9408 —> complete
  bitcoin#9966 —> complete
  bitcoin#10330 —> complete

ACKs for top commit:
  random-zebra:
    ACK 9b9c616
  Fuzzbawls:
    ACK 9b9c616

Tree-SHA512: 186bd09bbb19b55ec0d46dc27291663a0df2d60d8238c661a8c9b8cbf3677b6f8a92fa56bd7346a3cb5293712eeccc5d6542ee3c441d35a4f61e7d12e2ce489a
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Aug 16, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Persist mempool, arrival times, and prioritisetransaction across restarts