From ba8a9ae21da6513b25cd5e107dd447f35fdc5b36 Mon Sep 17 00:00:00 2001
From: woodser <woodser@protonmail.com>
Date: Tue, 2 Jan 2024 13:19:38 -0500
Subject: [PATCH] await external prices on startup

---
 .../haveno/core/offer/OpenOfferManager.java   |  2 +-
 .../core/provider/price/PriceFeedService.java | 37 +++++++++++++------
 .../core/support/dispute/DisputeManager.java  |  2 +-
 3 files changed, 27 insertions(+), 14 deletions(-)

diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java
index ce3a86a2..27bf6fd9 100644
--- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java
+++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java
@@ -403,7 +403,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
         HavenoUtils.submitToThread(() -> {
             
             // Wait for prices to be available
-            priceFeedService.awaitPrices();
+            priceFeedService.awaitExternalPrices();
 
             // Republish means we send the complete offer object
             republishOffers();
diff --git a/core/src/main/java/haveno/core/provider/price/PriceFeedService.java b/core/src/main/java/haveno/core/provider/price/PriceFeedService.java
index 4d2c4e08..28cab492 100644
--- a/core/src/main/java/haveno/core/provider/price/PriceFeedService.java
+++ b/core/src/main/java/haveno/core/provider/price/PriceFeedService.java
@@ -140,17 +140,24 @@ public class PriceFeedService {
     /**
      * Awaits prices to be available, but does not request them.
      */
-    public void awaitPrices() {
-        if (hasPrices()) return;
+    public void awaitExternalPrices() {
         CountDownLatch latch = new CountDownLatch(1);
-        ChangeListener<? super Number> listener = (observable, oldValue, newValue) -> { latch.countDown(); };
+        ChangeListener<? super Number> listener = (observable, oldValue, newValue) -> { 
+            if (hasExternalPrices()) latch.countDown();
+        };
         updateCounter.addListener(listener);
+        if (hasExternalPrices()) {
+            updateCounter.removeListener(listener);
+            return;
+        }
         HavenoUtils.awaitLatch(latch);
         updateCounter.removeListener(listener);
     }
 
-    public boolean hasPrices() {
-        return !cache.isEmpty();
+    public boolean hasExternalPrices() {
+        synchronized (cache) {
+            return cache.values().stream().anyMatch(MarketPrice::isExternallyProvidedPrice);
+        }
     }
 
     public void startRequestingPrices() {
@@ -280,16 +287,20 @@ public class PriceFeedService {
 
     @Nullable
     public MarketPrice getMarketPrice(String currencyCode) {
-        return cache.getOrDefault(currencyCode, null);
+        synchronized (cache) {
+            return cache.getOrDefault(currencyCode, null);
+        }
     }
 
     private void setHavenoMarketPrice(String currencyCode, Price price) {
         UserThread.await(() -> {
-            if (!cache.containsKey(currencyCode) || !cache.get(currencyCode).isExternallyProvidedPrice()) {
-                cache.put(currencyCode, new MarketPrice(currencyCode,
-                        MathUtils.scaleDownByPowerOf10(price.getValue(), CurrencyUtil.isCryptoCurrency(currencyCode) ? CryptoMoney.SMALLEST_UNIT_EXPONENT : TraditionalMoney.SMALLEST_UNIT_EXPONENT),
-                        0,
-                        false));
+            synchronized (cache) {
+                if (!cache.containsKey(currencyCode) || !cache.get(currencyCode).isExternallyProvidedPrice()) {
+                    cache.put(currencyCode, new MarketPrice(currencyCode,
+                            MathUtils.scaleDownByPowerOf10(price.getValue(), CurrencyUtil.isCryptoCurrency(currencyCode) ? CryptoMoney.SMALLEST_UNIT_EXPONENT : TraditionalMoney.SMALLEST_UNIT_EXPONENT),
+                            0,
+                            false));
+                }
                 updateCounter.set(updateCounter.get() + 1);
             }
         });
@@ -456,7 +467,9 @@ public class PriceFeedService {
 
                     Map<String, MarketPrice> priceMap = result;
 
-                    cache.putAll(priceMap);
+                    synchronized (cache) {
+                        cache.putAll(priceMap);
+                    }
 
                     resultHandler.run();
                 });
diff --git a/core/src/main/java/haveno/core/support/dispute/DisputeManager.java b/core/src/main/java/haveno/core/support/dispute/DisputeManager.java
index 71155402..256ad8c4 100644
--- a/core/src/main/java/haveno/core/support/dispute/DisputeManager.java
+++ b/core/src/main/java/haveno/core/support/dispute/DisputeManager.java
@@ -1022,7 +1022,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
     // proposal). But if gain is larger than this loss he has economically an incentive to default in the trade.
     // We do all those calculations to give a hint to mediators to detect option trades.
     protected void addPriceInfoMessage(Dispute dispute, int counter) {
-        if (!priceFeedService.hasPrices()) {
+        if (!priceFeedService.hasExternalPrices()) {
             if (counter < 3) {
                 log.info("Price provider has still no data. This is expected at startup. We try again in 10 sec.");
                 UserThread.runAfter(() -> addPriceInfoMessage(dispute, counter + 1), 10);