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

All the feerate you can eat... #1846

Conversation

rustyrussell
Copy link
Contributor

Lots of things in here, mainly weaning us off manual fee manipulation where possible.

@@ -444,6 +459,20 @@ static void json_feerates(struct command *cmd,
if (missing)
json_add_string(response, "warning",
"Some fee estimates unavailable: bitcoind startup?");
else {
json_object_start(response, "onchain_estimates");
Copy link
Collaborator

Choose a reason for hiding this comment

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

change onchain_estimates to onchain_fee_estimates to make it more descriptive

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed!

@rustyrussell
Copy link
Contributor Author

Rebased and folded @SimonVrouwe 's suggested fix...

@rustyrussell rustyrussell changed the title See branch name... All the feerate you can eat... Aug 16, 2018
@SimonVrouwe
Copy link
Collaborator

Like the idea of feehistory, but it should take into account the time constant poll_second, which can change using --dev-bitcoind-poll.

It looks feehistory is currently only used to determine the min and max feerate estimates of the recent past.

min = feerate;
/* If one of last three was an outlier, use that. */
for (size_t i = 0; i < FEE_HISTORY_NUM; i++) {
if (feehistory[i] < min)
min = feehistory[i];

/* If one of last three was an outlier, use that. */
for (size_t i = 0; i < FEE_HISTORY_NUM; i++) {
if (feehistory[i] > feerate)
feerate = feehistory[i];

Just some thoughts:

  • Perhaps feehistory should consist of just the min/max outliers of past (lets say) 90 seconds.
  • Not sure how to code that, maybe by adding timestamp or a (poll_counter) to these outliers to replace them when too old.

@SimonVrouwe
Copy link
Collaborator

Or forget about timestamping, set a floor to poll_seconds of (lets say) 5 seconds? Currently it can be set to any u32 using --dev-bitcoind-poll.

Then give fee_history array 18 elements so it can store 90 seconds of history. And limit the search for the outliers to the first floor(90 / poll_seconds) elements of that array.

@rustyrussell
Copy link
Contributor Author

It's tempting to get more sophisticated, but remember that dev options are only for testing: here having only 3 is a feature.

The long term averaging is The Right Thing, and it's already in place. But older nodes don't do that, plus there may be other reasons why they're using what we consider outliers, eg. just starting up.

For min-max range checks, we really want to know "is this reasonable": I think if it's a value that estimatefee returns, the answer has to be "yes".

It introduces imprecision (took 1 satoshi off results in the coming
tests), and we have a helper for this already.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
It may fail, but it's better than having a window where we're using
the default feerate.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
In particular, this lets us intercept individual commands, such as
estimatesmartfee.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We're going to use it to override specific commands.  It's non-valgrinded
already since we use '--trace-children-skip=*bitcoin-cli*' so the overhead
should be minimal.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
It (almost?) always fails for regtest; best to override it directly.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We don't update a channel's feerate on reestablishment: we insert a restart
in test_onchain_different_fees() (which we'll need soon anyway) to show it.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This is a noop if we're opening a new channel (channel_fees_can_change(channel)
is false until funding locked in), but important if we're restarting.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We don't respond to fee changes until we're locked in: make sure we catch
up at that point.

Note that we use NORMAL fees during opening, but IMMEDIATE after, so
this often sends a fee update.  The tests which break, we set those
feerates to be equal.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
…ntry.

Not just during startup: we could have bitcoind not give estimates until
later, but we don't want to smooth with zero.

The test changes in next patch trigger this, so I didn't write a test
with this patch.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Manipulate fees via fake-bitcoin-cli.  It's not quite the same, as
these are pre-smoothing, so we need a restart to override that where
we really need an exact change.  Or we can wait until it reaches a
certain value in cases we don't care about exact amounts.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Useful it we want to intercept bitcoin-cli first.

We move the getinfo() caching into start(), as that's when we can actually
use RPC.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We use feerate in several places, and each one really should react
differently when it's not available (such as when bitcoind is still
catching up):

1. For general fee-enforcement, we use the broadest possible limits.
2. For closingd, we use it as our opening negotiation point: just use half
   the last tx feerate.
3. For onchaind, we can use the last tx feerate as a guide for our own txs;
   it might be too high, but at least we know it was sufficient to be mined.
4. For withdraw and fund_channel, we can simply refuse.

Fixes: ElementsProject#1836
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
And no more filtering out messages, as we should no longer spam the
logs with them (the 'Connected json input' one was removed some time
ago).

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This is only used for logging now, but it gets more important as it
enters the RPC API.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This is useful mainly in the case where bitcoind is not giving estimates,
but can also be used to bias results if you want.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We don't know what our peer is doing, but if we see those values, maybe
they did too, and for longer.  And add the min/max acceptable values
into our JSON API.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
We could refine this later (based on existing wallet, for example), but
this gives some estimate.

[ Rename onchain_estimates -> onchain_fee_estimates Suggested-by: @SimonVrouwe ]
Suggested-by: @molxyz
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
@rustyrussell
Copy link
Contributor Author

Rebased to fix conflicts with new param sweep...

@rustyrussell
Copy link
Contributor Author

Travis uncovered a real issue, will address tomorrow, meanwhile this is now a WIP

@SimonVrouwe
Copy link
Collaborator

SimonVrouwe commented Aug 21, 2018

Testing this branch and the feerates command showed some weird results concerning unit sipa<->bitcoind conversions.

I did some digging and found, which looks like an old bug:

efee->satoshi_per_kw[efee->i] = feerate / 4;

should be multiply by 4 because feerate [sat/kilobyte] is feerate [sat /( kw/4)] is 4*feerate [sat/kw]

EDIT: No you you cannot do this, bytes and weight are separate quantities/dimensions!

same here:

feerates[i] = (feerates[i] + 3) / 4;
bitcoind_style = true;
mulfactor = 4;

/ 4 should be * 4 and mulfactor should 1/4 but maybe better change mulfactor to u32 divfactor ...etc

opening_feerate(cmd->ld->topology) * 702);

needs / 1000 factor

@cdecker
Copy link
Member

cdecker commented Aug 21, 2018

Travis uncovered a real issue, will address tomorrow, meanwhile this is now a WIP

Can we split this PR somehow, cherry-picking the commits that are necessary for other PRs that build on it? Otherwise this becomes a head of line blocker.

(smaller PRs would be easier to review and reduce dependencies as well)

return;
if (!topo->root)
return;
io_break(topo);
Copy link
Member

Choose a reason for hiding this comment

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

Looks a bit like we're trying to reinvent the countdown latch here :-)


if (topo->feerate_uninitialized) {
/* Moving this forward in time is ok, but feerate smoothing is effectively
* disabled until topo->startup is set to false */
Copy link
Member

Choose a reason for hiding this comment

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

topo->startup was removed, comment needs adjusting.

# Create and rename, for atomicity.
f = os.path.join(self.daemon.lightning_dir, "bitcoin-cli-fail.tmp")
with open(f, "w") as text_file:
text_file.write("%d" % exitcode)
os.rename(f, os.path.join(self.daemon.lightning_dir, "bitcoin-cli-fail"))
text_file.write(failscript)
Copy link
Member

@cdecker cdecker Aug 21, 2018

Choose a reason for hiding this comment

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

We'll definitely need to change this, a python script writing a pythonbash script that is then run by a subprocess, that's an antipattern if I ever saw one 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But it's faster (at least, if it's dash, not bash!).

I just extrapolated from the existing fake, which was a shell script. Happy to take more patches which neaten it up, but this is test infrastructure which is always a second-class citizen...

@rustyrussell
Copy link
Contributor Author

rustyrussell commented Aug 22, 2018

This PR is hard to split. About half the changes are test changes required to test the code changes.

The initial motivation was #1836 but when I started digging I found more cases of problemsl hence the branch name :(

I've split it up, but the cost is that some are now based on a merge of other PRs; let's see how well github deals with that!

@rustyrussell
Copy link
Contributor Author

@SimonVrouwe I don't think that's right (we had it that way in the past though, so you're not alone!)

If the rate is 1 satoshi per byte, and we have a 250 byte tx, it costs 1 * 250 = 250 satoshi.

That tx is 1000 sipa, so the rate per kw is 0.25: 0.25 * 1000 = 250 satoshi. So to convert bitcoind-style feerates to ours, we divide by 4.

Your point about the gross feerate estimate is correct however. I've invited you to be a r/o collaborator, which means I can ping you for reviews in future :)

@SimonVrouwe
Copy link
Collaborator

SimonVrouwe commented Aug 22, 2018

@rustyrussell Thanks for clearing that out! I found myself a bit lost in the woods there 😰
What looks like a unit conversion of a certain quantity (size of serialized tx), but it is not. Weight and size are really separate quantities/dimensions which happen to have a (numerical) relation between them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants