increase penalty fee to security deposit

This commit is contained in:
woodser 2023-03-08 15:06:55 -05:00
parent 8ea556fa4f
commit a16b03bb5c
11 changed files with 82 additions and 55 deletions

View File

@ -987,7 +987,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
// verify maker's reserve tx (double spend, trade fee, trade amount, mining fee) // verify maker's reserve tx (double spend, trade fee, trade amount, mining fee)
BigInteger sendAmount = offer.getDirection() == OfferDirection.BUY ? BigInteger.valueOf(0) : offer.getAmount(); BigInteger sendAmount = offer.getDirection() == OfferDirection.BUY ? BigInteger.valueOf(0) : offer.getAmount();
BigInteger securityDeposit = offer.getDirection() == OfferDirection.BUY ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit(); BigInteger securityDeposit = offer.getDirection() == OfferDirection.BUY ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit();
MoneroTx reserveTx = xmrWalletService.verifyTradeTx( Tuple2<MoneroTx, BigInteger> txResult = xmrWalletService.verifyTradeTx(
tradeFee, tradeFee,
sendAmount, sendAmount,
securityDeposit, securityDeposit,
@ -995,7 +995,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
request.getReserveTxHash(), request.getReserveTxHash(),
request.getReserveTxHex(), request.getReserveTxHex(),
request.getReserveTxKey(), request.getReserveTxKey(),
request.getReserveTxKeyImages()); request.getReserveTxKeyImages(),
true);
// arbitrator signs offer to certify they have valid reserve tx // arbitrator signs offer to certify they have valid reserve tx
String offerPayloadAsJson = JsonUtil.objectToJson(request.getOfferPayload()); String offerPayloadAsJson = JsonUtil.objectToJson(request.getOfferPayload());
@ -1008,11 +1009,11 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
System.currentTimeMillis(), System.currentTimeMillis(),
signedOfferPayload.getId(), signedOfferPayload.getId(),
offer.getAmount().longValueExact(), offer.getAmount().longValueExact(),
HavenoUtils.getMakerFee(offer.getAmount()).longValueExact(), txResult.second.longValueExact(),
request.getReserveTxHash(), request.getReserveTxHash(),
request.getReserveTxHex(), request.getReserveTxHex(),
request.getReserveTxKeyImages(), request.getReserveTxKeyImages(),
reserveTx.getFee().longValueExact(), txResult.first.getFee().longValueExact(),
signature); // TODO (woodser): no need for signature to be part of SignedOffer? signature); // TODO (woodser): no need for signature to be part of SignedOffer?
addSignedOffer(signedOffer); addSignedOffer(signedOffer);
requestPersistence(); requestPersistence();

View File

