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

Add check for dao state hash conflict with seed node #6504

3 changes: 2 additions & 1 deletion core/src/main/java/bisq/core/btc/wallet/WalletService.java
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,8 @@ public static void signTx(Wallet wallet,
continue;
}
if (!connectedOutput.isMine(wallet)) {
log.error("connectedOutput is not mine");
log.info("ConnectedOutput is not mine. This can be the case for BSQ transactions where the " +
"input gets signed by the other wallet. connectedOutput={}", connectedOutput);
continue;
}

Expand Down
17 changes: 5 additions & 12 deletions core/src/main/java/bisq/core/dao/DaoFacade.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.BaseTx;
import bisq.core.dao.state.model.blockchain.BaseTxOutput;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.model.blockchain.Tx;
import bisq.core.dao.state.model.blockchain.TxOutput;
Expand Down Expand Up @@ -531,10 +530,13 @@ public Optional<Block> getBlockAtHeight(int chainHeight) {
return daoStateService.getBlockAtHeight(chainHeight);
}

public boolean daoStateNeedsRebuilding() {
return daoStateMonitoringService.isInConflictWithSeedNode() || daoStateMonitoringService.isDaoStateBlockChainNotConnecting();
public boolean isDaoStateReadyAndInSync() {
return daoStateService.isParseBlockChainComplete() &&
!daoStateMonitoringService.isInConflictWithSeedNode() &&
!daoStateMonitoringService.isDaoStateBlockChainNotConnecting();
}


///////////////////////////////////////////////////////////////////////////////////////////
// Use case: Bonding
///////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -578,10 +580,6 @@ public long getTotalAmountOfConfiscatedTxOutputs() {
return daoStateService.getTotalAmountOfConfiscatedTxOutputs();
}

public long getTotalAmountOfInvalidatedBsq() {
return daoStateService.getTotalAmountOfInvalidatedBsq();
}

