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

estimatefee / estimatepriority RPC methods #3959

Merged
merged 3 commits into from Jun 6, 2014

Conversation

gavinandresen
Copy link
Contributor

New RPC methods: return an estimate of the fee (or priority) a transaction needs to be likely to confirm in a given number of blocks.

Fee-per-kilobyte estimates for 1..10 confirmations from this code:

gavin$ for i in {1..10}; do ./bitcoind estimatefee $i; done
0.00044643
0.00044248
0.00044053
0.00029499
0.00020492
0.00014970
0.00012981
0.00012190
0.00011769
0.00011217

Mike Hearn created the first version of this method for estimating fees.
It works as follows:

For transactions that took 1 to N (I picked N=25) blocks to confirm, keep N buckets with at most 100 entries in each recording the fees-per-kilobyte paid by those transactions.

(separate buckets are kept for transactions that confirmed because they are high-priority)

The buckets are filled as blocks are found, and are saved/restored as part of the mempool.dat file.

A few variations on Mike's initial scheme:

To estimate the fee needed for a transaction to confirm in X buckets, all of the samples in all of the buckets are used and a median of all of the data is used to make the estimate. For example, imagine
25 buckets each containing the full 100 entries. Those 2,500 samples are sorted, and the estimate of the fee needed to confirm in the very next block is the 50'th-highest-fee-entry in that sorted list; the estimate of the fee needed to confirm in the next two blocks is the 150'th-highest-fee-entry, etc.

That algorithm has the nice property that estimates of how much fee you need to pay to get confirmed in block N will always be greater than or equal to the estimate for block N+1. It would clearly be wrong
to say "pay 11 uBTC and you'll get confirmed in 3 blocks, but pay 12 uBTC and it will take LONGER".

A single block will not contribute more than 10 entries to any one bucket, so a single miner and a large block cannot overwhelm the estimates.

return i
return -1

def gather_inputs(from_node, amount_needed):
Copy link
Contributor

Choose a reason for hiding this comment

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

A lot of this stuff looks like it should be in a utility library (the main function too), otherwise it's getting copy/pasted everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moving useful functions to util.py, but am going to hold off on moving main; there's another pull request that bundles everything up into a framework...

@gavinandresen
Copy link
Contributor Author

Rebased on master, and tweaked to address Mike's comments (thanks for the review!).

@gavinandresen
Copy link
Contributor Author

Pull-tester errors were with the regression test:

pull-tester is running python 2.6 (fixed by using subprocess.check_call instead of python2.7 subprocess.check_output).

@laanwj laanwj added this to the 0.10.0 milestone May 6, 2014
@mikehearn
Copy link
Contributor

What's the status of this work? Does it need more review or testing or what?

@laanwj
Copy link
Member

laanwj commented May 7, 2014

It's queued to be merged for 0.10, so after the 0.9.2 branch split-off.
But more review and testing is always welcome.

@@ -610,19 +595,19 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)

// tool tips
QString toolTip1 = tr("This label turns red, if the transaction size is greater than 1000 bytes.") + "<br /><br />";
toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee)) + "<br /><br />";
toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::minTxFee.GetFee(1000))) + "<br /><br />";
Copy link
Member

Choose a reason for hiding this comment

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

Nit: There are a lot of getFee(1000)'s. I don't have an objection to them, although on first reading I was confused ('what is this magic 1000 number'). But instead of suggesting defining a constant BYTES_PER_KB :p I think it would be nicer to have a function getFeePerKB() that's simply an inline for getFee(1000).

@gavinandresen
Copy link
Contributor Author

Nit-picked : replace GetFee(1000) with inline GetFeePerK() method.

@gavinandresen
Copy link
Contributor Author

Rebased on master.

I'm very happy with how quickly this generates good estimates; after just three or four blocks it has a good idea of what priority or fee is needed to get into the next couple of blocks.

@sipa
Copy link
Member

sipa commented May 29, 2014

Save/restore of the mempool seems quite dangerous in making some unconfirmed transactions potentially live forever in the network...

@jgarzik
Copy link
Contributor

jgarzik commented May 29, 2014

Nod -- you only want to save/restore once we have transactions expiring from the mempool (janitor etc.) after X blocks.

@petertodd
Copy link
Contributor

@sipa Yup. Even with expiration it makes many DoS attacks and similar vulnerabilities much more effective. For instance OOM crashes are hard to exploit right now because the txs are hard to propagate; not true with save restore.

@jgarzik
Copy link
Contributor

jgarzik commented May 30, 2014

In general, lacking other tools, clearing the mempool [implicitly] at startup is a valuable tool.

@gavinandresen
Copy link
Contributor Author

Consensus is saving/restoring memory pool is dangerous, so I've removed that functionality.