@ -34,7 +34,7 @@ public final class SignedOffer implements PersistablePayload {
@Getter @Getter
private final long tradeAmount; private final long tradeAmount;
@Getter @Getter
private final long makerTradeFee; private final long penaltyAmount;
@Getter @Getter
private final String reserveTxHash; private final String reserveTxHash;
@Getter @Getter
@ -49,7 +49,7 @@ public final class SignedOffer implements PersistablePayload {
public SignedOffer(long timeStamp, public SignedOffer(long timeStamp,
String offerId, String offerId,
long tradeAmount, long tradeAmount,
long makerTradeFee, long penaltyAmount,
String reserveTxHash, String reserveTxHash,
String reserveTxHex, String reserveTxHex,
List<String> reserveTxKeyImages, List<String> reserveTxKeyImages,
@ -58,7 +58,7 @@ public final class SignedOffer implements PersistablePayload {
this.timeStamp = timeStamp; this.timeStamp = timeStamp;
this.offerId = offerId; this.offerId = offerId;
this.tradeAmount = tradeAmount; this.tradeAmount = tradeAmount;
this.makerTradeFee = makerTradeFee; this.penaltyAmount = penaltyAmount;
this.reserveTxHash = reserveTxHash; this.reserveTxHash = reserveTxHash;
this.reserveTxHex = reserveTxHex; this.reserveTxHex = reserveTxHex;
this.reserveTxKeyImages = reserveTxKeyImages; this.reserveTxKeyImages = reserveTxKeyImages;
@ -76,7 +76,7 @@ public final class SignedOffer implements PersistablePayload {
.setTimeStamp(timeStamp) .setTimeStamp(timeStamp)
.setOfferId(offerId) .setOfferId(offerId)
.setTradeAmount(tradeAmount) .setTradeAmount(tradeAmount)
.setMakerTradeFee(makerTradeFee) .setPenaltyAmount(penaltyAmount)
.setReserveTxHash(reserveTxHash) .setReserveTxHash(reserveTxHash)
.setReserveTxHex(reserveTxHex) .setReserveTxHex(reserveTxHex)
.addAllReserveTxKeyImages(reserveTxKeyImages) .addAllReserveTxKeyImages(reserveTxKeyImages)
@ -89,7 +89,7 @@ public final class SignedOffer implements PersistablePayload {
return new SignedOffer(proto.getTimeStamp(), return new SignedOffer(proto.getTimeStamp(),
proto.getOfferId(), proto.getOfferId(),
proto.getTradeAmount(), proto.getTradeAmount(),
proto.getMakerTradeFee(), proto.getPenaltyAmount(),
proto.getReserveTxHash(), proto.getReserveTxHash(),
proto.getReserveTxHex(), proto.getReserveTxHex(),
proto.getReserveTxKeyImagesList(), proto.getReserveTxKeyImagesList(),
@ -108,7 +108,7 @@ public final class SignedOffer implements PersistablePayload {
",\n timeStamp=" + timeStamp + ",\n timeStamp=" + timeStamp +
",\n offerId=" + offerId + ",\n offerId=" + offerId +
",\n tradeAmount=" + tradeAmount + ",\n tradeAmount=" + tradeAmount +
",\n makerTradeFee=" + makerTradeFee + ",\n penaltyAmount=" + penaltyAmount +
",\n reserveTxHash=" + reserveTxHash + ",\n reserveTxHash=" + reserveTxHash +
",\n reserveTxHex=" + reserveTxHex + ",\n reserveTxHex=" + reserveTxHex +
",\n reserveTxKeyImages=" + reserveTxKeyImages + ",\n reserveTxKeyImages=" + reserveTxKeyImages +

View File

@ -1570,12 +1570,12 @@ public abstract class Trade implements Tradable, Model {
public BigInteger getBuyerSecurityDeposit() { public BigInteger getBuyerSecurityDeposit() {
if (getBuyer().getDepositTxHash() == null) return null; if (getBuyer().getDepositTxHash() == null) return null;
return BigInteger.valueOf(getBuyer().getSecurityDeposit()); return getBuyer().getSecurityDeposit();
} }
public BigInteger getSellerSecurityDeposit() { public BigInteger getSellerSecurityDeposit() {
if (getSeller().getDepositTxHash() == null) return null; if (getSeller().getDepositTxHash() == null) return null;
return BigInteger.valueOf(getSeller().getSecurityDeposit()); return getSeller().getSecurityDeposit();
} }
@Nullable @Nullable
@ -1719,11 +1719,11 @@ public abstract class Trade implements Tradable, Model {
getTaker().setDepositTx(makerFirst ? txs.get(1) : txs.get(0)); getTaker().setDepositTx(makerFirst ? txs.get(1) : txs.get(0));
// set security deposits // set security deposits
if (getBuyer().getSecurityDeposit() == 0) { if (getBuyer().getSecurityDeposit().longValueExact() == 0) {
BigInteger buyerSecurityDeposit = ((MoneroTxWallet) getBuyer().getDepositTx()).getIncomingAmount(); BigInteger buyerSecurityDeposit = ((MoneroTxWallet) getBuyer().getDepositTx()).getIncomingAmount();
BigInteger sellerSecurityDeposit = ((MoneroTxWallet) getSeller().getDepositTx()).getIncomingAmount().subtract(getAmount()); BigInteger sellerSecurityDeposit = ((MoneroTxWallet) getSeller().getDepositTx()).getIncomingAmount().subtract(getAmount());
getBuyer().setSecurityDeposit(buyerSecurityDeposit.longValueExact()); getBuyer().setSecurityDeposit(buyerSecurityDeposit);
getSeller().setSecurityDeposit(sellerSecurityDeposit.longValueExact()); getSeller().setSecurityDeposit(sellerSecurityDeposit);
} }
// set deposits published state // set deposits published state

View File

@ -27,6 +27,8 @@ import haveno.core.payment.payload.PaymentAccountPayload;
import haveno.core.proto.CoreProtoResolver; import haveno.core.proto.CoreProtoResolver;
import haveno.core.xmr.model.RawTransactionInput; import haveno.core.xmr.model.RawTransactionInput;
import haveno.network.p2p.NodeAddress; import haveno.network.p2p.NodeAddress;
import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -133,6 +135,14 @@ public final class TradePeer implements PersistablePayload {
public TradePeer() { public TradePeer() {
} }
public BigInteger getSecurityDeposit() {
return BigInteger.valueOf(securityDeposit);
}
public void setSecurityDeposit(BigInteger securityDeposit) {
this.securityDeposit = securityDeposit.longValueExact();
}
@Override @Override
public Message toProtoMessage() { public Message toProtoMessage() {
final protobuf.TradePeer.Builder builder = protobuf.TradePeer.newBuilder() final protobuf.TradePeer.Builder builder = protobuf.TradePeer.newBuilder()
@ -219,7 +229,7 @@ public final class TradePeer implements PersistablePayload {
tradePeer.setDepositTxHash(ProtoUtil.stringOrNullFromProto(proto.getDepositTxHash())); tradePeer.setDepositTxHash(ProtoUtil.stringOrNullFromProto(proto.getDepositTxHash()));
tradePeer.setDepositTxHex(ProtoUtil.stringOrNullFromProto(proto.getDepositTxHex())); tradePeer.setDepositTxHex(ProtoUtil.stringOrNullFromProto(proto.getDepositTxHex()));
tradePeer.setDepositTxKey(ProtoUtil.stringOrNullFromProto(proto.getDepositTxKey())); tradePeer.setDepositTxKey(ProtoUtil.stringOrNullFromProto(proto.getDepositTxKey()));
tradePeer.setSecurityDeposit(proto.getSecurityDeposit()); tradePeer.setSecurityDeposit(BigInteger.valueOf(proto.getSecurityDeposit()));
tradePeer.setUpdatedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getUpdatedMultisigHex())); tradePeer.setUpdatedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getUpdatedMultisigHex()));
return tradePeer; return tradePeer;
} }

View File

@ -90,7 +90,8 @@ public class ArbitratorProcessDepositRequest extends TradeTask {
trader.getDepositTxHash(), trader.getDepositTxHash(),
request.getDepositTxHex(), request.getDepositTxHex(),
request.getDepositTxKey(), request.getDepositTxKey(),
null); null,
false);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Error processing deposit tx from " + (isFromTaker ? "taker " : "maker ") + trader.getNodeAddress() + ", offerId=" + offer.getId() + ": " + e.getMessage()); throw new RuntimeException("Error processing deposit tx from " + (isFromTaker ? "taker " : "maker ") + trader.getNodeAddress() + ", offerId=" + offer.getId() + ": " + e.getMessage());
} }

View File

@ -18,6 +18,7 @@
package haveno.core.trade.protocol.tasks; package haveno.core.trade.protocol.tasks;
import haveno.common.taskrunner.TaskRunner; import haveno.common.taskrunner.TaskRunner;
import haveno.common.util.Tuple2;
import haveno.core.offer.Offer; import haveno.core.offer.Offer;
import haveno.core.offer.OfferDirection; import haveno.core.offer.OfferDirection;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
@ -26,6 +27,7 @@ import haveno.core.trade.protocol.TradePeer;
import java.math.BigInteger; import java.math.BigInteger;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import monero.daemon.model.MoneroTx;
/** /**
* Arbitrator verifies reserve tx from maker or taker. * Arbitrator verifies reserve tx from maker or taker.
@ -55,8 +57,9 @@ public class ArbitratorProcessReserveTx extends TradeTask {
BigInteger tradeFee = isFromTaker ? trade.getTakerFee() : trade.getMakerFee(); BigInteger tradeFee = isFromTaker ? trade.getTakerFee() : trade.getMakerFee();
BigInteger sendAmount = isFromBuyer ? BigInteger.valueOf(0) : offer.getAmount(); BigInteger sendAmount = isFromBuyer ? BigInteger.valueOf(0) : offer.getAmount();
BigInteger securityDeposit = isFromBuyer ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit(); BigInteger securityDeposit = isFromBuyer ? offer.getBuyerSecurityDeposit() : offer.getSellerSecurityDeposit();
Tuple2<MoneroTx, BigInteger> txResult;
try { try {
trade.getXmrWalletService().verifyTradeTx( txResult = trade.getXmrWalletService().verifyTradeTx(
tradeFee, tradeFee,
sendAmount, sendAmount,
securityDeposit, securityDeposit,
@ -64,7 +67,8 @@ public class ArbitratorProcessReserveTx extends TradeTask {
request.getReserveTxHash(), request.getReserveTxHash(),
request.getReserveTxHex(), request.getReserveTxHex(),
request.getReserveTxKey(), request.getReserveTxKey(),
null); null,
true);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Error processing reserve tx from " + (isFromTaker ? "taker " : "maker ") + request.getSenderNodeAddress() + ", offerId=" + offer.getId() + ": " + e.getMessage()); throw new RuntimeException("Error processing reserve tx from " + (isFromTaker ? "taker " : "maker ") + request.getSenderNodeAddress() + ", offerId=" + offer.getId() + ": " + e.getMessage());
} }
@ -74,6 +78,7 @@ public class ArbitratorProcessReserveTx extends TradeTask {
trader.setReserveTxHash(request.getReserveTxHash()); trader.setReserveTxHash(request.getReserveTxHash());
trader.setReserveTxHex(request.getReserveTxHex()); trader.setReserveTxHex(request.getReserveTxHex());
trader.setReserveTxKey(request.getReserveTxKey()); trader.setReserveTxKey(request.getReserveTxKey());
trader.setSecurityDeposit(txResult.second);
// persist trade // persist trade
processModel.getTradeManager().requestPersistence(); processModel.getTradeManager().requestPersistence();

View File

@ -9,6 +9,7 @@ import haveno.common.UserThread;
import haveno.common.config.BaseCurrencyNetwork; import haveno.common.config.BaseCurrencyNetwork;
import haveno.common.config.Config; import haveno.common.config.Config;
import haveno.common.file.FileUtil; import haveno.common.file.FileUtil;
import haveno.common.util.Tuple2;
import haveno.common.util.Utilities; import haveno.common.util.Utilities;
import haveno.core.api.AccountServiceListener; import haveno.core.api.AccountServiceListener;
import haveno.core.api.CoreAccountService; import haveno.core.api.CoreAccountService;
@ -300,7 +301,7 @@ public class XmrWalletService {
*/ */
public MoneroTxWallet createReserveTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String returnAddress) { public MoneroTxWallet createReserveTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String returnAddress) {
log.info("Creating reserve tx with fee={}, sendAmount={}, securityDeposit={}", tradeFee, sendAmount, securityDeposit); log.info("Creating reserve tx with fee={}, sendAmount={}, securityDeposit={}", tradeFee, sendAmount, securityDeposit);
return createTradeTx(tradeFee, sendAmount, securityDeposit, returnAddress); return createTradeTx(tradeFee, sendAmount, securityDeposit, returnAddress, true);
} }
/** /**
@ -326,11 +327,11 @@ public class XmrWalletService {
} }
log.info("Creating deposit tx with fee={}, sendAmount={}, securityDeposit={}", tradeFee, sendAmount, securityDeposit); log.info("Creating deposit tx with fee={}, sendAmount={}, securityDeposit={}", tradeFee, sendAmount, securityDeposit);
return createTradeTx(tradeFee, sendAmount, securityDeposit, multisigAddress); return createTradeTx(tradeFee, sendAmount, securityDeposit, multisigAddress, false);
} }
} }
private MoneroTxWallet createTradeTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address) { private MoneroTxWallet createTradeTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address, boolean isReserveTx) {
MoneroWallet wallet = getWallet(); MoneroWallet wallet = getWallet();
synchronized (wallet) { synchronized (wallet) {
@ -341,10 +342,10 @@ public class XmrWalletService {
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
try { try {
BigInteger appliedSecurityDeposit = new BigDecimal(securityDeposit).multiply(new BigDecimal(1.0 - SECURITY_DEPOSIT_TOLERANCE * appliedTolerance)).toBigInteger(); BigInteger appliedSecurityDeposit = new BigDecimal(securityDeposit).multiply(new BigDecimal(1.0 - SECURITY_DEPOSIT_TOLERANCE * appliedTolerance)).toBigInteger();
BigInteger amount = sendAmount.add(appliedSecurityDeposit); BigInteger amount = sendAmount.add(isReserveTx ? tradeFee : appliedSecurityDeposit);
tradeTx = wallet.createTx(new MoneroTxConfig() tradeTx = wallet.createTx(new MoneroTxConfig()
.setAccountIndex(0) .setAccountIndex(0)
.addDestination(HavenoUtils.getTradeFeeAddress(), tradeFee) .addDestination(HavenoUtils.getTradeFeeAddress(), isReserveTx ? appliedSecurityDeposit : tradeFee) // reserve tx charges security deposit if published
.addDestination(address, amount)); .addDestination(address, amount));
appliedTolerance -= searchDiff; // apply less tolerance to increase security deposit appliedTolerance -= searchDiff; // apply less tolerance to increase security deposit
if (appliedTolerance < 0.0) break; // can send full security deposit if (appliedTolerance < 0.0) break; // can send full security deposit
@ -375,12 +376,13 @@ public class XmrWalletService {
* @param txHex transaction hex * @param txHex transaction hex
* @param txKey transaction key * @param txKey transaction key
* @param keyImages expected key images of inputs, ignored if null * @param keyImages expected key images of inputs, ignored if null
* @return the verified tx * @return tuple with the verified tx and its actual security deposit
*/ */
public MoneroTx verifyTradeTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address, String txHash, String txHex, String txKey, List<String> keyImages) { public Tuple2<MoneroTx, BigInteger> verifyTradeTx(BigInteger tradeFee, BigInteger sendAmount, BigInteger securityDeposit, String address, String txHash, String txHex, String txKey, List<String> keyImages, boolean isReserveTx) {
MoneroDaemonRpc daemon = getDaemon(); MoneroDaemonRpc daemon = getDaemon();
MoneroWallet wallet = getWallet(); MoneroWallet wallet = getWallet();
MoneroTx tx = null; MoneroTx tx = null;
BigInteger actualSecurityDeposit = null;
synchronized (daemon) { synchronized (daemon) {
try { try {
@ -403,28 +405,36 @@ public class XmrWalletService {
// verify unlock height // verify unlock height
if (tx.getUnlockHeight() != 0) throw new RuntimeException("Unlock height must be 0"); if (tx.getUnlockHeight() != 0) throw new RuntimeException("Unlock height must be 0");
// verify trade fee
String feeAddress = HavenoUtils.getTradeFeeAddress();
MoneroCheckTx check = wallet.checkTxKey(txHash, txKey, feeAddress);
if (!check.isGood()) throw new RuntimeException("Invalid proof of trade fee");
if (!check.getReceivedAmount().equals(tradeFee)) throw new RuntimeException("Trade fee is incorrect amount, expected " + tradeFee + " but was " + check.getReceivedAmount());
// verify miner fee // verify miner fee
BigInteger feeEstimate = getFeeEstimate(tx.getWeight()); BigInteger feeEstimate = getFeeEstimate(tx.getWeight());
double feeDiff = tx.getFee().subtract(feeEstimate).abs().doubleValue() / feeEstimate.doubleValue(); // TODO: use BigDecimal? double feeDiff = tx.getFee().subtract(feeEstimate).abs().doubleValue() / feeEstimate.doubleValue(); // TODO: use BigDecimal?
if (feeDiff > MINER_FEE_TOLERANCE) throw new Error("Miner fee is not within " + (MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + feeEstimate + " but was " + tx.getFee()); if (feeDiff > MINER_FEE_TOLERANCE) throw new Error("Miner fee is not within " + (MINER_FEE_TOLERANCE * 100) + "% of estimated fee, expected " + feeEstimate + " but was " + tx.getFee());
log.info("Trade tx fee {} is within tolerance, diff%={}", tx.getFee(), feeDiff); log.info("Trade tx fee {} is within tolerance, diff%={}", tx.getFee(), feeDiff);
// verify transfer proof to fee address
String feeAddress = HavenoUtils.getTradeFeeAddress();
MoneroCheckTx feeCheck = wallet.checkTxKey(txHash, txKey, feeAddress);
if (!feeCheck.isGood()) throw new RuntimeException("Invalid proof of trade fee");
// verify transfer proof to return address
MoneroCheckTx returnCheck = wallet.checkTxKey(txHash, txKey, address);
if (!returnCheck.isGood()) throw new RuntimeException("Invalid proof of return funds");
// collect actual trade fee, send amount, and security deposit
BigInteger actualTradeFee = isReserveTx ? returnCheck.getReceivedAmount().subtract(sendAmount) : feeCheck.getReceivedAmount();
actualSecurityDeposit = isReserveTx ? feeCheck.getReceivedAmount() : returnCheck.getReceivedAmount().subtract(sendAmount);
BigInteger actualSendAmount = returnCheck.getReceivedAmount().subtract(isReserveTx ? actualTradeFee : actualSecurityDeposit);
// verify trade fee
if (!tradeFee.equals(actualTradeFee)) throw new RuntimeException("Trade fee is incorrect amount, expected " + tradeFee + " but was " + actualTradeFee);
// verify sufficient security deposit // verify sufficient security deposit
check = wallet.checkTxKey(txHash, txKey, address);
if (!check.isGood()) throw new RuntimeException("Invalid proof of deposit amount");
BigInteger minSecurityDeposit = new BigDecimal(securityDeposit).multiply(new BigDecimal(1.0 - SECURITY_DEPOSIT_TOLERANCE)).toBigInteger(); BigInteger minSecurityDeposit = new BigDecimal(securityDeposit).multiply(new BigDecimal(1.0 - SECURITY_DEPOSIT_TOLERANCE)).toBigInteger();
BigInteger actualSecurityDeposit = check.getReceivedAmount().subtract(sendAmount);
if (actualSecurityDeposit.compareTo(minSecurityDeposit) < 0) throw new RuntimeException("Security deposit amount is not enough, needed " + minSecurityDeposit + " but was " + actualSecurityDeposit); if (actualSecurityDeposit.compareTo(minSecurityDeposit) < 0) throw new RuntimeException("Security deposit amount is not enough, needed " + minSecurityDeposit + " but was " + actualSecurityDeposit);
// verify deposit amount + miner fee within dust tolerance // verify deposit amount + miner fee within dust tolerance
BigInteger minDepositAndFee = sendAmount.add(securityDeposit).subtract(new BigDecimal(tx.getFee()).multiply(new BigDecimal(1.0 - DUST_TOLERANCE)).toBigInteger()); BigInteger minDepositAndFee = sendAmount.add(securityDeposit).subtract(new BigDecimal(tx.getFee()).multiply(new BigDecimal(1.0 - DUST_TOLERANCE)).toBigInteger());
BigInteger actualDepositAndFee = check.getReceivedAmount().add(tx.getFee()); BigInteger actualDepositAndFee = actualSendAmount.add(actualSecurityDeposit).add(tx.getFee());
if (actualDepositAndFee.compareTo(minDepositAndFee) < 0) throw new RuntimeException("Deposit amount + fee is not enough, needed " + minDepositAndFee + " but was " + actualDepositAndFee); if (actualDepositAndFee.compareTo(minDepositAndFee) < 0) throw new RuntimeException("Deposit amount + fee is not enough, needed " + minDepositAndFee + " but was " + actualDepositAndFee);
} finally { } finally {
try { try {
@ -434,7 +444,7 @@ public class XmrWalletService {
throw err.getCode() == -32601 ? new RuntimeException("Failed to flush tx from pool. Arbitrator must use trusted, unrestricted daemon") : err; throw err.getCode() == -32601 ? new RuntimeException("Failed to flush tx from pool. Arbitrator must use trusted, unrestricted daemon") : err;
} }
} }
return tx; return new Tuple2<>(tx, actualSecurityDeposit);
} }
} }

View File

@ -993,7 +993,7 @@ portfolio.failed.cantUnfail=This trade cannot be moved back to open trades at th
Try again after completion of trade(s) {0} Try again after completion of trade(s) {0}
portfolio.failed.depositTxNull=The trade cannot be reverted to a open trade. Deposit transaction is null. portfolio.failed.depositTxNull=The trade cannot be reverted to a open trade. Deposit transaction is null.
portfolio.failed.delayedPayoutTxNull=The trade cannot be reverted to a open trade. Delayed payout transaction is null. portfolio.failed.delayedPayoutTxNull=The trade cannot be reverted to a open trade. Delayed payout transaction is null.
portfolio.failed.penalty.msg=This will charge the {0}/{1} the trade fee of {2} and return the remaining trade funds to their wallet. Are you sure you want to send?\n\n\ portfolio.failed.penalty.msg=This will charge the {0}/{1} a penalty fee of {2} and return the remaining trade funds to their wallet. Are you sure you want to send?\n\n\
Other Info:\n\ Other Info:\n\
Transaction Fee: {3}\n\ Transaction Fee: {3}\n\
Reserve Tx Hash: {4} Reserve Tx Hash: {4}
@ -1097,9 +1097,9 @@ support.tab.ArbitratorsSupportTickets={0}'s tickets
support.filter=Search disputes support.filter=Search disputes
support.filter.prompt=Enter trade ID, date, onion address or account data support.filter.prompt=Enter trade ID, date, onion address or account data
support.tab.SignedOffers=Signed Offers support.tab.SignedOffers=Signed Offers
support.prompt.signedOffer.penalty.msg=This will charge the maker the trade fee and return their remaining trade funds to their wallet. Are you sure you want to send?\n\n\ support.prompt.signedOffer.penalty.msg=This will charge the maker a penalty fee and return the remaining trade funds to their wallet. Are you sure you want to send?\n\n\
Offer ID: {0}\n\ Offer ID: {0}\n\
Maker Trade Fee: {1}\n\ Maker Penalty Fee: {1}\n\
Reserve Tx Miner Fee: {2}\n\ Reserve Tx Miner Fee: {2}\n\
Reserve Tx Hash: {3}\n\ Reserve Tx Hash: {3}\n\
Reserve Tx Key Images: {4}\n\ Reserve Tx Key Images: {4}\n\
@ -1163,15 +1163,15 @@ support.requested=Requested
support.closed=Closed support.closed=Closed
support.open=Open support.open=Open
support.process=Process support.process=Process
support.buyerMaker=XMR buyer/Maker support.buyerMaker=XMR Buyer/Maker
support.sellerMaker=XMR seller/Maker support.sellerMaker=XMR Seller/Maker
support.buyerTaker=XMR buyer/Taker support.buyerTaker=XMR Buyer/Taker
support.sellerTaker=XMR seller/Taker support.sellerTaker=XMR Seller/Taker
support.txKeyImages=Key Images support.txKeyImages=Key Images
support.txHash=Transaction Hash support.txHash=Transaction Hash
support.txHex=Transaction Hex support.txHex=Transaction Hex
support.signature=Signature support.signature=Signature
support.maker.trade.fee=Maker Trade Fee support.maker.penalty.fee=Maker Penalty Fee
support.tx.miner.fee=Miner Fee support.tx.miner.fee=Miner Fee
support.backgroundInfo=Haveno is not a company, so it handles disputes differently.\n\n\ support.backgroundInfo=Haveno is not a company, so it handles disputes differently.\n\n\

View File

@ -218,7 +218,7 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
handleContextMenu("portfolio.failed.penalty.msg", handleContextMenu("portfolio.failed.penalty.msg",
Res.get(selectedFailedTrade.getMaker() == selectedFailedTrade.getBuyer() ? "shared.buyer" : "shared.seller"), Res.get(selectedFailedTrade.getMaker() == selectedFailedTrade.getBuyer() ? "shared.buyer" : "shared.seller"),
Res.get("shared.maker"), Res.get("shared.maker"),
selectedFailedTrade.getMakerFee(), selectedFailedTrade.getMaker().getSecurityDeposit(),
selectedFailedTrade.getMaker().getReserveTxHash(), selectedFailedTrade.getMaker().getReserveTxHash(),
selectedFailedTrade.getMaker().getReserveTxHex()); selectedFailedTrade.getMaker().getReserveTxHex());
}); });
@ -228,7 +228,7 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
handleContextMenu("portfolio.failed.penalty.msg", handleContextMenu("portfolio.failed.penalty.msg",
Res.get(selectedFailedTrade.getTaker() == selectedFailedTrade.getBuyer() ? "shared.buyer" : "shared.seller"), Res.get(selectedFailedTrade.getTaker() == selectedFailedTrade.getBuyer() ? "shared.buyer" : "shared.seller"),
Res.get("shared.taker"), Res.get("shared.taker"),
selectedFailedTrade.getTakerFee(), selectedFailedTrade.getTaker().getSecurityDeposit(),
selectedFailedTrade.getTaker().getReserveTxHash(), selectedFailedTrade.getTaker().getReserveTxHash(),
selectedFailedTrade.getTaker().getReserveTxHex()); selectedFailedTrade.getTaker().getReserveTxHex());
}); });

