Skip to content

Conversation

@instagibbs
Copy link
Member

@instagibbs instagibbs commented Apr 11, 2019

The typical usage pattern of CReserveKey is to explicitly KeepKey, or ReturnKey when it's detected it will not be used.

Implementers such as myself may fail to complete this pattern, and could result in key re-use: #15557 (comment)

Have it assert on debug builds when it's not dealt with, and log an important sounding message otherwise.

@DrahtBot
Copy link
Contributor

DrahtBot commented Apr 11, 2019

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

No conflicts as of last run.

@instagibbs
Copy link
Member Author

secp unit test failure, reported here in one build: bitcoin-core/secp256k1#610

restarted.

@jnewbery
Copy link
Contributor

I think the whole CReserveKey cycle needs to be rethought. It's too easy to accidentally not mark a key as used (eg #15557 (comment)). I'm not sure whether this change is an improvement. It removes the possibility of a key being re-used, but it does mean that if CreateTranasction fails after reserving a key (eg here

return false;
or here
return false;
or even here:
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
) then the keypool key will be burned instead of returned.

I don't know whether that could cause problems for users or client applications. For example a client that tries to create a transaction, hits an error, retries in a loop and burns many keypool keys.

@instagibbs
Copy link
Member Author

Granted this is an extremely timid change, but you can make more keys. You cannot make more privacy when this fails.

I'll let others chime in before I try to nibble at the edges and make sure these return false situations are handled explicitly.

@jnewbery
Copy link
Contributor

Granted this is an extremely timid change, but you can make more keys. You cannot make more privacy when this fails.

Yep. To be clear, I'm not NACKing this. Just trying to figure out what the least bad option is.

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

utACK eac5438108339a65f3dd6d9cce06404167736c2e. Seems sensible to choose the wrong thing you can recover from if you're choosing between doing two potentially wrong things.

I guess the least "timid change" would be unconditionally asserting or throwing when KeepKey or ReturnKey aren't called. But other less timid options would be logging errors when they aren't called, or asserting that they are called in --debug builds.

But current change does seem like this simplest option to implement, and I think it's an improvement.

@luke-jr
Copy link
Member

luke-jr commented Apr 18, 2019

assert seems better IMO

@maflcko
Copy link
Member

maflcko commented May 14, 2019

Needs documentation updated (after rebase)

@instagibbs
Copy link
Member Author

Deciding if it's worth the rebase. There are as many opinions here.

I'm also fine with an assert to be honest.

@promag
Copy link
Contributor

promag commented May 14, 2019

assert seems better IMO

You mean assert(nIndex == -1)?

@luke-jr
Copy link
Member

luke-jr commented May 18, 2019

Sure, something like that.

@instagibbs
Copy link
Member Author

@jnewbery @ryanofsky are you guys ok with an assert instead? I'd rather do something than let this languish, and it seems reasonable.

@ryanofsky
Copy link
Contributor

@jnewbery @ryanofsky are you guys ok with an assert instead? I'd rather do something than let this languish, and it seems reasonable.

I'd prefer the assert, but I think the current change is also an improvement. I'd be happy with either change.

@jnewbery
Copy link
Contributor

other less timid options would be logging errors when they aren't called, or asserting that they are called in --debug builds.

I would prefer to do this.

@instagibbs
Copy link
Member Author

TIL about debug builds. Added an assert behind that, and a print otherwise telling the user to report it.

@instagibbs
Copy link
Member Author

pushed fancier reporting error log courtesy of @MarcoFalke

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

utACK 735518bbeb17f61b3a79eae503b0efb17089acd7. PR title needs to be updated (in the future I think it's better to close and open a new PR if you implement an alternate approach so discussion makes more sense and it's easy to look back and see what the rejected alternatives looked like). Also in the future, the CHECK macro proposed in #16136 might simplify this more.

@instagibbs instagibbs changed the title CReserveKey should not allow passive key re-use, KeepKey in destructor CReserveKey should not allow passive key re-use, debug assert in destructor Jun 13, 2019
@instagibbs
Copy link
Member Author

Having second-thoughts, mea culpa. Basically anytime there's a wallet error of any sort, you need to make sure you handle it and ReturnKey. This happens in dozens of places, including in CommitTransaction itself in a number of places.

Opened an alternative here: #16208

@instagibbs
Copy link
Member Author

closing this one for now

@instagibbs instagibbs closed this Jun 13, 2019
@ryanofsky
Copy link
Contributor

ryanofsky commented Jul 11, 2019

This pr is closed, and the alternate implementation in #16208 seems good, but I just wanted to note that it would be possible to check with compile errors, not just runtime asserts that the CReserveKey/ReserveDestination destructor is only called after after the key is returned or kept. Clang apparently has set_typestate(consumed) and callable_when(consumed) annotations which could enforce this. From https://awesomekling.github.io/Catching-use-after-move-bugs-with-Clang-consumed-annotations/ and https://news.ycombinator.com/item?id=20402733

