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

Refactor fee estimation #2251

Merged

Conversation

ManfredKarrer
Copy link
Contributor

Danger zone ;-)
This needs some good testing and careful review.

- 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;
Copy link

Choose a reason for hiding this comment

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

Copy link
Contributor Author

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)
Copy link

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.

Copy link
Contributor Author

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
Copy link

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

Copy link
Contributor Author

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 ;-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@blabno I broke them up in 877d0ae and looked again into it. See commit comments... Could u have a look to try to fix it?

return new Tuple2<>(txFee, size);
}

@VisibleForTesting
Copy link

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.

Copy link
Contributor Author

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...

Copy link
Contributor

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 😉

Copy link
Contributor Author

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);
Copy link

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?
Copy link

Choose a reason for hiding this comment

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

Same as above

Copy link
Contributor Author

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!

@ManfredKarrer
Copy link
Contributor Author

@ripcurlx Could you ACK if you have completed review?

Copy link
Contributor

@ripcurlx ripcurlx left a 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
Copy link
Contributor

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 😉

} else {
feeTxSize = 320;
txFeeFromFeeService = getTxFeeBySize(feeTxSize);
feeTxSize = 380;
Copy link
Contributor

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?

Copy link
Contributor Author

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);

@ManfredKarrer
Copy link
Contributor Author

No idea why tests fail, the EditOfferDataModelTest does not exist. Seems a caching issue.

@ManfredKarrer ManfredKarrer merged commit 200c90b into bisq-network:master Jan 23, 2019
@ManfredKarrer ManfredKarrer deleted the refactor-fee-estimation branch January 28, 2019 17:50
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