View File

@ -85,7 +85,7 @@ public class SignedOfferView extends ActivatableView<VBox, Void> {
@FXML @FXML
TableColumn<SignedOffer, SignedOffer> reserveTxMinerFeeColumn; TableColumn<SignedOffer, SignedOffer> reserveTxMinerFeeColumn;
@FXML @FXML
TableColumn<SignedOffer, SignedOffer> makerTradeFeeColumn; TableColumn<SignedOffer, SignedOffer> makerPenaltyFeeColumn;
@FXML @FXML
InputTextField filterTextField; InputTextField filterTextField;
@FXML @FXML
@ -163,7 +163,7 @@ public class SignedOfferView extends ActivatableView<VBox, Void> {
if(selectedSignedOffer != null) { if(selectedSignedOffer != null) {
new Popup().warning(Res.get("support.prompt.signedOffer.penalty.msg", new Popup().warning(Res.get("support.prompt.signedOffer.penalty.msg",
selectedSignedOffer.getOfferId(), selectedSignedOffer.getOfferId(),
HavenoUtils.formatXmr(selectedSignedOffer.getMakerTradeFee(), true), HavenoUtils.formatXmr(selectedSignedOffer.getPenaltyAmount(), true),
HavenoUtils.formatXmr(selectedSignedOffer.getReserveTxMinerFee(), true), HavenoUtils.formatXmr(selectedSignedOffer.getReserveTxMinerFee(), true),
selectedSignedOffer.getReserveTxHash(), selectedSignedOffer.getReserveTxHash(),
selectedSignedOffer.getReserveTxKeyImages()) selectedSignedOffer.getReserveTxKeyImages())
@ -212,8 +212,8 @@ public class SignedOfferView extends ActivatableView<VBox, Void> {
arbitratorSignatureColumn = getArbitratorSignatureColumn(); arbitratorSignatureColumn = getArbitratorSignatureColumn();
tableView.getColumns().add(arbitratorSignatureColumn); tableView.getColumns().add(arbitratorSignatureColumn);
makerTradeFeeColumn = getMakerTradeFeeColumn(); makerPenaltyFeeColumn = getMakerPenaltyFeeColumn();
tableView.getColumns().add(makerTradeFeeColumn); tableView.getColumns().add(makerPenaltyFeeColumn);
reserveTxMinerFeeColumn = getReserveTxMinerFeeColumn(); reserveTxMinerFeeColumn = getReserveTxMinerFeeColumn();
tableView.getColumns().add(reserveTxMinerFeeColumn); tableView.getColumns().add(reserveTxMinerFeeColumn);
@ -389,8 +389,8 @@ public class SignedOfferView extends ActivatableView<VBox, Void> {
return column; return column;
} }
private TableColumn<SignedOffer, SignedOffer> getMakerTradeFeeColumn() { private TableColumn<SignedOffer, SignedOffer> getMakerPenaltyFeeColumn() {
TableColumn<SignedOffer, SignedOffer> column = new AutoTooltipTableColumn<>(Res.get("support.maker.trade.fee")) { TableColumn<SignedOffer, SignedOffer> column = new AutoTooltipTableColumn<>(Res.get("support.maker.penalty.fee")) {
{ {
setMinWidth(160); setMinWidth(160);
} }
@ -405,7 +405,7 @@ public class SignedOfferView extends ActivatableView<VBox, Void> {
public void updateItem(final SignedOffer item, boolean empty) { public void updateItem(final SignedOffer item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
if (item != null && !empty) if (item != null && !empty)
setText(HavenoUtils.formatXmr(item.getMakerTradeFee(), true)); setText(HavenoUtils.formatXmr(item.getPenaltyAmount(), true));
else else
setText(""); setText("");
} }

View File

@ -1351,7 +1351,7 @@ message SignedOffer {
int64 time_stamp = 1; int64 time_stamp = 1;
string offer_id = 2; string offer_id = 2;
uint64 trade_amount = 3; uint64 trade_amount = 3;
uint64 maker_trade_fee = 4; uint64 penalty_amount = 4;
string reserve_tx_hash = 5; string reserve_tx_hash = 5;
string reserve_tx_hex = 6; string reserve_tx_hex = 6;
repeated string reserve_tx_key_images = 7; repeated string reserve_tx_key_images = 7;