@instagibbs
Copy link
Member Author

@ryanofsky interesting!

meshcollider added a commit that referenced this pull request Jul 17, 2019
…Transaction

e10e1e8 Restrict lifetime of ReserveDestination to CWallet::CreateTransaction (Gregory Sanders)
d9ff862 CreateTransaction calls KeepDestination on ReserveDestination before success (Gregory Sanders)

Pull request description:

  The typical usage pattern of `ReserveDestination` is to explicitly `KeepDestination`, or `ReturnDestination` when it's detected it will not be used.

  Implementers such as myself may fail to complete this pattern, and could result in key re-use: #15557 (comment)

  Since ReserveDestination is currently only used directly in the `CreateTransaction`/`CommitTransaction` flow(or fee bumping where it's just used in `CreateTransaction`), I instead make the assumption that if a transaction is returned by `CreateTransaction` it's highly likely that it will be accepted by the caller, and the `ReserveDestination` kept. This simplifies the API as well. There are very few cases where this would not be the case which may result in keys being burned.

  Those failure cases appear to be:
  `CommitTransaction` failing to get the transaction into the mempool
  Belt and suspenders check in `WalletModel::prepareTransaction`

  Alternative to #15796

ACKs for top commit:
  achow101:
    ACK e10e1e8 Reviewed the diff
  stevenroose:
    utACK e10e1e8
  meshcollider:
    utACK e10e1e8

Tree-SHA512: 78d047a00f39ab41cfa297052cc1e9c224d5f47d3d2299face650d71827635de077ac33fb4ab9f7dc6fc5a27f4a68415a1bc9ca33a3cb09a78f4f15b2a48411b
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jul 29, 2019
… CreateTransaction

e10e1e8 Restrict lifetime of ReserveDestination to CWallet::CreateTransaction (Gregory Sanders)
d9ff862 CreateTransaction calls KeepDestination on ReserveDestination before success (Gregory Sanders)

Pull request description:

  The typical usage pattern of `ReserveDestination` is to explicitly `KeepDestination`, or `ReturnDestination` when it's detected it will not be used.

  Implementers such as myself may fail to complete this pattern, and could result in key re-use: bitcoin#15557 (comment)

  Since ReserveDestination is currently only used directly in the `CreateTransaction`/`CommitTransaction` flow(or fee bumping where it's just used in `CreateTransaction`), I instead make the assumption that if a transaction is returned by `CreateTransaction` it's highly likely that it will be accepted by the caller, and the `ReserveDestination` kept. This simplifies the API as well. There are very few cases where this would not be the case which may result in keys being burned.

  Those failure cases appear to be:
  `CommitTransaction` failing to get the transaction into the mempool
  Belt and suspenders check in `WalletModel::prepareTransaction`

  Alternative to bitcoin#15796

ACKs for top commit:
  achow101:
    ACK e10e1e8 Reviewed the diff
  stevenroose:
    utACK e10e1e8
  meshcollider:
    utACK e10e1e8

Tree-SHA512: 78d047a00f39ab41cfa297052cc1e9c224d5f47d3d2299face650d71827635de077ac33fb4ab9f7dc6fc5a27f4a68415a1bc9ca33a3cb09a78f4f15b2a48411b
monstrobishi pushed a commit to DeFiCh/ain that referenced this pull request Sep 6, 2020
…Transaction

e10e1e8 Restrict lifetime of ReserveDestination to CWallet::CreateTransaction (Gregory Sanders)
d9ff862 CreateTransaction calls KeepDestination on ReserveDestination before success (Gregory Sanders)

Pull request description:

  The typical usage pattern of `ReserveDestination` is to explicitly `KeepDestination`, or `ReturnDestination` when it's detected it will not be used.

  Implementers such as myself may fail to complete this pattern, and could result in key re-use: bitcoin/bitcoin#15557 (comment)

  Since ReserveDestination is currently only used directly in the `CreateTransaction`/`CommitTransaction` flow(or fee bumping where it's just used in `CreateTransaction`), I instead make the assumption that if a transaction is returned by `CreateTransaction` it's highly likely that it will be accepted by the caller, and the `ReserveDestination` kept. This simplifies the API as well. There are very few cases where this would not be the case which may result in keys being burned.

  Those failure cases appear to be:
  `CommitTransaction` failing to get the transaction into the mempool
  Belt and suspenders check in `WalletModel::prepareTransaction`

  Alternative to bitcoin/bitcoin#15796

ACKs for top commit:
  achow101:
    ACK e10e1e8 Reviewed the diff
  stevenroose:
    utACK e10e1e8
  meshcollider:
    utACK e10e1e8

Tree-SHA512: 78d047a00f39ab41cfa297052cc1e9c224d5f47d3d2299face650d71827635de077ac33fb4ab9f7dc6fc5a27f4a68415a1bc9ca33a3cb09a78f4f15b2a48411b
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Dec 16, 2021
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.

7 participants