// Contains burned fee and invalidated bsq due invalid txs
public long getTotalAmountOfBurntBsq() {
return daoStateService.getTotalAmountOfBurntBsq();
Expand All @@ -595,11 +593,6 @@ public List<Tx> getIrregularTxs() {
return daoStateService.getIrregularTxs();
}

public long getTotalAmountOfUnspentTxOutputs() {
// Does not consider confiscated outputs (they stay as utxo)
return daoStateService.getUnspentTxOutputMap().values().stream().mapToLong(BaseTxOutput::getValue).sum();
}

public Optional<Integer> getLockTime(String txId) {
return daoStateService.getLockTime(txId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ public static boolean isHotfixActivated() {
// spike when opening arbitration.
private static final long DPT_MIN_TX_FEE_RATE = 10;


private final DaoStateService daoStateService;
private final BurningManService burningManService;
private int currentChainHeight;
Expand Down Expand Up @@ -127,12 +126,6 @@ public List<Tuple2<Long, String>> getReceivers(int burningManSelectionHeight,
burningManService.getActiveBurningManCandidates(burningManSelectionHeight) :
burningManService.getBurningManCandidatesByName(burningManSelectionHeight).values();


if (burningManCandidates.isEmpty()) {
// If there are no compensation requests (e.g. at dev testing) we fall back to the legacy BM
return List.of(new Tuple2<>(inputAmount, burningManService.getLegacyBurningManAddress(burningManSelectionHeight)));
}

// We need to use the same txFeePerVbyte value for both traders.
// We use the tradeTxFee value which is calculated from the average of taker fee tx size and deposit tx size.
// Otherwise, we would need to sync the fee rate of both traders.
Expand All @@ -146,12 +139,19 @@ public List<Tuple2<Long, String>> getReceivers(int burningManSelectionHeight,
// Smallest tx size is 246. With additional change output we add 32. To be safe we use the largest expected size.
double txSize = 278;
long txFeePerVbyte = Math.max(DPT_MIN_TX_FEE_RATE, Math.round(tradeTxFee / txSize));

if (burningManCandidates.isEmpty()) {
// If there are no compensation requests (e.g. at dev testing) we fall back to the legacy BM
long spendableAmount = getSpendableAmount(1, inputAmount, txFeePerVbyte);
return List.of(new Tuple2<>(spendableAmount, burningManService.getLegacyBurningManAddress(burningManSelectionHeight)));
}

long spendableAmount = getSpendableAmount(burningManCandidates.size(), inputAmount, txFeePerVbyte);
// We only use outputs > 1000 sat or at least 2 times the cost for the output (32 bytes).
// If we remove outputs it will be spent as miner fee.
long minOutputAmount = Math.max(DPT_MIN_OUTPUT_AMOUNT, txFeePerVbyte * 32 * 2);
// Sanity check that max share of a non-legacy BM is 20% over MAX_BURN_SHARE (taking into account potential increase due adjustment)
long maxOutputAmount = Math.round(inputAmount * (BurningManService.MAX_BURN_SHARE * 1.2));
long maxOutputAmount = Math.round(spendableAmount * (BurningManService.MAX_BURN_SHARE * 1.2));
// We accumulate small amounts which gets filtered out and subtract it from 1 to get an adjustment factor
// used later to be applied to the remaining burningmen share.
double adjustment = 1 - burningManCandidates.stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public void onFault(String errorMessage, @Nullable Connection connection) {
}

private void handleRepublishGovernanceDataRequest() {
log.warn("We received a RepublishGovernanceDataRequest and re-published all proposalPayloads and " +
log.info("We received a RepublishGovernanceDataRequest and re-published all proposalPayloads and " +
"blindVotePayloads to the P2P network.");
missingDataRequestService.reRepublishAllGovernanceData();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import bisq.core.trade.protocol.bisq_v1.messages.InputsForDepositTxRequest;
import bisq.core.trade.protocol.bisq_v1.messages.PayoutTxPublishedMessage;
import bisq.core.trade.protocol.bisq_v1.tasks.ApplyFilter;
import bisq.core.trade.protocol.bisq_v1.tasks.CheckIfDaoStateIsInSync;
import bisq.core.trade.protocol.bisq_v1.tasks.TradeTask;
import bisq.core.trade.protocol.bisq_v1.tasks.buyer.BuyerFinalizesDelayedPayoutTx;
import bisq.core.trade.protocol.bisq_v1.tasks.buyer.BuyerProcessDelayedPayoutTxSignatureRequest;
Expand Down Expand Up @@ -71,6 +72,7 @@ public void handleTakeOfferRequest(InputsForDepositTxRequest message,
.with(message)
.from(peer))
.setup(tasks(
CheckIfDaoStateIsInSync.class,
MakerProcessesInputsForDepositTxRequest.class,
ApplyFilter.class,
getVerifyPeersFeePaymentClass(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import bisq.core.trade.protocol.bisq_v1.messages.InputsForDepositTxResponse;
import bisq.core.trade.protocol.bisq_v1.messages.PayoutTxPublishedMessage;
import bisq.core.trade.protocol.bisq_v1.tasks.ApplyFilter;
import bisq.core.trade.protocol.bisq_v1.tasks.CheckIfDaoStateIsInSync;
import bisq.core.trade.protocol.bisq_v1.tasks.TradeTask;
import bisq.core.trade.protocol.bisq_v1.tasks.buyer.BuyerFinalizesDelayedPayoutTx;
import bisq.core.trade.protocol.bisq_v1.tasks.buyer.BuyerProcessDelayedPayoutTxSignatureRequest;
Expand Down Expand Up @@ -77,6 +78,7 @@ public void onTakeOffer() {
expect(phase(Trade.Phase.INIT)
.with(TakerEvent.TAKE_OFFER))
.setup(tasks(
CheckIfDaoStateIsInSync.class,
ApplyFilter.class,
getVerifyPeersFeePaymentClass(),
CreateTakerFeeTx.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import bisq.core.trade.protocol.bisq_v1.messages.DepositTxMessage;
import bisq.core.trade.protocol.bisq_v1.messages.InputsForDepositTxRequest;
import bisq.core.trade.protocol.bisq_v1.tasks.ApplyFilter;
import bisq.core.trade.protocol.bisq_v1.tasks.CheckIfDaoStateIsInSync;
import bisq.core.trade.protocol.bisq_v1.tasks.TradeTask;
import bisq.core.trade.protocol.bisq_v1.tasks.maker.MakerCreateAndSignContract;
import bisq.core.trade.protocol.bisq_v1.tasks.maker.MakerProcessesInputsForDepositTxRequest;
Expand Down Expand Up @@ -73,6 +74,7 @@ public void handleTakeOfferRequest(InputsForDepositTxRequest message,
.with(message)
.from(peer))
.setup(tasks(
CheckIfDaoStateIsInSync.class,
MaybeCreateSubAccount.class,
MakerProcessesInputsForDepositTxRequest.class,
ApplyFilter.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import bisq.core.trade.protocol.bisq_v1.messages.DelayedPayoutTxSignatureResponse;
import bisq.core.trade.protocol.bisq_v1.messages.InputsForDepositTxResponse;
import bisq.core.trade.protocol.bisq_v1.tasks.ApplyFilter;
import bisq.core.trade.protocol.bisq_v1.tasks.CheckIfDaoStateIsInSync;
import bisq.core.trade.protocol.bisq_v1.tasks.TradeTask;
import bisq.core.trade.protocol.bisq_v1.tasks.seller.MaybeCreateSubAccount;
import bisq.core.trade.protocol.bisq_v1.tasks.seller.SellerCreatesDelayedPayoutTx;
Expand Down Expand Up @@ -73,6 +74,7 @@ public void onTakeOffer() {
.with(TakerEvent.TAKE_OFFER)
.from(trade.getTradingPeerNodeAddress()))
.setup(tasks(
CheckIfDaoStateIsInSync.class,
MaybeCreateSubAccount.class,
ApplyFilter.class,
getVerifyPeersFeePaymentClass(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.core.trade.protocol.bisq_v1.tasks;

import bisq.core.trade.model.bisq_v1.Trade;

import bisq.common.taskrunner.TaskRunner;

import lombok.extern.slf4j.Slf4j;

import static com.google.common.base.Preconditions.checkArgument;

@Slf4j
public class CheckIfDaoStateIsInSync extends TradeTask {
public CheckIfDaoStateIsInSync(TaskRunner<Trade> taskHandler, Trade trade) {
super(taskHandler, trade);
}

@Override
protected void run() {
try {
runInterceptHook();

checkArgument(processModel.getDaoFacade().isDaoStateReadyAndInSync(), "DAO state is not in sync with seed nodes");
complete();
} catch (Throwable t) {
failed(t);
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@

import lombok.extern.slf4j.Slf4j;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

@Slf4j
Expand Down Expand Up @@ -74,8 +73,19 @@ protected void run() {
depositTx,
delayedPayoutTxReceivers,
lockTime);
checkArgument(buyersDelayedPayoutTx.getTxId().equals(finalDelayedPayoutTx.getTxId()),
"TxIds of buyersDelayedPayoutTx and finalDelayedPayoutTx must be the same");

if (!buyersDelayedPayoutTx.getTxId().equals(finalDelayedPayoutTx.getTxId())) {
String errorMsg = "TxIds of buyersDelayedPayoutTx and finalDelayedPayoutTx must be the same.";
log.error("{} \nbuyersDelayedPayoutTx={}, \nfinalDelayedPayoutTx={}, " +
"\nBtcWalletService.chainHeight={}, " +
"\nDaoState.chainHeight={}, " +
"\nisDaoStateIsInSync={}",
errorMsg, buyersDelayedPayoutTx, finalDelayedPayoutTx,
processModel.getBtcWalletService().getBestChainHeight(),
processModel.getDaoFacade().getChainHeight(),
processModel.getDaoFacade().isDaoStateReadyAndInSync());
throw new IllegalArgumentException(errorMsg);
}
}

complete();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@

import lombok.extern.slf4j.Slf4j;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

@Slf4j
Expand Down Expand Up @@ -66,8 +65,18 @@ protected void run() {
preparedDepositTx,
delayedPayoutTxReceivers,
lockTime);
checkArgument(buyersPreparedDelayedPayoutTx.getTxId().equals(sellersPreparedDelayedPayoutTx.getTxId()),
"TxIds of buyersPreparedDelayedPayoutTx and sellersPreparedDelayedPayoutTx must be the same");
if (!buyersPreparedDelayedPayoutTx.getTxId().equals(sellersPreparedDelayedPayoutTx.getTxId())) {
String errorMsg = "TxIds of buyersPreparedDelayedPayoutTx and sellersPreparedDelayedPayoutTx must be the same.";
log.error("{} \nbuyersPreparedDelayedPayoutTx={}, \nsellersPreparedDelayedPayoutTx={}, " +
"\nBtcWalletService.chainHeight={}, " +
"\nDaoState.chainHeight={}, " +
"\nisDaoStateIsInSync={}",
errorMsg, buyersPreparedDelayedPayoutTx, sellersPreparedDelayedPayoutTx,
processModel.getBtcWalletService().getBestChainHeight(),
processModel.getDaoFacade().getChainHeight(),
processModel.getDaoFacade().isDaoStateReadyAndInSync());
throw new IllegalArgumentException(errorMsg);
}
}

// If the deposit tx is non-malleable, we already know its final ID, so should check that now
Expand Down
8 changes: 4 additions & 4 deletions desktop/src/main/java/bisq/desktop/main/MainViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ public void onSetupComplete() {
setupClockWatcherPopup();

marketPricePresentation.setup();
daoPresentation.setup();
daoPresentation.init();
accountPresentation.setup();
settingsPresentation.setup();

Expand Down Expand Up @@ -505,7 +505,7 @@ private void setupHandlers() {
.show());

bisqSetup.getBtcSyncProgress().addListener((observable, oldValue, newValue) -> updateBtcSyncProgress());
daoPresentation.getBsqSyncProgress().addListener((observable, oldValue, newValue) -> updateBtcSyncProgress());
daoPresentation.getDaoStateSyncProgress().addListener((observable, oldValue, newValue) -> updateBtcSyncProgress());

bisqSetup.setFilterWarningHandler(warning -> new Popup().warning(warning).show());

Expand Down Expand Up @@ -704,7 +704,7 @@ private void updateBtcSyncProgress() {
if (btcSyncProgress.doubleValue() < 1) {
combinedSyncProgress.set(btcSyncProgress.doubleValue());
} else {
combinedSyncProgress.set(daoPresentation.getBsqSyncProgress().doubleValue());
combinedSyncProgress.set(daoPresentation.getDaoStateSyncProgress().doubleValue());
}
}

Expand Down Expand Up @@ -783,7 +783,7 @@ StringProperty getBtcInfo() {

StringProperty getCombinedFooterInfo() {
final StringProperty combinedInfo = new SimpleStringProperty();
combinedInfo.bind(Bindings.concat(this.footerVersionInfo, " ", daoPresentation.getBsqInfo()));
combinedInfo.bind(Bindings.concat(this.footerVersionInfo, " ", daoPresentation.getDaoStateInfo()));
return combinedInfo;
}

Expand Down
Loading