-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Refactor fee estimation #2251
Refactor fee estimation #2251
Conversation
- feeTxSizeEstimationRecursionCounter was never reset, so that caused probably bugs by ignoring fee estimation after it was called >10 times. The default size was used then so the bug was not very obvious as long the tx size was not very large.
|
||
if (!preferences.isPayFeeInBtc()) { | ||
// If we pay the fee in BSQ we have one input more which adds about 150 bytes | ||
estimatedTxSize += 150; |
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 we avoid magic numbers? https://en.wikipedia.org/wiki/Magic_number_(programming)
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 we can use a constant. It is just used once so thats why I did not use one and the class is small so easty to overlook all as well there is a comment decribing the value. But no problem to make a constant for it.
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
@RunWith(PowerMockRunner.class) |
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 no need for PowerMock in this test class.
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.
OK, I will remove it
assertEquals(260, result); | ||
|
||
|
||
// TODO check how to use the mocking framework for repeated calls |
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 separate test scenario and should be placed in separate method.
Good naming convention for test methods is testMethodName_scenario_expectedCondition
i.e. getEstimatedTxSize_nullOutputs_throwsException
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 refer to the commented out lines, right? Would you mind to have a hands on the test class? More your domain, you know my love for tests is limited ;-)
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.
return new Tuple2<>(txFee, size); | ||
} | ||
|
||
@VisibleForTesting |
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 been always told that this is a bad idea, as it's an attempt to test implementation details.
Either extract it to an utility class that could be tested separately or do not test it individually.
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 clear what you mean. Lets discuss on a call in a bit...
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 @blabno is talking about the fact, that if you need to make something visible for testing that wouldn't be accessible otherwise, is a sign that something should be structured or tested differently 😉
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.
Ah ok. Yes basically agree. In that case I would not like to expose getEstimatedTxSize as it is only interesting for that class. The more high level methods carry much more special case handling that they would be harder to test then the core functionality in getEstimatedTxSize.
Set ignore to failing tests @bernard could you have a look and try to get those working? Reason for problem is that repeated calls to getEstimatedFeeTxSize do not work (returns 0 at second call in loop which cause test to fail)
realTxSize = 2600; | ||
|
||
txFee = txFeePerByte.multiply(initialEstimatedTxSize); | ||
when(btcWalletService.getEstimatedFeeTxSize(outputValues, txFee)).thenReturn(realTxSize); |
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.
do/while loop inside getEstimatedFeeTxSize
has 2 cycles because first invocation of isInTolerance
returns false
.
So for the first iteration we have to mock with initial txFee
:
when(btcWalletService.getEstimatedFeeTxSize(outputValues, txFee)).thenReturn(realTxSize);
And for the second iteration we have to mock with txFee
multiplied by txFeePerByte
.
when(btcWalletService.getEstimatedFeeTxSize(outputValues, txFeePerByte.multiply(realTxSize))).thenReturn(realTxSize)
Alternatively we can mock getEstimatedFeeTxSize
to return the same result for any txFee
:
when(btcWalletService.getEstimatedFeeTxSize(eq(outputValues), any(Coin.class))).thenReturn(realTxSize);
assertEquals(2600, result); | ||
} | ||
|
||
// FIXME @Bernard could you have a look? |
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.
Same as above
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.
@blabno Thanks. I would prefer to leave that for another PR, too buys with other high prio stuff. If you have time to take that would be great as well!
@ripcurlx Could you ACK if you have completed review? |
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.
ACK - Just a couple of questions and minor changes.
return new Tuple2<>(txFee, size); | ||
} | ||
|
||
@VisibleForTesting |
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 @blabno is talking about the fact, that if you need to make something visible for testing that wouldn't be accessible otherwise, is a sign that something should be structured or tested differently 😉
desktop/src/main/java/bisq/desktop/main/offer/MutableOfferDataModel.java
Outdated
Show resolved
Hide resolved
} else { | ||
feeTxSize = 320; | ||
txFeeFromFeeService = getTxFeeBySize(feeTxSize); | ||
feeTxSize = 380; |
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.
Why did this change from 320?
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 the TxFeeEstimationService we use as min. the payout tx size (380), so that reflects the same logic. It is to be more on the safe side and prefer to overpay the other txs and not underpay the payout tx.
From: TxFeeEstimationService
int averageSize = (estimatedTxSize + DEPOSIT_TX_SIZE) / 2; // deposit tx has about 320 bytes
// We use at least the size of the payout tx to not underpay at payout.
size = Math.max(PAYOUT_TX_SIZE, averageSize);
desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java
Outdated
Show resolved
Hide resolved
No idea why tests fail, the EditOfferDataModelTest does not exist. Seems a caching issue. |
Danger zone ;-)
This needs some good testing and careful review.