mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-08-09 07:02:24 -04:00
add date and key images to SignedOffer
arbitrator retains failed trades after reserve tx received legacy ui shows trade details including reserve tx, with or w/o contract fix npe searching arbitrator tickets due to null payment accounts synchronize offer book list items fix npe before key image poller initialized
This commit is contained in:
parent
882f1c070a
commit
a0235c8ebd
8 changed files with 194 additions and 153 deletions
|
@ -89,28 +89,32 @@ public class OfferBook {
|
|||
// Use offer.equals(offer) to see if the OfferBook list contains an exact
|
||||
// match -- offer.equals(offer) includes comparisons of payload, state
|
||||
// and errorMessage.
|
||||
boolean hasSameOffer = offerBookListItems.stream().anyMatch(item -> item.getOffer().equals(offer));
|
||||
if (!hasSameOffer) {
|
||||
OfferBookListItem newOfferBookListItem = new OfferBookListItem(offer);
|
||||
removeDuplicateItem(newOfferBookListItem);
|
||||
offerBookListItems.add(newOfferBookListItem); // Add replacement.
|
||||
if (log.isDebugEnabled()) { // TODO delete debug stmt in future PR.
|
||||
log.debug("onAdded: Added new offer {}\n"
|
||||
+ "\twith newItem.payloadHash: {}",
|
||||
offer.getId(),
|
||||
newOfferBookListItem.hashOfPayload.getHex());
|
||||
synchronized (offerBookListItems) {
|
||||
boolean hasSameOffer = offerBookListItems.stream().anyMatch(item -> item.getOffer().equals(offer));
|
||||
if (!hasSameOffer) {
|
||||
OfferBookListItem newOfferBookListItem = new OfferBookListItem(offer);
|
||||
removeDuplicateItem(newOfferBookListItem);
|
||||
offerBookListItems.add(newOfferBookListItem); // Add replacement.
|
||||
if (log.isDebugEnabled()) { // TODO delete debug stmt in future PR.
|
||||
log.debug("onAdded: Added new offer {}\n"
|
||||
+ "\twith newItem.payloadHash: {}",
|
||||
offer.getId(),
|
||||
newOfferBookListItem.hashOfPayload.getHex());
|
||||
}
|
||||
} else {
|
||||
log.debug("We have the exact same offer already in our list and ignore the onAdded call. ID={}", offer.getId());
|
||||
}
|
||||
} else {
|
||||
log.debug("We have the exact same offer already in our list and ignore the onAdded call. ID={}", offer.getId());
|
||||
printOfferBookListItems("After onAdded");
|
||||
}
|
||||
printOfferBookListItems("After onAdded");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoved(Offer offer) {
|
||||
printOfferBookListItems("Before onRemoved");
|
||||
removeOffer(offer);
|
||||
printOfferBookListItems("After onRemoved");
|
||||
synchronized (offerBookListItems) {
|
||||
printOfferBookListItems("Before onRemoved");
|
||||
removeOffer(offer);
|
||||
printOfferBookListItems("After onRemoved");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -122,77 +126,83 @@ public class OfferBook {
|
|||
}
|
||||
|
||||
private void removeDuplicateItem(OfferBookListItem newOfferBookListItem) {
|
||||
String offerId = newOfferBookListItem.getOffer().getId();
|
||||
// We need to remove any view items with a matching offerId before
|
||||
// a newOfferBookListItem is added to the view.
|
||||
List<OfferBookListItem> duplicateItems = offerBookListItems.stream()
|
||||
.filter(item -> item.getOffer().getId().equals(offerId))
|
||||
.collect(Collectors.toList());
|
||||
duplicateItems.forEach(oldOfferItem -> {
|
||||
offerBookListItems.remove(oldOfferItem);
|
||||
if (log.isDebugEnabled()) { // TODO delete debug stmt in future PR.
|
||||
log.debug("onAdded: Removed old offer {}\n"
|
||||
+ "\twith payload hash {} from list.\n"
|
||||
+ "\tThis may make a subsequent onRemoved( {} ) call redundant.",
|
||||
offerId,
|
||||
oldOfferItem.getHashOfPayload().getHex(),
|
||||
oldOfferItem.getOffer().getId());
|
||||
}
|
||||
});
|
||||
synchronized (offerBookListItems) {
|
||||
String offerId = newOfferBookListItem.getOffer().getId();
|
||||
|
||||
// We need to remove any view items with a matching offerId before
|
||||
// a newOfferBookListItem is added to the view.
|
||||
List<OfferBookListItem> duplicateItems = offerBookListItems.stream()
|
||||
.filter(item -> item.getOffer().getId().equals(offerId))
|
||||
.collect(Collectors.toList());
|
||||
duplicateItems.forEach(oldOfferItem -> {
|
||||
offerBookListItems.remove(oldOfferItem);
|
||||
if (log.isDebugEnabled()) { // TODO delete debug stmt in future PR.
|
||||
log.debug("onAdded: Removed old offer {}\n"
|
||||
+ "\twith payload hash {} from list.\n"
|
||||
+ "\tThis may make a subsequent onRemoved( {} ) call redundant.",
|
||||
offerId,
|
||||
oldOfferItem.getHashOfPayload().getHex(),
|
||||
oldOfferItem.getOffer().getId());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void removeOffer(Offer offer) {
|
||||
// Update state in case that that offer is used in the take offer screen, so it gets updated correctly
|
||||
offer.setState(Offer.State.REMOVED);
|
||||
offer.cancelAvailabilityRequest();
|
||||
synchronized (offerBookListItems) {
|
||||
|
||||
P2PDataStorage.ByteArray hashOfPayload = new P2PDataStorage.ByteArray(offer.getOfferPayload().getHash());
|
||||
// Update state in case that that offer is used in the take offer screen, so it gets updated correctly
|
||||
offer.setState(Offer.State.REMOVED);
|
||||
offer.cancelAvailabilityRequest();
|
||||
|
||||
if (log.isDebugEnabled()) { // TODO delete debug stmt in future PR.
|
||||
log.debug("onRemoved: id = {}\n"
|
||||
+ "\twith payload-hash = {}",
|
||||
offer.getId(),
|
||||
hashOfPayload.getHex());
|
||||
}
|
||||
P2PDataStorage.ByteArray hashOfPayload = new P2PDataStorage.ByteArray(offer.getOfferPayload().getHash());
|
||||
|
||||
// Find the removal candidate in the OfferBook list with matching offerId and payload-hash.
|
||||
Optional<OfferBookListItem> candidateWithMatchingPayloadHash = offerBookListItems.stream()
|
||||
.filter(item -> item.getOffer().getId().equals(offer.getId())
|
||||
&& item.hashOfPayload.equals(hashOfPayload))
|
||||
.findAny();
|
||||
|
||||
if (!candidateWithMatchingPayloadHash.isPresent()) {
|
||||
if (log.isDebugEnabled()) { // TODO delete debug stmt in future PR.
|
||||
log.debug("UI view list does not contain offer with id {} and payload-hash {}",
|
||||
log.debug("onRemoved: id = {}\n"
|
||||
+ "\twith payload-hash = {}",
|
||||
offer.getId(),
|
||||
hashOfPayload.getHex());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
OfferBookListItem candidate = candidateWithMatchingPayloadHash.get();
|
||||
// Remove the candidate only if the candidate's offer payload the hash matches the
|
||||
// onRemoved hashOfPayload parameter. We may receive add/remove messages out of
|
||||
// order from the API's 'editoffer' method, and use the offer payload hash to
|
||||
// ensure we do not remove an edited offer immediately after it was added.
|
||||
if (candidate.getHashOfPayload().equals(hashOfPayload)) {
|
||||
// The payload-hash test passed, remove the candidate and print reason.
|
||||
offerBookListItems.remove(candidate);
|
||||
// Find the removal candidate in the OfferBook list with matching offerId and payload-hash.
|
||||
Optional<OfferBookListItem> candidateWithMatchingPayloadHash = offerBookListItems.stream()
|
||||
.filter(item -> item.getOffer().getId().equals(offer.getId())
|
||||
&& item.hashOfPayload.equals(hashOfPayload))
|
||||
.findAny();
|
||||
|
||||
if (log.isDebugEnabled()) { // TODO delete debug stmt in future PR.
|
||||
log.debug("Candidate.payload-hash: {} == onRemoved.payload-hash: {} ?"
|
||||
+ " Yes, removed old offer",
|
||||
candidate.hashOfPayload.getHex(),
|
||||
hashOfPayload.getHex());
|
||||
if (!candidateWithMatchingPayloadHash.isPresent()) {
|
||||
if (log.isDebugEnabled()) { // TODO delete debug stmt in future PR.
|
||||
log.debug("UI view list does not contain offer with id {} and payload-hash {}",
|
||||
offer.getId(),
|
||||
hashOfPayload.getHex());
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (log.isDebugEnabled()) { // TODO delete debug stmt in future PR.
|
||||
// Candidate's payload-hash test failed: payload-hash != onRemoved.payload-hash.
|
||||
// Print reason for not removing candidate.
|
||||
log.debug("Candidate.payload-hash: {} == onRemoved.payload-hash: {} ?"
|
||||
+ " No, old offer not removed",
|
||||
candidate.hashOfPayload.getHex(),
|
||||
hashOfPayload.getHex());
|
||||
|
||||
OfferBookListItem candidate = candidateWithMatchingPayloadHash.get();
|
||||
// Remove the candidate only if the candidate's offer payload the hash matches the
|
||||
// onRemoved hashOfPayload parameter. We may receive add/remove messages out of
|
||||
// order from the API's 'editoffer' method, and use the offer payload hash to
|
||||
// ensure we do not remove an edited offer immediately after it was added.
|
||||
if (candidate.getHashOfPayload().equals(hashOfPayload)) {
|
||||
// The payload-hash test passed, remove the candidate and print reason.
|
||||
offerBookListItems.remove(candidate);
|
||||
|
||||
if (log.isDebugEnabled()) { // TODO delete debug stmt in future PR.
|
||||
log.debug("Candidate.payload-hash: {} == onRemoved.payload-hash: {} ?"
|
||||
+ " Yes, removed old offer",
|
||||
candidate.hashOfPayload.getHex(),
|
||||
hashOfPayload.getHex());
|
||||
}
|
||||
} else {
|
||||
if (log.isDebugEnabled()) { // TODO delete debug stmt in future PR.
|
||||
// Candidate's payload-hash test failed: payload-hash != onRemoved.payload-hash.
|
||||
// Print reason for not removing candidate.
|
||||
log.debug("Candidate.payload-hash: {} == onRemoved.payload-hash: {} ?"
|
||||
+ " No, old offer not removed",
|
||||
candidate.hashOfPayload.getHex(),
|
||||
hashOfPayload.getHex());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,33 +212,37 @@ public class OfferBook {
|
|||
}
|
||||
|
||||
public void fillOfferBookListItems() {
|
||||
try {
|
||||
// setAll causes sometimes an UnsupportedOperationException
|
||||
// Investigate why....
|
||||
offerBookListItems.clear();
|
||||
offerBookListItems.addAll(offerBookService.getOffers().stream()
|
||||
.filter(this::isOfferAllowed)
|
||||
.map(OfferBookListItem::new)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
log.debug("offerBookListItems.size {}", offerBookListItems.size());
|
||||
fillOfferCountMaps();
|
||||
} catch (Throwable t) {
|
||||
log.error("Error at fillOfferBookListItems: " + t);
|
||||
synchronized (offerBookListItems) {
|
||||
try {
|
||||
// setAll causes sometimes an UnsupportedOperationException
|
||||
// Investigate why....
|
||||
offerBookListItems.clear();
|
||||
offerBookListItems.addAll(offerBookService.getOffers().stream()
|
||||
.filter(this::isOfferAllowed)
|
||||
.map(OfferBookListItem::new)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
log.debug("offerBookListItems.size {}", offerBookListItems.size());
|
||||
fillOfferCountMaps();
|
||||
} catch (Throwable t) {
|
||||
log.error("Error at fillOfferBookListItems: " + t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void printOfferBookListItems(String msg) {
|
||||
if (log.isDebugEnabled()) {
|
||||
if (offerBookListItems.size() == 0) {
|
||||
log.debug("{} -> OfferBookListItems: none", msg);
|
||||
return;
|
||||
synchronized (offerBookListItems) {
|
||||
if (log.isDebugEnabled()) {
|
||||
if (offerBookListItems.size() == 0) {
|
||||
log.debug("{} -> OfferBookListItems: none", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder(msg + " -> ").append("OfferBookListItems:").append("\n");
|
||||
offerBookListItems.forEach(i -> stringBuilder.append("\t").append(i.toString()).append("\n"));
|
||||
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
|
||||
log.debug(stringBuilder.toString());
|
||||
}
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder(msg + " -> ").append("OfferBookListItems:").append("\n");
|
||||
offerBookListItems.forEach(i -> stringBuilder.append("\t").append(i.toString()).append("\n"));
|
||||
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
|
||||
log.debug(stringBuilder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -319,63 +319,66 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
|
|||
String sellerWitnessHash = trade.getSeller().getAccountAgeWitness() == null ? "null" : Utilities.bytesAsHexString(trade.getSeller().getAccountAgeWitness().getHash());
|
||||
String sellerPubKeyRingHash = Utilities.bytesAsHexString(trade.getSeller().getPubKeyRing().getSignaturePubKeyBytes());
|
||||
|
||||
if (contract != null) {
|
||||
viewContractButton.setOnAction(e -> {
|
||||
TextArea textArea = new HavenoTextArea();
|
||||
textArea.setText(trade.getContractAsJson());
|
||||
String data = "Contract as json:\n";
|
||||
data += trade.getContractAsJson();
|
||||
data += "\n\nOther detail data:";
|
||||
if (offer.isFiatOffer()) {
|
||||
data += "\n\nBuyers witness hash,pub key ring hash: " + buyerWitnessHash + "," + buyerPubKeyRingHash;
|
||||
data += "\nBuyers account age: " + buyersAccountAge;
|
||||
data += "\nSellers witness hash,pub key ring hash: " + sellerWitnessHash + "," + sellerPubKeyRingHash;
|
||||
data += "\nSellers account age: " + sellersAccountAge;
|
||||
}
|
||||
viewContractButton.setOnAction(e -> {
|
||||
TextArea textArea = new HavenoTextArea();
|
||||
textArea.setText(trade.getContractAsJson());
|
||||
String data = "Contract as json:\n";
|
||||
data += trade.getContractAsJson();
|
||||
data += "\n\nOther detail data:";
|
||||
if (!trade.isDepositPublished()) {
|
||||
data += "\n\n" + (trade.getMaker() == trade.getBuyer() ? "Buyer" : "Seller") + " as maker reserve tx hex: " + trade.getMaker().getReserveTxHex();
|
||||
data += "\n\n" + (trade.getTaker() == trade.getBuyer() ? "Buyer" : "Seller") + " as taker reserve tx hex: " + trade.getTaker().getReserveTxHex();
|
||||
}
|
||||
if (offer.isFiatOffer()) {
|
||||
data += "\n\nBuyers witness hash,pub key ring hash: " + buyerWitnessHash + "," + buyerPubKeyRingHash;
|
||||
data += "\nBuyers account age: " + buyersAccountAge;
|
||||
data += "\nSellers witness hash,pub key ring hash: " + sellerWitnessHash + "," + sellerPubKeyRingHash;
|
||||
data += "\nSellers account age: " + sellersAccountAge;
|
||||
}
|
||||
|
||||
// TODO (woodser): include maker and taker deposit tx hex in contract?
|
||||
// TODO (woodser): include maker and taker deposit tx hex in contract?
|
||||
// if (depositTx != null) {
|
||||
// String depositTxAsHex = Utils.HEX.encode(depositTx.bitcoinSerialize(true));
|
||||
// data += "\n\nRaw deposit transaction as hex:\n" + depositTxAsHex;
|
||||
// }
|
||||
|
||||
data += "\n\nSelected arbitrator: " + DisputeAgentLookupMap.getMatrixUserName(contract.getArbitratorNodeAddress().getFullAddress());
|
||||
|
||||
textArea.setText(data);
|
||||
textArea.setPrefHeight(50);
|
||||
textArea.setEditable(false);
|
||||
textArea.setWrapText(true);
|
||||
textArea.setPrefSize(800, 600);
|
||||
data += "\n\nSelected arbitrator: " + trade.getArbitrator().getNodeAddress();
|
||||
|
||||
Scene viewContractScene = new Scene(textArea);
|
||||
Stage viewContractStage = new Stage();
|
||||
viewContractStage.setTitle(Res.get("shared.contract.title", trade.getShortId()));
|
||||
viewContractStage.setScene(viewContractScene);
|
||||
if (owner == null)
|
||||
owner = MainView.getRootContainer();
|
||||
Scene rootScene = owner.getScene();
|
||||
viewContractStage.initOwner(rootScene.getWindow());
|
||||
viewContractStage.initModality(Modality.NONE);
|
||||
viewContractStage.initStyle(StageStyle.UTILITY);
|
||||
viewContractStage.setOpacity(0);
|
||||
viewContractStage.show();
|
||||
textArea.setText(data);
|
||||
textArea.setPrefHeight(50);
|
||||
textArea.setEditable(false);
|
||||
textArea.setWrapText(true);
|
||||
textArea.setPrefSize(800, 600);
|
||||
|
||||
Window window = rootScene.getWindow();
|
||||
double titleBarHeight = window.getHeight() - rootScene.getHeight();
|
||||
viewContractStage.setX(Math.round(window.getX() + (owner.getWidth() - viewContractStage.getWidth()) / 2) + 200);
|
||||
viewContractStage.setY(Math.round(window.getY() + titleBarHeight + (owner.getHeight() - viewContractStage.getHeight()) / 2) + 50);
|
||||
// Delay display to next render frame to avoid that the popup is first quickly displayed in default position
|
||||
// and after a short moment in the correct position
|
||||
UserThread.execute(() -> viewContractStage.setOpacity(1));
|
||||
Scene viewContractScene = new Scene(textArea);
|
||||
Stage viewContractStage = new Stage();
|
||||
viewContractStage.setTitle(Res.get("shared.contract.title", trade.getShortId()));
|
||||
viewContractStage.setScene(viewContractScene);
|
||||
if (owner == null)
|
||||
owner = MainView.getRootContainer();
|
||||
Scene rootScene = owner.getScene();
|
||||
viewContractStage.initOwner(rootScene.getWindow());
|
||||
viewContractStage.initModality(Modality.NONE);
|
||||
viewContractStage.initStyle(StageStyle.UTILITY);
|
||||
viewContractStage.setOpacity(0);
|
||||
viewContractStage.show();
|
||||
|
||||
viewContractScene.setOnKeyPressed(ev -> {
|
||||
if (ev.getCode() == KeyCode.ESCAPE) {
|
||||
ev.consume();
|
||||
viewContractStage.hide();
|
||||
}
|
||||
});
|
||||
Window window = rootScene.getWindow();
|
||||
double titleBarHeight = window.getHeight() - rootScene.getHeight();
|
||||
viewContractStage.setX(Math.round(window.getX() + (owner.getWidth() - viewContractStage.getWidth()) / 2) + 200);
|
||||
viewContractStage.setY(Math.round(window.getY() + titleBarHeight + (owner.getHeight() - viewContractStage.getHeight()) / 2) + 50);
|
||||
// Delay display to next render frame to avoid that the popup is first quickly displayed in default position
|
||||
// and after a short moment in the correct position
|
||||
UserThread.execute(() -> viewContractStage.setOpacity(1));
|
||||
|
||||
viewContractScene.setOnKeyPressed(ev -> {
|
||||
if (ev.getCode() == KeyCode.ESCAPE) {
|
||||
ev.consume();
|
||||
viewContractStage.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
closeButton.setOnAction(e -> {
|
||||
closeHandlerOptional.ifPresent(Runnable::run);
|
||||
|
|
|
@ -445,11 +445,11 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
|
|||
return FilterResult.SELLER_NODE_ADDRESS;
|
||||
}
|
||||
|
||||
if (dispute.getBuyerPaymentAccountPayload().getPaymentDetails().toLowerCase().contains(filter)) {
|
||||
if (dispute.getBuyerPaymentAccountPayload() != null && dispute.getBuyerPaymentAccountPayload().getPaymentDetails().toLowerCase().contains(filter)) {
|
||||
return FilterResult.BUYER_ACCOUNT_DETAILS;
|
||||
}
|
||||
|
||||
if (dispute.getSellerPaymentAccountPayload().getPaymentDetails().toLowerCase().contains(filter)) {
|
||||
if (dispute.getSellerPaymentAccountPayload() != null && dispute.getSellerPaymentAccountPayload().getPaymentDetails().toLowerCase().contains(filter)) {
|
||||
return FilterResult.SELLER_ACCOUNT_DETAILS;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue