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

Don't allow trade start if BitcoinJ is not fully synced #4764

Merged
merged 1 commit into from Nov 11, 2020
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
11 changes: 11 additions & 0 deletions core/src/main/java/bisq/core/btc/setup/WalletsSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ protected void onSetupCompleted() {
return message;
});

chainHeight.set(chain.getBestChainHeight());
chain.addNewBestBlockListener(block -> {
connectedPeers.set(peerGroup.getConnectedPeers());
chainHeight.set(block.getHeight());
Expand Down Expand Up @@ -522,6 +523,16 @@ public boolean isDownloadComplete() {
return downloadPercentageProperty().get() == 1d;
}

public boolean isChainHeightSyncedWithinTolerance() {
int peersChainHeight = PeerGroup.getMostCommonChainHeight(connectedPeers.get());
int bestChainHeight = walletConfig.chain().getBestChainHeight();
if (peersChainHeight - bestChainHeight <= 3) {
return true;
}
log.warn("Our chain height: {} is out of sync with peer nodes chain height: {}", chainHeight.get(), peersChainHeight);
return false;
}

public Set<Address> getAddressesByContext(@SuppressWarnings("SameParameterValue") AddressEntry.Context context) {
return addressEntryList.getAddressEntriesAsListImmutable().stream()
.filter(addressEntry -> addressEntry.getContext() == context)
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/bisq/core/btc/wallet/WalletService.java
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,10 @@ public int getBestChainHeight() {
return isWalletReady() && chain != null ? chain.getBestChainHeight() : 0;
}

public boolean isChainHeightSyncedWithinTolerance() {
return walletsSetup.isChainHeightSyncedWithinTolerance();
}

public Transaction getClonedTransaction(Transaction tx) {
return new Transaction(params, tx.bitcoinSerialize());
}
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/java/bisq/core/offer/OpenOfferManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,14 @@ private void handleOfferAvailabilityRequest(OfferAvailabilityRequest request, No
return;
}

// Don't allow trade start if BitcoinJ is not fully synced (bisq issue #4764)
if (!btcWalletService.isChainHeightSyncedWithinTolerance()) {
errorMessage = "We got a handleOfferAvailabilityRequest but our chain is not synced.";
log.info(errorMessage);
sendAckMessage(request, peer, false, errorMessage);
return;
}

if (stopped) {
errorMessage = "We have stopped already. We ignore that handleOfferAvailabilityRequest call.";
log.debug(errorMessage);
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/resources/i18n/displayStrings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1275,6 +1275,7 @@ settings.net.creationDateColumn=Established
settings.net.connectionTypeColumn=In/Out
settings.net.sentDataLabel=Sent data statistics
settings.net.receivedDataLabel=Received data statistics
settings.net.chainHeightLabel=Latest BTC block height
settings.net.roundTripTimeColumn=Roundtrip
settings.net.sentBytesColumn=Sent
settings.net.receivedBytesColumn=Received
Expand All @@ -1289,6 +1290,7 @@ settings.net.needRestart=You need to restart the application to apply that chang
settings.net.notKnownYet=Not known yet...
settings.net.sentData=Sent data: {0}, {1} messages, {2} messages/sec
settings.net.receivedData=Received data: {0}, {1} messages, {2} messages/sec
settings.net.chainHeight=Bisq: {0} | Peers: {1}
settings.net.ips=[IP address:port | host name:port | onion address:port] (comma separated). Port can be omitted if default is used (8333).
settings.net.seedNode=Seed node
settings.net.directPeer=Peer (direct)
Expand Down Expand Up @@ -2781,6 +2783,8 @@ popup.warning.noMediatorsAvailable=There are no mediators available.
popup.warning.notFullyConnected=You need to wait until you are fully connected to the network.\nThat might take up to about 2 minutes at startup.
popup.warning.notSufficientConnectionsToBtcNetwork=You need to wait until you have at least {0} connections to the Bitcoin network.
popup.warning.downloadNotComplete=You need to wait until the download of missing Bitcoin blocks is complete.
popup.warning.chainNotSynced=The Bisq wallet blockchain height is not synced correctly. If you recently started the application, please wait until one Bitcoin block has been published.\n\n\
You can check the blockchain height in Settings/Network Info. If more than one block passes and this problem persists it may be stalled, in which case you should do an SPV resync. [HYPERLINK:https://bisq.wiki/Resyncing_SPV_file]
popup.warning.removeOffer=Are you sure you want to remove that offer?\nThe maker fee of {0} will be lost if you remove that offer.
popup.warning.tooLargePercentageValue=You cannot set a percentage of 100% or larger.
popup.warning.examplePercentageValue=Please enter a percentage number like \"5.4\" for 5.4%
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import bisq.desktop.util.GUIUtil;

import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.filter.FilterManager;
import bisq.core.locale.BankUtil;
import bisq.core.locale.CountryUtil;
Expand Down Expand Up @@ -99,6 +100,7 @@ class OfferBookViewModel extends ActivatableViewModel {
private final User user;
private final OfferBook offerBook;
final Preferences preferences;
private final WalletsSetup walletsSetup;
private final P2PService p2PService;
final PriceFeedService priceFeedService;
private final ClosedTradableManager closedTradableManager;
Expand Down Expand Up @@ -142,6 +144,7 @@ public OfferBookViewModel(User user,
OpenOfferManager openOfferManager,
OfferBook offerBook,
Preferences preferences,
WalletsSetup walletsSetup,
P2PService p2PService,
PriceFeedService priceFeedService,
ClosedTradableManager closedTradableManager,
Expand All @@ -156,6 +159,7 @@ public OfferBookViewModel(User user,
this.user = user;
this.offerBook = offerBook;
this.preferences = preferences;
this.walletsSetup = walletsSetup;
this.p2PService = p2PService;
this.priceFeedService = priceFeedService;
this.closedTradableManager = closedTradableManager;
Expand Down Expand Up @@ -532,6 +536,7 @@ boolean hasPaymentAccountForCurrency() {

boolean canCreateOrTakeOffer() {
return GUIUtil.canCreateOrTakeOfferOrShowPopup(user, navigation) &&
GUIUtil.isChainHeightSyncedWithinToleranceOrShowPopup(walletsSetup) &&
GUIUtil.isBootstrappedOrShowPopup(p2PService);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
<AutoTooltipButton fx:id="reSyncSPVChainButton"/>
</VBox>

<TitledGroupBg fx:id="p2pHeader" GridPane.rowIndex="5" GridPane.rowSpan="4">
<TitledGroupBg fx:id="p2pHeader" GridPane.rowIndex="5" GridPane.rowSpan="5">
<padding>
<Insets top="50.0"/>
</padding>
Expand Down Expand Up @@ -159,7 +159,10 @@
<BisqTextField fx:id="receivedDataTextField" GridPane.rowIndex="8" editable="false"
focusTraversable="false" labelFloat="true"/>

<AutoTooltipButton fx:id="openTorSettingsButton" GridPane.rowIndex="9" GridPane.columnIndex="0"/>
<BisqTextField fx:id="chainHeightTextField" GridPane.rowIndex="9" editable="false"
focusTraversable="false" labelFloat="true"/>

<AutoTooltipButton fx:id="openTorSettingsButton" GridPane.rowIndex="10" GridPane.columnIndex="0"/>

<columnConstraints>
<ColumnConstraints hgrow="ALWAYS" minWidth="500"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
import bisq.common.ClockWatcher;
import bisq.common.UserThread;

import org.bitcoinj.core.PeerGroup;

import javax.inject.Inject;

import javafx.fxml.FXML;
Expand Down Expand Up @@ -86,7 +88,7 @@ public class NetworkSettingsView extends ActivatableView<GridPane, Void> {
@FXML
InputTextField btcNodesInputTextField;
@FXML
TextField onionAddress, sentDataTextField, receivedDataTextField;
TextField onionAddress, sentDataTextField, receivedDataTextField, chainHeightTextField;
@FXML
Label p2PPeersLabel, bitcoinPeersLabel;
@FXML
Expand Down Expand Up @@ -180,6 +182,7 @@ public void initialize() {
connectionTypeColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.connectionTypeColumn")));
sentDataTextField.setPromptText(Res.get("settings.net.sentDataLabel"));
receivedDataTextField.setPromptText(Res.get("settings.net.receivedDataLabel"));
chainHeightTextField.setPromptText(Res.get("settings.net.chainHeightLabel"));
roundTripTimeColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.roundTripTimeColumn")));
sentBytesColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.sentBytesColumn")));
receivedBytesColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.receivedBytesColumn")));
Expand Down Expand Up @@ -487,6 +490,9 @@ private void updateBitcoinPeersTable() {
bitcoinNetworkListItems.setAll(walletsSetup.getPeerGroup().getConnectedPeers().stream()
.map(BitcoinNetworkListItem::new)
.collect(Collectors.toList()));
chainHeightTextField.textProperty().setValue(Res.get("settings.net.chainHeight",
walletsSetup.chainHeightProperty().get(),
PeerGroup.getMostCommonChainHeight(walletsSetup.connectedPeersProperty().get())));
}
}

9 changes: 9 additions & 0 deletions desktop/src/main/java/bisq/desktop/util/GUIUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,15 @@ public static boolean isReadyForTxBroadcastOrShowPopup(P2PService p2PService, Wa
return true;
}

public static boolean isChainHeightSyncedWithinToleranceOrShowPopup(WalletsSetup walletsSetup) {
if (!walletsSetup.isChainHeightSyncedWithinTolerance()) {
new Popup().information(Res.get("popup.warning.chainNotSynced")).show();
return false;
}

return true;
}

public static boolean canCreateOrTakeOfferOrShowPopup(User user, Navigation navigation) {
if (!user.hasAcceptedRefundAgents()) {
new Popup().warning(Res.get("popup.warning.noArbitratorsAvailable")).show();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ public void testMaxCharactersForAmountWithNoOffes() {

when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);

final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null,
final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null,
null, null, null, null, coinFormatter, new BsqFormatter());
assertEquals(0, model.maxPlacesForAmount.intValue());
}
Expand All @@ -243,7 +243,7 @@ public void testMaxCharactersForAmount() {

when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);

final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null,
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
null, null, null, null, coinFormatter, new BsqFormatter());
model.activate();

Expand All @@ -261,7 +261,7 @@ public void testMaxCharactersForAmountRange() {

when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);

final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null,
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
null, null, null, null, coinFormatter, new BsqFormatter());
model.activate();

Expand All @@ -280,7 +280,7 @@ public void testMaxCharactersForVolumeWithNoOffes() {

when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);

final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null,
final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null,
null, null, null, null, coinFormatter, new BsqFormatter());
assertEquals(0, model.maxPlacesForVolume.intValue());
}
Expand All @@ -294,7 +294,7 @@ public void testMaxCharactersForVolume() {

when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);

final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null,
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
null, null, null, null, coinFormatter, new BsqFormatter());
model.activate();

Expand All @@ -312,7 +312,7 @@ public void testMaxCharactersForVolumeRange() {

when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);

final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null,
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
null, null, null, null, coinFormatter, new BsqFormatter());
model.activate();

Expand All @@ -331,7 +331,7 @@ public void testMaxCharactersForPriceWithNoOffers() {

when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);

final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null,
final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null,
null, null, null, null, coinFormatter, new BsqFormatter());
assertEquals(0, model.maxPlacesForPrice.intValue());
}
Expand All @@ -345,7 +345,7 @@ public void testMaxCharactersForPrice() {

when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);

final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null,
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
null, null, null, null, coinFormatter, new BsqFormatter());
model.activate();

Expand All @@ -363,7 +363,7 @@ public void testMaxCharactersForPriceDistanceWithNoOffers() {

when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);

final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null,
final OfferBookViewModel model = new OfferBookViewModel(null, null, offerBook, empty, null, null, null,
null, null, null, null, coinFormatter, new BsqFormatter());
assertEquals(0, model.maxPlacesForMarketPriceMargin.intValue());
}
Expand Down Expand Up @@ -391,7 +391,7 @@ public void testMaxCharactersForPriceDistance() {
item4.getOffer().setPriceFeedService(priceFeedService);
offerBookListItems.addAll(item1, item2);

final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, priceFeedService,
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, priceFeedService,
null, null, null, null, coinFormatter, new BsqFormatter());
model.activate();

Expand All @@ -412,7 +412,7 @@ public void testGetPrice() {
when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems);
when(priceFeedService.getMarketPrice(anyString())).thenReturn(new MarketPrice("USD", 12684.0450, Instant.now().getEpochSecond(), true));

final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null,
final OfferBookViewModel model = new OfferBookViewModel(null, openOfferManager, offerBook, empty, null, null, null,
null, null, null, null, coinFormatter, new BsqFormatter());

final OfferBookListItem item = make(btcBuyItem.but(
Expand Down