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 BSQ fee validation to handle multiple txins. #6200

Merged
merged 1 commit into from May 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 deletions core/src/main/java/bisq/core/provider/mempool/TxValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -260,25 +260,16 @@ private boolean checkFeeAmountBSQ(String jsonTxt, Coin tradeAmount, boolean isMa
JsonObject jsonVin0 = jsonVin.get(0).getAsJsonObject();
JsonObject jsonVout0 = jsonVout.get(0).getAsJsonObject();
JsonElement jsonVIn0Value = jsonVin0.getAsJsonObject("prevout").get("value");
JsonElement jsonFeeValue = jsonVout0.get("value");
if (jsonVIn0Value == null || jsonFeeValue == null) {
JsonElement jsonVOut0Value = jsonVout0.getAsJsonObject().get("value");
if (jsonVIn0Value == null || jsonVOut0Value == null) {
throw new JsonSyntaxException("vin/vout missing data");
}
Param minFeeParam = isMaker ? Param.MIN_MAKER_FEE_BSQ : Param.MIN_TAKER_FEE_BSQ;
Coin expectedFee = calculateFee(tradeAmount,
long expectedFeeAsLong = calculateFee(tradeAmount,
isMaker ? getMakerFeeRateBsq(blockHeight) : getTakerFeeRateBsq(blockHeight),
minFeeParam);
long feeValue = jsonVIn0Value.getAsLong() - jsonFeeValue.getAsLong();
// if the first output (BSQ) is greater than the first input (BSQ) include the second input (presumably BSQ)
if ((jsonFeeValue.getAsLong() > jsonVIn0Value.getAsLong() ||
expectedFee.getValue() > jsonVIn0Value.getAsLong()) && jsonVin.size() > 2) {
// in this case 2 or more UTXOs were spent to pay the fee:
JsonObject jsonVin1 = jsonVin.get(1).getAsJsonObject();
JsonElement jsonVIn1Value = jsonVin1.getAsJsonObject("prevout").get("value");
feeValue += jsonVIn1Value.getAsLong();
}
minFeeParam).getValue();
long feeValue = getBsqBurnt(jsonVin, jsonVOut0Value.getAsLong(), expectedFeeAsLong);
log.debug("BURNT BSQ maker fee: {} BSQ ({} sats)", (double) feeValue / 100.0, feeValue);
long expectedFeeAsLong = expectedFee.getValue();
String description = String.format("Expected fee: %.2f BSQ, actual fee paid: %.2f BSQ",
(double) expectedFeeAsLong / 100.0, (double) feeValue / 100.0);

Expand Down Expand Up @@ -359,6 +350,39 @@ private static boolean initialSanityChecks(String txId, String jsonTxt) {
// we don't care if it is confirmed or not, just that it exists.
}

// a BSQ maker/taker fee transaction looks like this:
// BSQ INPUT 1 BSQ OUTPUT
// BSQ INPUT 2 BTC OUTPUT FOR RESERVED AMOUNT
// BSQ INPUT n BTC OUTPUT FOR CHANGE
// BTC INPUT 1
// BTC INPUT 2
// BTC INPUT n
// there can be any number of BSQ inputs and BTC inputs
// BSQ inputs always come first in the tx, followed by BTC for the collateral.
// the sum of all BSQ inputs minus the BSQ output is the burnt amount, or trading fee.
long getBsqBurnt(JsonArray jsonVin, long bsqOutValue, long expectedFee) {
// sum consecutive inputs until we have accumulated enough to cover the output + burnt amount
long bsqInValue = 0;
for (int txIndex = 0; txIndex < jsonVin.size() - 1; txIndex++) {
bsqInValue += jsonVin.get(txIndex).getAsJsonObject().getAsJsonObject("prevout").get("value").getAsLong();
if (bsqInValue - expectedFee >= bsqOutValue) {
break; // target reached - bsq input exceeds the output and expected burn amount
}
}
// guard against negative burn amount (i.e. only 1 tx input, or first in < first out)
long burntAmount = Math.max(0, bsqInValue - bsqOutValue);
// since we do not know which of the first 'n' are definitively BSQ inputs, sanity-check that the burnt amount
// is not too ridiculously high, as that would imply that we counted a BTC input.
if (burntAmount > 10 * expectedFee) {
log.error("The apparent BSQ fee burnt seems ridiculously high ({}) compared to expected ({})", burntAmount, expectedFee);
burntAmount = 0; // returning zero will flag the trade for manual review
}
if (burntAmount == 0) {
log.error("Could not obtain the burnt BSQ amount, trade will be flagged for manual review.");
}
return burntAmount;
}

private static long getTxConfirms(String jsonTxt, long chainHeight) {
long blockHeight = getTxBlockHeight(jsonTxt);
if (blockHeight > 0) {
Expand Down
Loading