Fee estimates ARE saved/restored, to datadir fee_estimates.dat file.

Use CFeeRate instead of an int64_t for quantities that are
fee-per-size.

Helps prevent unit-conversion mismatches between the wallet,
relaying, and mining code.
Choose ports at startup based on PID, so multiple regression tests
can run on the same system at the same time.
New RPC methods: return an estimate of the fee (or priority) a
transaction needs to be likely to confirm in a given number of
blocks.

Mike Hearn created the first version of this method for estimating fees.
It works as follows:

For transactions that took 1 to N (I picked N=25) blocks to confirm,
keep N buckets with at most 100 entries in each recording the
fees-per-kilobyte paid by those transactions.

(separate buckets are kept for transactions that confirmed because
they are high-priority)

The buckets are filled as blocks are found, and are saved/restored
in a new fee_estiamtes.dat file in the data directory.

A few variations on Mike's initial scheme:

To estimate the fee needed for a transaction to confirm in X buckets,
all of the samples in all of the buckets are used and a median of
all of the data is used to make the estimate. For example, imagine
25 buckets each containing the full 100 entries. Those 2,500 samples
are sorted, and the estimate of the fee needed to confirm in the very
next block is the 50'th-highest-fee-entry in that sorted list; the
estimate of the fee needed to confirm in the next two blocks is the
150'th-highest-fee-entry, etc.

That algorithm has the nice property that estimates of how much fee
you need to pay to get confirmed in block N will always be greater
than or equal to the estimate for block N+1. It would clearly be wrong
to say "pay 11 uBTC and you'll get confirmed in 3 blocks, but pay
12 uBTC and it will take LONGER".

A single block will not contribute more than 10 entries to any one
bucket, so a single miner and a large block cannot overwhelm
the estimates.
@jgarzik
Copy link
Contributor

jgarzik commented Jun 6, 2014

ACK. Speaking conservatively, appears to be low impact as implemented at first glance.

The largest impact is to existing operations is the hook that is executed when a block appears, necessitating the removal of TXs from the mempool. At a quick glance, it was difficult to discern the big-O() of this operation, but at least it is all in CPU/RAM, and only executed once per incoming new block.

One nit (perhaps for post merge): I don't like the mempool itself doing the reading. Would prefer a helper function outside the mempool class that perform the necessary I/O. Our normal pattern is a pure implement-serialize chain until actual file I/O is needed. This seems to mix metaphors a bit more than usual.

@@ -536,26 +536,11 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
{
nChange = nAmount - nPayFee - nPayAmount;

// if sub-cent change is required, the fee must be raised to at least CTransaction::nMinTxFee
Copy link
Member

Choose a reason for hiding this comment

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

Wouldn't we want to keep this block, but do it to prevent dust change rather than subcent change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Dust change is handled at line 543.

@BitcoinPullTester
Copy link

Automatic sanity-testing: FAILED BUILD/TEST, see http://jenkins.bluematt.me/pull-tester/171ca7745e77c9f78f26556457fe64e5b2004a75 for binaries and test log.

This could happen for one of several reasons:

  1. It chanages paths in makefile.linux-mingw or otherwise changes build scripts in a way that made them incompatible with the automated testing scripts (please tweak those patches in qa/pull-tester)
  2. It adds/modifies tests which test network rules (thanks for doing that), which conflicts with a patch applied at test time
  3. It does not build on either Linux i386 or Win32 (via MinGW cross compile)
  4. The test suite fails on either Linux i386 or Win32
  5. The block test-cases failed (lookup the first bNN identifier which failed in https://github.com/TheBlueMatt/test-scripts/blob/master/FullBlockTestGenerator.java)

If you believe this to be in error, please ping BlueMatt on freenode or TheBlueMatt here.

This test script verifies pulls every time they are updated. It, however, dies sometimes and fails to test properly. If you are waiting on a test, please check timestamps to verify that the test.log is moving at http://jenkins.bluematt.me/pull-tester/current/
Contact BlueMatt on freenode if something looks broken.

@laanwj laanwj merged commit 171ca77 into bitcoin:master Jun 6, 2014
laanwj added a commit that referenced this pull request Jun 6, 2014
171ca77 estimatefee / estimatepriority RPC methods (Gavin Andresen)
0193fb8 Allow multiple regression tests to run at once (Gavin Andresen)
c6cb21d Type-safe CFeeRate class (Gavin Andresen)
@gmaxwell
Copy link
Contributor

If mempool saving ever comes back as a proposal we should keep in mind that doing so presents a pretty strong watermarking attack. (... not that we don't have many other watermarking attacks.)

@bitcoin bitcoin locked as resolved and limited conversation to collaborators Sep 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants