-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Refactor Bumpfee, move core functionality to CWallet #9681
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
Conversation
src/wallet/wallet.h
Outdated
BumpFeeResult_INVALID_PARAMETER, | ||
BumpFeeResult_WALLET_ERROR, | ||
BumpFeeResult_MISC_ERROR, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there some reason not to use exceptions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I though about using exceptions but came to the conclusion that a simple result state enum will (probably) allow more flexibility for further optimisations. But no strong opinion.
I prefer an enum here (I'll elaborate if wanted). |
Concept ACK, it is better to have this in wallet than in the RPC code, especially as the code is going to be used by the GUI. No opinion on how to handle the errors. Though returning an enum is consistent with some other wallet functions that return a status. Aside: As said earlier on IRC I'm not entirely happy with the current trend of moving everything wallet related to CWallet - we're creating a "god class". It would be nice to see if we can parcel up the functionality and separate concerns, to avoid a huge file and exponentially growing complexity. But in any case, this is a step forward... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, just left a few minor comments. Hopefully this can be merged soon to avoid conflicts.
Agree with laanwj about the "god class" problem, but it seems straightforward to solve. BumpFee could easily be a standalone function taking a CWallet* pointer, instead of a CWallet method.
src/wallet/wallet.h
Outdated
*/ | ||
enum BumpFeeResult | ||
{ | ||
BumpFeeResult_OK, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could use "enum class" here to avoid the "BumpFeeResult_" prefixes here while still requiring them where the values are referenced.
src/wallet/wallet.cpp
Outdated
// calculation, but we should be able to refactor after priority is removed). | ||
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should | ||
// be IsAllFromMe). | ||
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears that you are only copying, not moving the CalculateMaximumSignedTxSize function in this commit (8993411d7b3e6fe2c4baa917874941ed95c39188 [Move] move bumpfee core logic to CWallet
). Would make more sense to remove it from rpcwallet.cpp in this commit instead of the next one.
src/wallet/wallet.h
Outdated
* Bumps the fee of an opt-in-RBF transaction <txid>, | ||
* replacing it with a new transaction <wtxNew> | ||
*/ | ||
enum BumpFeeResult |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enum is not being used in this commit (8993411d7b3e6fe2c4baa917874941ed95c39188 [Move] move bumpfee core logic to CWallet
), would probably make more sense to define in the next commit.
src/wallet/wallet.cpp
Outdated
@@ -2313,71 +2313,85 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx) | |||
if (!pwalletMain->DummySignTx(txNew, vCoins)) { | |||
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE) | |||
// implies that we can sign for every input. | |||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction contains inputs that cannot be signed"); | |||
return -1; | |||
} | |||
return GetVirtualTransactionSize(txNew); | |||
} | |||
|
|||
enum CWallet::BumpFeeResult CWallet::BumpFee(uint256 txid, int newConfirmTarget, bool specifiedConfirmTarget, CAmount totalFee, bool newTxReplaceable, CAmount& nOldFee, CAmount& nNewFee, std::shared_ptr<CWalletTx>& wtxNew, std::vector<std::string>& vErrors) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can drop "enum" in c++.
src/wallet/wallet.cpp
Outdated
// TODO: see if JSON-RPC has a standard way of returning a response | ||
// along with an exception. It would be good to return information about | ||
// wtxBumped to the caller even if marking the original transaction | ||
// replaced does not succeed for some reason. | ||
vErrors.push_back("Error: Created new bumpfee transaction but could not mark the original transaction as replaced."); | ||
} | ||
|
||
wtxNew = std::make_shared<CWalletTx>(wtxBumped); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's a good idea to return a mutable pointer to a copy of the CWalletTx that isn't actually in mapwallet, because changes a caller makes to this pointer might have unexpected or no effects. Since the shared pointer doesn't appear to be needed here or in #9697, I think it'd be better to return just wtxBumped.tx or wtxBumped.GetHash() instead.
ba57464
to
65e2e00
Compare
Completely rewrote the refactor.
|
d836e38
to
cc585d5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK cc585d5
I looked at all the moves in meld and verified that no behavior should be changing. I left a few comments on how I think the feebumper API might be improved, but do not think any work in that area should block this PR, because this is already doing a great job of moving things where they need to be, and this doesn't seem like an easy PR to keep maintaining and updating.
One suggestion I might make, though, is to maybe squash some of the commits. For example the commit 641c99c doesn't compile and might be annoying for git-bisect. And the multiple moves might be confusing for reviewers looking at the PR commit-by-commit.
Will take a look at the followup PR 9697 as soon as I get a chance.
src/wallet/wallet.cpp
Outdated
@@ -2318,50 +2318,70 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx) | |||
return GetVirtualTransactionSize(txNew); | |||
} | |||
|
|||
enum CWallet::BumpFeeResult CWallet::BumpFee(uint256 txid, int newConfirmTarget, bool specifiedConfirmTarget, CAmount totalFee, bool newTxReplaceable, CAmount& nOldFee, CAmount& nNewFee, std::shared_ptr<CWalletTx>& wtxNew, std::vector<std::string>& vErrors) | |||
bool CWallet::SignTransaction(std::shared_ptr<CMutableTransaction> tx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't see a reason to require shared_ptr here. CMutableTransaction& would allow callers to use it or not use it. Similarly for other shared_ptr arguments.
src/primitives/transaction.h
Outdated
@@ -451,7 +451,9 @@ struct CMutableTransaction | |||
}; | |||
|
|||
typedef std::shared_ptr<const CTransaction> CTransactionRef; | |||
typedef std::shared_ptr<CMutableTransaction> CMutableTransactionRef; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there's a reason to have these typedefs or even to use shared pointers in feebumper at all. Shared pointers are a great tool in places where they are actually needed, but plain c++ references that don't need to be deferenced, don't require reference counting, and can't be null are a better option in the places where they work. Another disadvantage of shared_ptr interfaces is that the requirement to wrap everything in shared_ptr can start spreading virally from callers, to callers callers, etc.
src/wallet/feebumper.h
Outdated
class CFeeBumper | ||
{ | ||
public: | ||
enum class BumpFeeResult |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the less verbose BumpFeeResult::OK
would be preferable to CFeeBumper::BumpFeeResult::OK
Could move enum above class to accomplish this. Or could change "enum class" to "enum" for CFeeBumper::OK
.
src/wallet/feebumper.h
Outdated
CAmount getOldFee() const { return nOldFee; } | ||
CAmount getNewFee() const { return nNewFee; } | ||
CMutableTransactionRef getBumpedTxRef() const { return mtx; } | ||
uint256 getBumpedTxId() const { return bumpedTxid; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seeing all these getters makes me think this API grouping is not a good idea. We have this class with two functions that do real work (constructor and commit), and then a bunch of members, and a bunch of getters, and somehow the caller is supposed to keep track of all the getters and know which ones are valid to call at different stages in the object lifetime. Also, member lifetimes are unnecessarily prolonged (constructor's return values like nOldFee/nNewFee/currentResult/vErrors do not need to be kept alive in order for the commit method to do it's job).
I think a better API here would look something like:
void CreateBumpTx(<input const ref args>, <output ref args>);
void CommitBumpTx(<input const ref args>, <output ref args>);
without the stateful class.
Added a commit c81a8a7ddef6f3c1e4cb0816aa12ae16b91cbf02 that addresses the shared pointer for The second commit a9c25b611504225d61291365ab6025dbd426e2d0 moves the enum class I haven't implemented the proposed API change (#9681 (comment)) which maybe could be a follow up. I'n not sure if this would go into a direction to purely static in/out functions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK a9c25b611504225d61291365ab6025dbd426e2d0
Should squash the commits down though to reduce churn, one or two commits would be ideal.
src/wallet/feebumper.cpp
Outdated
// wallet, with a valid index into the vout array. | ||
for (auto& input : tx.vin) { | ||
const auto mi = pwalletMain->mapWallet.find(input.prevout.hash); | ||
assert(mi != pwalletMain->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe split into two asserts, to be a little easier to read, and so you can tell which condition failed from the error.
42a66ce
to
4ce1d6f
Compare
Needed rebase (#8775). |
utACK 4ce1d6fb995bc30551571fe3621f185ea66eae7f Comparing against previous a9c25b611504225d61291365ab6025dbd426e2d0, there were no changes except for some whitespace and a renamed local iterator variable. |
Had to change this to respect the |
It would be really nice to have this merged soon. This PR is blocking the bumpfee GUI PR #9697, which is blocking the RBF checkbox PR #9592, which is blocking the RBF RPC PR #9672, and possibly the I guess maybe this needs another reviewer? It also needs a rebase to avoid trivial conflicts with #9853 and #9643. |
src/wallet/feebumper.cpp
Outdated
|
||
// Calculate the expected size of the new transaction. | ||
int64_t txSize = GetVirtualTransactionSize(*(wtx.tx)); | ||
const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a minor bug here. This needs to pass pWallet
into CalculateMaximumSignedTxSize
so it can stop using pwalletMain
. When making this change, I also had to make the CWallet::DummySignTx
method const. Feel free to use my complete fix: ryanofsky@731ca5c
4ce1d6f
to
a618359
Compare
Rebased (carefully). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK a6183598ea15d0b9acd8e22b3f8af6ac444f9db6, but any chance you could squash the following three commits into one?
- 26df8ae0fb9403acc8c64fe60d891f74a197a231 [Move] move bumpfee core logic to CWallet
- e9c88c855e558b7372445414a57ef635d18d22eb Refactor Bumpfee core functionality
- a6183598ea15d0b9acd8e22b3f8af6ac444f9db6 fixup! Refactor BumpFee, move relevant code into CFeeBumper
The first commit there doesn't compile, and the third commit was meant to be a fixup to another commit which no longer exists. Also, having the code move twice in one PR (rpcwallet -> wallet -> feebumper) only makes the PR harder to rebase and review, AFAICT.
src/wallet/feebumper.cpp
Outdated
|
||
if (wtx.GetDepthInMainChain() != 0) { | ||
vErrors.push_back("Transaction has been mined, or is conflicted with a mined transaction"); | ||
currentResult = BumpFeeResult::MISC_ERROR; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This appears to be WALLET_ERROR not MISC_ERROR in master:
bitcoin/src/wallet/rpcwallet.cpp
Line 2875 in 8910b47
throw JSONRPCError(RPC_WALLET_ERROR, "Transaction has been mined, or is conflicted with a mined transaction"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Great catch... fixed now.
src/wallet/rpcwallet.cpp
Outdated
SignatureData sigdata; | ||
if (!ProduceSignature(TransactionSignatureCreator(pwallet, &txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) { | ||
throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction."); | ||
CFeeBumper feeBump(pwalletMain, hash, newConfirmTarget, specifiedConfirmTarget, totalFee, replaceable); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pwalletMain should be pwallet, also a few places below
a618359
to
1d8529b
Compare
Fixed @ryanofsky findings and squashed into a single commit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slightly tested ACK 1d8529b.
Ran tests, confirmed the RPC error code and pwalletMain usages were fixed and that there were no other changes since last my previous ACK.
I did notice one case where the PR seemed to be unintentionally changing behavior (no longer raising error on 0 or negative totalFee option values, see comment in rpcwallet.cpp), but other than that I confirmed this should only be moving code, not changing behavior.
@@ -2933,157 +2850,55 @@ UniValue bumpfee(const JSONRPCRequest& request) | |||
} | |||
} else if (options.exists("totalFee")) { | |||
totalFee = options["totalFee"].get_int64(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "Bumpfee move request parameter interaction to the top":
This commit seems to be changing behavior in a way that I think would be good to avoid. Previously, if you passed a negative or 0 totalFee option, you would get an "Insufficient totalFee" error. But now, an automatic fee will be calculated, ignoring the totalFee value that was passed.
I think you could fix this by writing:
totalFee = options["totalFee"].get_int64();
if (totalFee <= 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid totalFee %s (must be greater than 0)", FormatMoney(totalFee)));
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. That's correct. I'm adding a commit that restores that behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've quickly reviewed the parts which I think have changed during the move. I haven't verified that the code move hasn't changed any of the functionality in the code that has been moved.
A couple of comments from me about error handling. Otherwise, lightly tested ACK 1d8529b
src/wallet/wallet.cpp
Outdated
int nIn = 0; | ||
for (auto& input : tx.vin) { | ||
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(input.prevout.hash); | ||
assert(mi != mapWallet.end() && input.prevout.n < mi->second.tx->vout.size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer this not to be an assert. Is it possible to return an error rather than assert?
I know that this was here before the code move, but my worry is that moving it to wallet.cpp might encourage people to call this function without knowing that they must lock the wallet and verify that all the transaction inputs are in the wallet map first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. This is a valid point. Adding a commit that returns false instead of the assert()
.
src/wallet/feebumper.cpp
Outdated
{ | ||
AssertLockHeld(pWallet->cs_wallet); | ||
vErrors.clear(); | ||
if (txid.IsNull() || !pWallet->mapWallet.count(txid)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if you need to check this here, when you already checked it in CFeeBumper::CFeeBumper()
?
If this check fails, I think you need to return false from this function immediately rather than fall through, since the function goes on to deference the transaction in the map.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's sane to keep this check... but your right, it should immediately return. Will fix.
29b6d6d
to
ccbdb2c
Compare
Addressed @ryanofsky and @jnewbery points. |
utACK ccbdb2ceadade6059d7c375592f01db7feda05f6. The three new commits, as well as the main commit 1d8529b (previously ACKed) all look good. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utack ccbdb2ceadade6059d7c375592f01db7feda05f6, couple of nits/things to fix.
src/wallet/feebumper.cpp
Outdated
// calculation, but we should be able to refactor after priority is removed). | ||
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should | ||
// be IsAllFromMe). | ||
int64_t CalculateMaximumSignedTxSize(const CWallet *pWallet, const CTransaction &tx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need to change the signature of CalculateMaximumSignedTxSize
, and the prior version seems better
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. This seems to be a rebase issue. Will change.
src/wallet/feebumper.cpp
Outdated
for (auto& input : tx.vin) { | ||
const auto mi = pWallet->mapWallet.find(input.prevout.hash); | ||
assert(mi != pWallet->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size()); | ||
vCoins.emplace_back(std::make_pair(&(mi->second), input.prevout.n)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make_pair with emplace_back is not necessary, may as well fix it.
src/wallet/feebumper.cpp
Outdated
|
||
// Calculate the expected size of the new transaction. | ||
int64_t txSize = GetVirtualTransactionSize(*(wtx.tx)); | ||
const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(pWallet, *wtx.tx); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might make more sense for CalculateMaximumSignexTxSize to take a vErrors argument, as this is non-obvious to me (from local code) why maxNewTxSize < 0 means there were unsignable inputs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, though the scope of this PR is – ideally – pure refactoring to make it usable over the UI. Let's optimise such things later.
src/wallet/feebumper.cpp
Outdated
currentResult = BumpFeeResult::INVALID_PARAMETER; | ||
return; | ||
} | ||
CAmount requiredFee = pWallet->GetRequiredFee(maxNewTxSize); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reason to change this line from CWallet::GetRequiredFee
?
src/wallet/feebumper.cpp
Outdated
} else { | ||
// if user specified a confirm target then don't consider any global payTxFee | ||
if (specifiedConfirmTarget) { | ||
nNewFee = pWallet->GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, CAmount(0)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also static, please keep CWallet::
src/wallet/feebumper.cpp
Outdated
} | ||
// otherwise use the regular wallet logic to select payTxFee or default confirm target | ||
else { | ||
nNewFee = pWallet->GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also static, please keep CWallet::
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. This seems to be again a rebase issue. Though, changing it to pWallet
would allow to be more flexible when multi wallet takes off... but changing it back for now.
// Now modify the output to increase the fee. | ||
// If the output is not large enough to pay the fee, fail. | ||
CAmount nDelta = nNewFee - nOldFee; | ||
assert(nDelta > 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you should refactor this to:
if (nOldFee >= nNewFee) {
vErrors.push_back("Attempt to increase fee decreased fee.");
currentResult = BumpFeeResult::WALLET_ERROR;
return;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's do this later and focus now on refactoring out the core functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense to just change it now... #8456 (comment) is where it got introduced, I think it still may be possible to trigger & throwing seems better for this than crashing your node.
I'll leave it up to you though -- just let me know if you decide not to & I'll prepare a patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's try to keep the scope ("refactoring"), behaviours changes have already sneaked into this PR.
src/wallet/rpcwallet.cpp
Outdated
|
||
CFeeBumper feeBump(pwallet, hash, newConfirmTarget, specifiedConfirmTarget, totalFee, replaceable); | ||
BumpFeeResult res = feeBump.getResult(); | ||
if (res != BumpFeeResult::OK) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should just make this a case of the switch.
src/wallet/feebumper.cpp
Outdated
bool CFeeBumper::commit(CWallet *pWallet) | ||
{ | ||
AssertLockHeld(pWallet->cs_wallet); | ||
vErrors.clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you should fail to commit if (!vErrors.empty())
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Will change.
src/wallet/feebumper.cpp
Outdated
currentResult = BumpFeeResult::OK; | ||
} | ||
|
||
bool CFeeBumper::commit(CWallet *pWallet) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I would prefer to see commit be done via RAII (but maybe that's bad if you make a container of CFeeBumpers...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm.. not sure if RAII for a bump commit is appropriate. IMO the commit (it actually does sign and broadcast) should be a clearly visible call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if you do something like
commit() {
assert(!comitted)
comitted = true;
...
}
~CFeeBumper() {
if (vErrors.empty())
assert(comitted);
}
To ensure that if a CFeeBump could be comitted, it is & comitting isn't tried multiple times?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure but this looks after too much hand holding. But if someone wants to implement this, yeah, maybe after this PR?
Thanks @JeremyRubin for the review. Fixed the relevant points. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK 3 new commits in 99070569662dde84a4f4b1d38cd53762063f53d2 since previously ACKed ccbdb2ceadade6059d7c375592f01db7feda05f6. This seems like it is ready to be merged.
src/wallet/feebumper.cpp
Outdated
@@ -20,7 +20,7 @@ | |||
// calculation, but we should be able to refactor after priority is removed). | |||
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should | |||
// be IsAllFromMe). | |||
int64_t CalculateMaximumSignedTxSize(const CWallet *pWallet, const CTransaction &tx) | |||
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *pWallet) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "Restore CalculateMaximumSignedTxSize function signature":
Previous signature took a CWallet reference instead of a pointer. (Just an observation, feel free to leave it.)
src/wallet/feebumper.cpp
Outdated
@@ -30,7 +30,7 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *pWal | |||
for (auto& input : tx.vin) { | |||
const auto mi = pWallet->mapWallet.find(input.prevout.hash); | |||
assert(mi != pWallet->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size()); | |||
vCoins.emplace_back(std::make_pair(&(mi->second), input.prevout.n)); | |||
vCoins.emplace_back(&(mi->second), input.prevout.n); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "Use static calls for GetRequiredFee and GetMinimumFee, remove make_pair from emplace_back"
Could drop parens around mi->second. (Just an observation, feel free to leave.)
src/wallet/feebumper.cpp
Outdated
@@ -232,6 +232,9 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConf | |||
bool CFeeBumper::commit(CWallet *pWallet) | |||
{ | |||
AssertLockHeld(pWallet->cs_wallet); | |||
if (!vErrors.empty()) { | |||
return false; | |||
} | |||
vErrors.clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vErrors.clear()
is now redundant.
// Now modify the output to increase the fee. | ||
// If the output is not large enough to pay the fee, fail. | ||
CAmount nDelta = nNewFee - nOldFee; | ||
assert(nDelta > 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense to just change it now... #8456 (comment) is where it got introduced, I think it still may be possible to trigger & throwing seems better for this than crashing your node.
I'll leave it up to you though -- just let me know if you decide not to & I'll prepare a patch.
src/wallet/feebumper.cpp
Outdated
currentResult = BumpFeeResult::OK; | ||
} | ||
|
||
bool CFeeBumper::commit(CWallet *pWallet) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if you do something like
commit() {
assert(!comitted)
comitted = true;
...
}
~CFeeBumper() {
if (vErrors.empty())
assert(comitted);
}
To ensure that if a CFeeBump could be comitted, it is & comitting isn't tried multiple times?
const uint256 txid; | ||
uint256 bumpedTxid; | ||
CMutableTransaction mtx; | ||
std::vector<std::string> vErrors; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You do not use vErrors to store more than one error, so may as well just use a std::string (this clarifies it is set-once).
Alternatively, you could use a smart pointer to a string and just check that it is not nullptr.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
L265 and L274 can both add an error that then get's spit out over the JSON-RPC "errors" array. I haven't changed this in this PR.
src/wallet/feebumper.cpp
Outdated
bool CFeeBumper::commit(CWallet *pWallet) | ||
{ | ||
AssertLockHeld(pWallet->cs_wallet); | ||
if (!vErrors.empty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe change to/add && BumpFeeResult::OK == currentResult
9907056
to
fb7bb67
Compare
Amend-changed the last commit to remove the redundant |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally looks good, only issue I have is the API where the RPC calls SignTransaction directly feels super strange.
} | ||
CAmount requiredFee = CWallet::GetRequiredFee(maxNewTxSize); | ||
if (totalFee < requiredFee) { | ||
vErrors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having this and the totalFee error be separate is somewhat strange, maybe combine them now that the checks are in the same place (in an additional commit) into one generic error so we dont have users chasing error messages for a few rounds?
src/wallet/feebumper.cpp
Outdated
|
||
CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConfirmTarget, bool specifiedConfirmTarget, CAmount totalFee, bool newTxReplaceable) | ||
: | ||
txid(txidIn), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: pass txidIn by reference or std::move it here.
src/wallet/feebumper.h
Outdated
const std::vector<std::string>& getErrors() const { return vErrors; } | ||
CAmount getOldFee() const { return nOldFee; } | ||
CAmount getNewFee() const { return nNewFee; } | ||
CMutableTransaction* getBumpedTxRef() { return &mtx; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like a super strange API...you're getting a reference to memory owned by CFeeBumper, passing it to the wallet, then calling a method owned by CFeeBumper which assumes you mutated its (seemingly internal) state. Given you're already passing a pwallet reference into CFeeBumper, maybe replace this with bool SignTransaction(CWallet *pwalletIn)?
src/wallet/feebumper.h
Outdated
CMutableTransaction* getBumpedTxRef() { return &mtx; } | ||
uint256 getBumpedTxId() const { return bumpedTxid; } | ||
|
||
bool commit(CWallet *pWalletNonConst); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you document the return value here? It initially confused me because I thought it would return false if it set vErrors, when, in fact, it might add vErrors and then return true.
Added a commit that addresses @TheBlueMatt's points.
|
…ir from emplace_back
8679200
to
5f59d3e
Compare
Rebased (trivial) after #9424 |
utACK 5f59d3e, confirmed first 8 commits (previously acked as 99070569662dde84a4f4b1d38cd53762063f53d2) did not change in the rebase, other than the log category fix. New 9th commit also looks fine. I think it would be good to merge this because it is a burden to maintain separately and keep reviewing. It's been rebased many times and is blocking other prs. |
5f59d3e Improve CFeeBumper interface, add comments, make use of std::move (Jonas Schnelli) 0df22ed Cancel feebump is vErrors is not empty (Jonas Schnelli) 44cabe6 Use static calls for GetRequiredFee and GetMinimumFee, remove make_pair from emplace_back (Jonas Schnelli) bb78c15 Restore CalculateMaximumSignedTxSize function signature (Jonas Schnelli) 51ea44f Use "return false" instead assert() in CWallet::SignTransaction (Jonas Schnelli) bcc72cc Directly abort execution in FeeBumper::commit if wallet or tx is not available (Jonas Schnelli) 2718db0 Restore invalid fee check (must be > 0) (Jonas Schnelli) 0337a39 Refactor Bumpfee core functionality (Jonas Schnelli) d1a95e8 Bumpfee move request parameter interaction to the top (Jonas Schnelli) Tree-SHA512: 0e6d1f3322ed671fa2291e59ac9556ce4646bc78267edc6eedc46b0014b7b08aa83c30315358b911d82898847d4845634a18b67e253a7b699dcc852eb2652c07
This is a required step for using the current bumpfee logic in the GUI.
The first two commits are "moveonly".