From c20964f47a44a1ca8cd2199dd3551035d7316546 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 26 Mar 2019 00:33:57 -0500 Subject: [PATCH 01/55] Don't create vote reveal tx in last block of phase --- .../core/dao/governance/votereveal/VoteRevealService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java index e3433fa4043..01effa8df12 100644 --- a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java +++ b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java @@ -179,8 +179,11 @@ private void maybeRevealVotes(int chainHeight) { .filter(myVote -> myVote.getRevealTxId() == null) // we have not already revealed .forEach(myVote -> { boolean isInVoteRevealPhase = periodService.getPhaseForHeight(chainHeight) == DaoPhase.Phase.VOTE_REVEAL; + // If we would create the tx in the last block it would be confirmed in the best case in th next + // block which would be already the break and would invalidate the vote reveal. + boolean isNotLastBlockInPhase = chainHeight != periodService.getLastBlockOfPhase(chainHeight, DaoPhase.Phase.VOTE_REVEAL); boolean isBlindVoteTxInCorrectPhaseAndCycle = periodService.isTxInPhaseAndCycle(myVote.getTxId(), DaoPhase.Phase.BLIND_VOTE, chainHeight); - if (isInVoteRevealPhase && isBlindVoteTxInCorrectPhaseAndCycle) { + if (isInVoteRevealPhase && isNotLastBlockInPhase && isBlindVoteTxInCorrectPhaseAndCycle) { log.info("We call revealVote at blockHeight {} for blindVoteTxId {}", chainHeight, myVote.getTxId()); // Standard case that we are in the correct phase and cycle and create the reveal tx. revealVote(myVote, true); From ce1da644c217db63fef6a4dc2430f7006254a34f Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 26 Mar 2019 00:37:55 -0500 Subject: [PATCH 02/55] Add IRREGULAR txType for txs which are not rule conform but not burnt We don't want to burn BSQ in cases like that the tx was published too late, which is a valid case if the tx does not make it in the next block. We set such txs as IRREGULAR and allow spending of the BSQ, but there function in the governance is invalidated. We also add a check if the sum of all UTXO is the same as the sum of the genesis + sum of issuance txs - burned fees. --- common/src/main/proto/pb.proto | 1 + .../main/java/bisq/core/dao/DaoFacade.java | 6 +++ .../voteresult/VoteResultConsensus.java | 11 ++-- .../monitoring/DaoStateMonitoringService.java | 18 +++++++ .../core/dao/node/explorer/JsonTxType.java | 3 +- .../bisq/core/dao/node/parser/TxParser.java | 52 +++++++++++-------- .../dao/state/model/blockchain/TxType.java | 3 +- ...UnconfirmedBsqChangeOutputListService.java | 2 + .../resources/i18n/displayStrings.properties | 3 ++ .../wallet/dashboard/BsqDashboardView.java | 13 +++-- .../desktop/main/dao/wallet/tx/BsqTxView.java | 35 ++++++++++++- 11 files changed, 114 insertions(+), 33 deletions(-) diff --git a/common/src/main/proto/pb.proto b/common/src/main/proto/pb.proto index f2f9b57a528..2b9cf09813c 100644 --- a/common/src/main/proto/pb.proto +++ b/common/src/main/proto/pb.proto @@ -1440,6 +1440,7 @@ enum TxType { UNLOCK = 13; ASSET_LISTING_FEE = 14; PROOF_OF_BURN = 15; + IRREGULAR = 16; } message TxInput { diff --git a/core/src/main/java/bisq/core/dao/DaoFacade.java b/core/src/main/java/bisq/core/dao/DaoFacade.java index e3413b6ea9e..a5681e47958 100644 --- a/core/src/main/java/bisq/core/dao/DaoFacade.java +++ b/core/src/main/java/bisq/core/dao/DaoFacade.java @@ -56,6 +56,7 @@ import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.DaoStateStorageService; 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; @@ -543,6 +544,11 @@ public long getTotalAmountOfConfiscatedTxOutputs() { return daoStateService.getTotalAmountOfConfiscatedTxOutputs(); } + public long getTotalAmountOfUnspentTxOutputs() { + // Does not consider confiscated outputs (they stay as utxo) + return daoStateService.getUnspentTxOutputMap().values().stream().mapToLong(BaseTxOutput::getValue).sum(); + } + public Optional getLockTime(String txId) { return daoStateService.getLockTime(txId); } diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultConsensus.java b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultConsensus.java index 15c50a1b096..1f0301db8b8 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultConsensus.java +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultConsensus.java @@ -109,10 +109,15 @@ public static TxOutput getConnectedBlindVoteStakeOutput(Tx voteRevealTx, DaoStat Optional optionalBlindVoteStakeOutput = daoStateService.getConnectedTxOutput(stakeTxInput); checkArgument(optionalBlindVoteStakeOutput.isPresent(), "blindVoteStakeOutput must be present"); TxOutput blindVoteStakeOutput = optionalBlindVoteStakeOutput.get(); - checkArgument(blindVoteStakeOutput.getTxOutputType() == TxOutputType.BLIND_VOTE_LOCK_STAKE_OUTPUT, - "blindVoteStakeOutput must be of type BLIND_VOTE_LOCK_STAKE_OUTPUT but is " + - blindVoteStakeOutput.getTxOutputType() + ". VoteRevealTx=" + voteRevealTx); + if (blindVoteStakeOutput.getTxOutputType() != TxOutputType.BLIND_VOTE_LOCK_STAKE_OUTPUT) { + String message = "blindVoteStakeOutput must be of type BLIND_VOTE_LOCK_STAKE_OUTPUT but is " + + blindVoteStakeOutput.getTxOutputType(); + log.warn(message + ". VoteRevealTx=" + voteRevealTx); + throw new VoteResultException.ValidationException(message + ". VoteRevealTxId=" + voteRevealTx.getId()); + } return blindVoteStakeOutput; + } catch (VoteResultException.ValidationException t) { + throw t; } catch (Throwable t) { throw new VoteResultException.ValidationException(t); } diff --git a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java index abf8240c881..f99d7ea3551 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java +++ b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java @@ -26,7 +26,9 @@ import bisq.core.dao.state.DaoStateListener; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.GenesisTxInfo; +import bisq.core.dao.state.model.blockchain.BaseTxOutput; import bisq.core.dao.state.model.blockchain.Block; +import bisq.core.dao.state.model.governance.IssuanceType; import bisq.network.p2p.NodeAddress; import bisq.network.p2p.network.Connection; @@ -134,6 +136,22 @@ public void onParseBlockChainComplete() { daoStateNetworkService.requestHashesFromAllConnectedSeedNodes(fromHeight); } + @Override + public void onDaoStateChanged(Block block) { + long genesisTotalSupply = daoStateService.getGenesisTotalSupply().value; + long totalBurntFee = daoStateService.getTotalBurntFee(); + long compensationIssuance = daoStateService.getTotalIssuedAmount(IssuanceType.COMPENSATION); + long reimbursementIssuance = daoStateService.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT); + long totalConfiscatedAmount = daoStateService.getTotalAmountOfConfiscatedTxOutputs(); + // confiscated funds are still in the utxo set + long sumUtxo = daoStateService.getUnspentTxOutputMap().values().stream().mapToLong(BaseTxOutput::getValue).sum(); + long sumBsq = genesisTotalSupply + compensationIssuance + reimbursementIssuance - totalBurntFee; + + if (sumBsq != sumUtxo) { + throw new RuntimeException("There is a mismatch between the UTXO set and the DAO state. Please contact the Bisq devlopers."); + } + } + /////////////////////////////////////////////////////////////////////////////////////////// // StateNetworkService.Listener /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxType.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonTxType.java index f0e972c6ea8..d14e17f8a56 100644 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxType.java +++ b/core/src/main/java/bisq/core/dao/node/explorer/JsonTxType.java @@ -36,7 +36,8 @@ enum JsonTxType { LOCKUP("Lockup"), UNLOCK("Unlock"), ASSET_LISTING_FEE("Asset listing fee"), - PROOF_OF_BURN("Proof of burn"); + PROOF_OF_BURN("Proof of burn"), + IRREGULAR("Irregular"); @Getter private String displayString; diff --git a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java index 5decbbbe4e4..e6d3bc69b44 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java +++ b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java @@ -150,9 +150,13 @@ private Optional findTx(RawTx rawTx) { //**************************************************************************************** applyTxTypeAndTxOutputType(blockHeight, tempTx, remainingInputValue); - - TxType txType = evaluateTxType(tempTx, optionalOpReturnType, hasBurntBsq, unLockInputValid); - tempTx.setTxType(txType); + TxType txType; + if (tempTx.getTxType() != TxType.IRREGULAR && tempTx.getTxType() != TxType.INVALID) { + txType = evaluateTxType(tempTx, optionalOpReturnType, hasBurntBsq, unLockInputValid); + tempTx.setTxType(txType); + } else { + txType = tempTx.getTxType(); + } if (isTxInvalid(tempTx, bsqOutputFound, hasBurntBond)) { tempTx.setTxType(TxType.INVALID); @@ -162,6 +166,9 @@ private Optional findTx(RawTx rawTx) { log.warn("We have destroyed BSQ because of an invalid tx. Burned BSQ={}. tx={}", burntBsq / 100D, tempTx); } + } else if (txType == TxType.IRREGULAR) { + log.warn("We have an irregular tx {}", tempTx); + txOutputParser.commitUTXOCandidates(); } else { txOutputParser.commitUTXOCandidates(); } @@ -231,8 +238,8 @@ private void applyTxTypeAndTxOutputType(int blockHeight, TempTx tempTx, long bsq private void processProposal(int blockHeight, TempTx tempTx, long bsqFee) { boolean isFeeAndPhaseValid = isFeeAndPhaseValid(tempTx.getId(), blockHeight, bsqFee, DaoPhase.Phase.PROPOSAL, Param.PROPOSAL_FEE); if (!isFeeAndPhaseValid) { - // TODO don't burn in such cases - tempTx.setTxType(TxType.INVALID); + // We tolerate such an incorrect tx and do not burn the BSQ + tempTx.setTxType(TxType.IRREGULAR); } } @@ -249,17 +256,17 @@ private void processIssuance(int blockHeight, TempTx tempTx, long bsqFee) { "As the BSQ fee is set it must be either a buggy tx or an manually crafted invalid tx."); // Even though the request part if invalid the BSQ transfer and change output should still be valid // as long as the BSQ change <= BSQ inputs. - // TODO do we want to burn in such a case? - tempTx.setTxType(TxType.INVALID); + // We tolerate such an incorrect tx and do not burn the BSQ + tempTx.setTxType(TxType.IRREGULAR); } } else { // This could be a valid compensation request that failed to be included in a block during the // correct phase due to no fault of the user. Better not burn the change as long as the BSQ inputs // cover the value of the outputs. - // TODO don't burn in such cases - tempTx.setTxType(TxType.INVALID); + // We tolerate such an incorrect tx and do not burn the BSQ + tempTx.setTxType(TxType.IRREGULAR); - // TODO don't burn in such cases + // Make sure the optionalIssuanceCandidate is set the BTC optionalIssuanceCandidate.ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT)); // Empty Optional case is a possible valid case where a random tx matches our opReturn rules but it is not a // valid BSQ tx. @@ -269,11 +276,11 @@ private void processIssuance(int blockHeight, TempTx tempTx, long bsqFee) { private void processBlindVote(int blockHeight, TempTx tempTx, long bsqFee) { boolean isFeeAndPhaseValid = isFeeAndPhaseValid(tempTx.getId(), blockHeight, bsqFee, DaoPhase.Phase.BLIND_VOTE, Param.BLIND_VOTE_FEE); if (!isFeeAndPhaseValid) { - // TODO don't burn in such cases - tempTx.setTxType(TxType.INVALID); + // We tolerate such an incorrect tx and do not burn the BSQ + tempTx.setTxType(TxType.IRREGULAR); - // TODO don't burn in such cases - txOutputParser.getOptionalBlindVoteLockStakeOutput().ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT)); + // Set the stake output from BLIND_VOTE_LOCK_STAKE_OUTPUT to BSQ + txOutputParser.getOptionalBlindVoteLockStakeOutput().ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BSQ_OUTPUT)); // Empty Optional case is a possible valid case where a random tx matches our opReturn rules but it is not a // valid BSQ tx. } @@ -374,7 +381,6 @@ static TxType evaluateTxType(TempTx tempTx, Optional optionalOpRet if (!isUnLockInputValid) return TxType.INVALID; - // UNLOCK tx has no fee, no OpReturn return TxType.UNLOCK; } @@ -393,9 +399,9 @@ static TxType evaluateTxTypeFromOpReturnType(TempTx tempTx, OpReturnType opRetur boolean hasCorrectNumOutputs = tempTx.getTempTxOutputs().size() >= 3; if (!hasCorrectNumOutputs) { log.warn("Compensation/reimbursement request tx need to have at least 3 outputs"); - // This is not an issuance request but it should still not burn the BSQ change - // TODO do we want to burn in such a case? - return TxType.INVALID; + // This is not a valid issuance tx + // We tolerate such an incorrect tx and do not burn the BSQ + return TxType.IRREGULAR; } TempTxOutput issuanceTxOutput = tempTx.getTempTxOutputs().get(1); @@ -403,9 +409,9 @@ static TxType evaluateTxTypeFromOpReturnType(TempTx tempTx, OpReturnType opRetur if (!hasIssuanceOutput) { log.warn("Compensation/reimbursement request txOutput type of output at index 1 need to be ISSUANCE_CANDIDATE_OUTPUT. " + "TxOutputType={}", issuanceTxOutput.getTxOutputType()); - // This is not an issuance request but it should still not burn the BSQ change - // TODO do we want to burn in such a case? - return TxType.INVALID; + // This is not a valid issuance tx + // We tolerate such an incorrect tx and do not burn the BSQ + return TxType.IRREGULAR; } return opReturnType == OpReturnType.COMPENSATION_REQUEST ? @@ -423,8 +429,8 @@ static TxType evaluateTxTypeFromOpReturnType(TempTx tempTx, OpReturnType opRetur return TxType.PROOF_OF_BURN; default: log.warn("We got a BSQ tx with an unknown OP_RETURN. tx={}, opReturnType={}", tempTx, opReturnType); - // TODO do we want to burn in such a case? - return TxType.INVALID; + // We tolerate such an incorrect tx and do not burn the BSQ + return TxType.IRREGULAR; } } } diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java index 8e5339db7fc..390362e7788 100644 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java +++ b/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java @@ -45,7 +45,8 @@ public enum TxType implements ImmutableDaoStateModel { LOCKUP(true, false), UNLOCK(true, false), ASSET_LISTING_FEE(true, true), - PROOF_OF_BURN(true, true); + PROOF_OF_BURN(true, true), + IRREGULAR(true, true); // the params are here irrelevant as we can have any tx which violated the rules set to irregular /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/bisq/core/dao/state/unconfirmed/UnconfirmedBsqChangeOutputListService.java b/core/src/main/java/bisq/core/dao/state/unconfirmed/UnconfirmedBsqChangeOutputListService.java index e213b60586a..03d6d8961b6 100644 --- a/core/src/main/java/bisq/core/dao/state/unconfirmed/UnconfirmedBsqChangeOutputListService.java +++ b/core/src/main/java/bisq/core/dao/state/unconfirmed/UnconfirmedBsqChangeOutputListService.java @@ -115,6 +115,8 @@ public void onCommitTx(Transaction tx, TxType txType, Wallet wallet) { case PROOF_OF_BURN: changeOutputIndex = 0; break; + case IRREGULAR: + return; default: return; } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 440fcdbf78f..c3193e57f46 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1766,6 +1766,7 @@ dao.wallet.dashboard.totalLockedUpAmount=Locked up in bonds dao.wallet.dashboard.totalUnlockingAmount=Unlocking BSQ from bonds dao.wallet.dashboard.totalUnlockedAmount=Unlocked BSQ from bonds dao.wallet.dashboard.totalConfiscatedAmount=Confiscated BSQ from bonds +dao.wallet.dashboard.totalInvalidatedAmount=Invalidated BSQ (burned due invalid tx) dao.wallet.dashboard.allTx=No. of all BSQ transactions dao.wallet.dashboard.utxo=No. of all unspent transaction outputs dao.wallet.dashboard.compensationIssuanceTx=No. of all compensation request issuance transactions @@ -1841,6 +1842,8 @@ dao.tx.type.enum.UNLOCK=Unlock bond dao.tx.type.enum.ASSET_LISTING_FEE=Asset listing fee # suppress inspection "UnusedProperty" dao.tx.type.enum.PROOF_OF_BURN=Proof of burn +# suppress inspection "UnusedProperty" +dao.tx.type.enum.IRREGULAR=Irregular dao.tx.issuanceFromCompReq=Compensation request/issuance dao.tx.issuanceFromCompReq.tooltip=Compensation request which led to an issuance of new BSQ.\n\ diff --git a/desktop/src/main/java/bisq/desktop/main/dao/wallet/dashboard/BsqDashboardView.java b/desktop/src/main/java/bisq/desktop/main/dao/wallet/dashboard/BsqDashboardView.java index 5458c02bc43..491d9c74125 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/wallet/dashboard/BsqDashboardView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/wallet/dashboard/BsqDashboardView.java @@ -65,7 +65,7 @@ public class BsqDashboardView extends ActivatableView implements private int gridRow = 0; private TextField genesisIssueAmountTextField, compRequestIssueAmountTextField, reimbursementAmountTextField, availableAmountTextField, burntAmountTextField, totalLockedUpAmountTextField, totalUnlockingAmountTextField, - totalUnlockedAmountTextField, totalConfiscatedAmountTextField, allTxTextField, burntTxTextField, + totalUnlockedAmountTextField, totalConfiscatedAmountTextField, totalInvalidatedAmountTextField, allTxTextField, burntTxTextField, utxoTextField, compensationIssuanceTxTextField, reimbursementIssuanceTxTextField, priceTextField, marketCapTextField; private ChangeListener priceChangeListener; @@ -108,7 +108,7 @@ public void initialize() { totalUnlockingAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockingAmount")).second; totalUnlockedAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockedAmount")).second; totalConfiscatedAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalConfiscatedAmount")).second; - gridRow++; + totalInvalidatedAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalInvalidatedAmount")).second; startRow = gridRow; addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.wallet.dashboard.market"), Layout.GROUP_DISTANCE); @@ -196,11 +196,17 @@ private void updateWithBsqBlockChainData() { Coin totalUnlockingAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockingTxOutputs()); Coin totalUnlockedAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockedTxOutputs()); Coin totalConfiscatedAmount = Coin.valueOf(daoFacade.getTotalAmountOfConfiscatedTxOutputs()); - availableAmount = issuedAmountFromGenesis + Coin totalUtxoAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnspentTxOutputs()); + + daoFacade.getTotalAmountOfConfiscatedTxOutputs(); + availableAmount = totalUtxoAmount.subtract(totalConfiscatedAmount); + + Coin totalAmount = issuedAmountFromGenesis .add(issuedAmountFromCompRequests) .add(issuedAmountFromReimbursementRequests) .subtract(burntFee) .subtract(totalConfiscatedAmount); + Coin totalInvalidatedAmount = totalAmount.subtract(availableAmount); availableAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(availableAmount)); burntAmountTextField.setText("-" + bsqFormatter.formatAmountWithGroupSeparatorAndCode(burntFee)); @@ -208,6 +214,7 @@ private void updateWithBsqBlockChainData() { totalUnlockingAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockingAmount)); totalUnlockedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockedAmount)); totalConfiscatedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalConfiscatedAmount)); + totalInvalidatedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalInvalidatedAmount)); allTxTextField.setText(String.valueOf(daoFacade.getTxs().size())); utxoTextField.setText(String.valueOf(daoFacade.getUnspentTxOutputs().size())); compensationIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.COMPENSATION))); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/wallet/tx/BsqTxView.java b/desktop/src/main/java/bisq/desktop/main/dao/wallet/tx/BsqTxView.java index aa9e20e41e1..1c15b629ead 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/wallet/tx/BsqTxView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/wallet/tx/BsqTxView.java @@ -305,6 +305,33 @@ private void updateList() { observableList.setAll(items); } + private boolean isValidType(TxType txType) { + switch (txType) { + case UNDEFINED: + case UNDEFINED_TX_TYPE: + case UNVERIFIED: + case INVALID: + return false; + case GENESIS: + case TRANSFER_BSQ: + case PAY_TRADE_FEE: + case PROPOSAL: + case COMPENSATION_REQUEST: + case REIMBURSEMENT_REQUEST: + case BLIND_VOTE: + case VOTE_REVEAL: + case LOCKUP: + case UNLOCK: + case ASSET_LISTING_FEE: + case PROOF_OF_BURN: + return true; + case IRREGULAR: + return false; + default: + return false; + } + } + private void addDateColumn() { TableColumn column = new AutoTooltipTableColumn<>(Res.get("shared.dateTime")); column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); @@ -397,7 +424,7 @@ public void updateItem(final BsqTxListItem item, boolean empty) { final TxType txType = item.getTxType(); String labelString = Res.get("dao.tx.type.enum." + txType.name()); Label label; - if (item.getConfirmations() > 0 && txType.ordinal() > TxType.INVALID.ordinal()) { + if (item.getConfirmations() > 0 && isValidType(txType)) { if (txType == TxType.COMPENSATION_REQUEST && daoFacade.isIssuanceTx(item.getTxId(), IssuanceType.COMPENSATION)) { if (field != null) @@ -470,7 +497,7 @@ public void updateItem(final BsqTxListItem item, boolean empty) { super.updateItem(item, empty); if (item != null && !empty) { TxType txType = item.getTxType(); - setText(item.getConfirmations() > 0 && txType.ordinal() > TxType.INVALID.ordinal() ? + setText(item.getConfirmations() > 0 && isValidType(txType) ? bsqFormatter.formatCoin(item.getAmount()) : Res.get("shared.na")); } else @@ -621,6 +648,10 @@ public void updateItem(final BsqTxListItem item, boolean empty) { awesomeIcon = AwesomeIcon.FILE_TEXT; style = "dao-tx-type-proposal-fee-icon"; break; + case IRREGULAR: + awesomeIcon = AwesomeIcon.WARNING_SIGN; + style = "dao-tx-type-invalid-icon"; + break; default: awesomeIcon = AwesomeIcon.QUESTION_SIGN; style = "dao-tx-type-unverified-icon"; From 3d5ff1c67444eeb97ea4dda2bd51908d364caf39 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Tue, 26 Mar 2019 18:56:20 -0500 Subject: [PATCH 03/55] Update core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java Co-Authored-By: ManfredKarrer --- .../bisq/core/dao/governance/votereveal/VoteRevealService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java index 01effa8df12..4b2675e0fba 100644 --- a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java +++ b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java @@ -181,7 +181,7 @@ private void maybeRevealVotes(int chainHeight) { boolean isInVoteRevealPhase = periodService.getPhaseForHeight(chainHeight) == DaoPhase.Phase.VOTE_REVEAL; // If we would create the tx in the last block it would be confirmed in the best case in th next // block which would be already the break and would invalidate the vote reveal. - boolean isNotLastBlockInPhase = chainHeight != periodService.getLastBlockOfPhase(chainHeight, DaoPhase.Phase.VOTE_REVEAL); + boolean isLastBlockInPhase = chainHeight == periodService.getLastBlockOfPhase(chainHeight, DaoPhase.Phase.VOTE_REVEAL); boolean isBlindVoteTxInCorrectPhaseAndCycle = periodService.isTxInPhaseAndCycle(myVote.getTxId(), DaoPhase.Phase.BLIND_VOTE, chainHeight); if (isInVoteRevealPhase && isNotLastBlockInPhase && isBlindVoteTxInCorrectPhaseAndCycle) { log.info("We call revealVote at blockHeight {} for blindVoteTxId {}", chainHeight, myVote.getTxId()); From 83b7ed9fea2206c0a7efc6c79499436e0a0c86c8 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Tue, 26 Mar 2019 18:56:28 -0500 Subject: [PATCH 04/55] Update core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java Co-Authored-By: ManfredKarrer --- .../bisq/core/dao/governance/votereveal/VoteRevealService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java index 4b2675e0fba..dad35af296f 100644 --- a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java +++ b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java @@ -183,7 +183,7 @@ private void maybeRevealVotes(int chainHeight) { // block which would be already the break and would invalidate the vote reveal. boolean isLastBlockInPhase = chainHeight == periodService.getLastBlockOfPhase(chainHeight, DaoPhase.Phase.VOTE_REVEAL); boolean isBlindVoteTxInCorrectPhaseAndCycle = periodService.isTxInPhaseAndCycle(myVote.getTxId(), DaoPhase.Phase.BLIND_VOTE, chainHeight); - if (isInVoteRevealPhase && isNotLastBlockInPhase && isBlindVoteTxInCorrectPhaseAndCycle) { + if (isInVoteRevealPhase && !isLastBlockInPhase && isBlindVoteTxInCorrectPhaseAndCycle) { log.info("We call revealVote at blockHeight {} for blindVoteTxId {}", chainHeight, myVote.getTxId()); // Standard case that we are in the correct phase and cycle and create the reveal tx. revealVote(myVote, true); From a3e1e677c797a50d0960a3552d5c249470a1f06e Mon Sep 17 00:00:00 2001 From: sqrrm Date: Tue, 26 Mar 2019 18:57:31 -0500 Subject: [PATCH 05/55] Update core/src/main/java/bisq/core/dao/node/parser/TxParser.java Co-Authored-By: ManfredKarrer --- core/src/main/java/bisq/core/dao/node/parser/TxParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java index e6d3bc69b44..4d37f105b90 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java +++ b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java @@ -266,7 +266,7 @@ private void processIssuance(int blockHeight, TempTx tempTx, long bsqFee) { // We tolerate such an incorrect tx and do not burn the BSQ tempTx.setTxType(TxType.IRREGULAR); - // Make sure the optionalIssuanceCandidate is set the BTC + // Make sure the optionalIssuanceCandidate is set to BTC optionalIssuanceCandidate.ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT)); // Empty Optional case is a possible valid case where a random tx matches our opReturn rules but it is not a // valid BSQ tx. From 2758db554e980570611027f9d33042c237c6cbd3 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Tue, 26 Mar 2019 19:03:23 -0500 Subject: [PATCH 06/55] Update desktop/src/main/java/bisq/desktop/main/dao/wallet/dashboard/BsqDashboardView.java Co-Authored-By: ManfredKarrer --- .../bisq/desktop/main/dao/wallet/dashboard/BsqDashboardView.java | 1 - 1 file changed, 1 deletion(-) diff --git a/desktop/src/main/java/bisq/desktop/main/dao/wallet/dashboard/BsqDashboardView.java b/desktop/src/main/java/bisq/desktop/main/dao/wallet/dashboard/BsqDashboardView.java index bfb6cf80439..d8746962c08 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/wallet/dashboard/BsqDashboardView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/wallet/dashboard/BsqDashboardView.java @@ -202,7 +202,6 @@ private void updateWithBsqBlockChainData() { Coin totalConfiscatedAmount = Coin.valueOf(daoFacade.getTotalAmountOfConfiscatedTxOutputs()); Coin totalUtxoAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnspentTxOutputs()); - daoFacade.getTotalAmountOfConfiscatedTxOutputs(); availableAmount = totalUtxoAmount.subtract(totalConfiscatedAmount); Coin totalAmount = issuedAmountFromGenesis From 7479b88a08b60418ba9d3a2876e12b28d3aab220 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 26 Mar 2019 19:04:51 -0500 Subject: [PATCH 07/55] Keep invalid state for txs which cannot have been created by the app --- .../main/java/bisq/core/dao/node/parser/TxParser.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java index e6d3bc69b44..72214dd6594 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java +++ b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java @@ -399,9 +399,8 @@ static TxType evaluateTxTypeFromOpReturnType(TempTx tempTx, OpReturnType opRetur boolean hasCorrectNumOutputs = tempTx.getTempTxOutputs().size() >= 3; if (!hasCorrectNumOutputs) { log.warn("Compensation/reimbursement request tx need to have at least 3 outputs"); - // This is not a valid issuance tx - // We tolerate such an incorrect tx and do not burn the BSQ - return TxType.IRREGULAR; + // Such a transaction cannot be created by the Bisq client and is considered invalid. + return TxType.INVALID; } TempTxOutput issuanceTxOutput = tempTx.getTempTxOutputs().get(1); @@ -409,9 +408,8 @@ static TxType evaluateTxTypeFromOpReturnType(TempTx tempTx, OpReturnType opRetur if (!hasIssuanceOutput) { log.warn("Compensation/reimbursement request txOutput type of output at index 1 need to be ISSUANCE_CANDIDATE_OUTPUT. " + "TxOutputType={}", issuanceTxOutput.getTxOutputType()); - // This is not a valid issuance tx - // We tolerate such an incorrect tx and do not burn the BSQ - return TxType.IRREGULAR; + // Such a transaction cannot be created by the Bisq client and is considered invalid. + return TxType.INVALID; } return opReturnType == OpReturnType.COMPENSATION_REQUEST ? From bf83687524b1b23bd812d0ad3b0317a98fd3c6f4 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 26 Mar 2019 19:24:45 -0500 Subject: [PATCH 08/55] Rename BTC_DAO_TESTNET to BTC_DAO_TESTNET2 to enforce new network --- core/src/main/java/bisq/core/app/BisqExecutable.java | 2 +- core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java | 4 ++-- core/src/main/java/bisq/core/btc/BitcoinModule.java | 2 +- core/src/main/java/bisq/core/user/Preferences.java | 4 ++-- core/src/main/resources/i18n/displayStrings.properties | 2 +- core/src/main/resources/i18n/displayStrings_de.properties | 2 +- core/src/main/resources/i18n/displayStrings_el.properties | 2 +- core/src/main/resources/i18n/displayStrings_es.properties | 2 +- core/src/main/resources/i18n/displayStrings_fa.properties | 2 +- core/src/main/resources/i18n/displayStrings_fr.properties | 2 +- core/src/main/resources/i18n/displayStrings_hu.properties | 2 +- core/src/main/resources/i18n/displayStrings_pt.properties | 2 +- core/src/main/resources/i18n/displayStrings_ro.properties | 2 +- core/src/main/resources/i18n/displayStrings_ru.properties | 2 +- core/src/main/resources/i18n/displayStrings_sr.properties | 2 +- core/src/main/resources/i18n/displayStrings_th.properties | 2 +- core/src/main/resources/i18n/displayStrings_vi.properties | 2 +- core/src/main/resources/i18n/displayStrings_zh.properties | 2 +- 18 files changed, 20 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java index dcd87d1c38d..6bd2108054e 100644 --- a/core/src/main/java/bisq/core/app/BisqExecutable.java +++ b/core/src/main/java/bisq/core/app/BisqExecutable.java @@ -494,7 +494,7 @@ protected void customizeOptionParsing(OptionParser parser) { format("Base currency network (default: %s)", BisqEnvironment.getDefaultBaseCurrencyNetwork().name())) .withRequiredArg() .ofType(String.class) - .describedAs(format("%s|%s|%s|%s", BTC_MAINNET, BTC_TESTNET, BTC_REGTEST, BTC_DAO_TESTNET, BTC_DAO_BETANET)); + .describedAs(format("%s|%s|%s|%s", BTC_MAINNET, BTC_TESTNET, BTC_REGTEST, BTC_DAO_TESTNET2, BTC_DAO_BETANET)); parser.accepts(BtcOptionKeys.REG_TEST_HOST, format("Bitcoin regtest host when using BTC_REGTEST network (default: %s)", RegTestHost.DEFAULT_HOST)) diff --git a/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java b/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java index 12d50336223..9b309b1dd72 100644 --- a/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java +++ b/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java @@ -28,7 +28,7 @@ public enum BaseCurrencyNetwork { BTC_MAINNET(MainNetParams.get(), "BTC", "MAINNET", "Bitcoin"), BTC_TESTNET(TestNet3Params.get(), "BTC", "TESTNET", "Bitcoin"), BTC_REGTEST(RegTestParams.get(), "BTC", "REGTEST", "Bitcoin"), - BTC_DAO_TESTNET(RegTestParams.get(), "BTC", "REGTEST", "Bitcoin"), // server side regtest + BTC_DAO_TESTNET2(RegTestParams.get(), "BTC", "REGTEST", "Bitcoin"), // server side regtest BTC_DAO_BETANET(MainNetParams.get(), "BTC", "MAINNET", "Bitcoin"); // mainnet test genesis @Getter @@ -56,7 +56,7 @@ public boolean isTestnet() { } public boolean isDaoTestNet() { - return "BTC_DAO_TESTNET".equals(name()); + return "BTC_DAO_TESTNET2".equals(name()); } public boolean isDaoBetaNet() { diff --git a/core/src/main/java/bisq/core/btc/BitcoinModule.java b/core/src/main/java/bisq/core/btc/BitcoinModule.java index 60c55d31c65..784fcb2cc2c 100644 --- a/core/src/main/java/bisq/core/btc/BitcoinModule.java +++ b/core/src/main/java/bisq/core/btc/BitcoinModule.java @@ -54,7 +54,7 @@ public BitcoinModule(Environment environment) { @Override protected void configure() { - // We we have selected BTC_DAO_TESTNET we use our master regtest node, otherwise the specified host or default + // We we have selected BTC_DAO_TESTNET2 we use our master regtest node, otherwise the specified host or default // (localhost) String regTestHost = environment.getProperty(BtcOptionKeys.REG_TEST_HOST, String.class, ""); if (regTestHost.isEmpty()) { diff --git a/core/src/main/java/bisq/core/user/Preferences.java b/core/src/main/java/bisq/core/user/Preferences.java index 754e6b75a0e..962b781a5f0 100644 --- a/core/src/main/java/bisq/core/user/Preferences.java +++ b/core/src/main/java/bisq/core/user/Preferences.java @@ -651,7 +651,7 @@ public BlockChainExplorer getBlockChainExplorer() { case BTC_TESTNET: case BTC_REGTEST: return prefPayload.getBlockChainExplorerTestNet(); - case BTC_DAO_TESTNET: + case BTC_DAO_TESTNET2: return BTC_DAO_TEST_NET_EXPLORERS.get(0); case BTC_DAO_BETANET: return prefPayload.getBlockChainExplorerMainNet(); @@ -668,7 +668,7 @@ public ArrayList getBlockChainExplorers() { case BTC_TESTNET: case BTC_REGTEST: return BTC_TEST_NET_EXPLORERS; - case BTC_DAO_TESTNET: + case BTC_DAO_TESTNET2: return BTC_DAO_TEST_NET_EXPLORERS; case BTC_DAO_BETANET: return BTC_MAIN_NET_EXPLORERS; diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 51ef2ace867..50139b5a4b1 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2407,7 +2407,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet +BTC_DAO_TESTNET2=Bitcoin DAO Testnet # suppress inspection "UnusedProperty" BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index c0f033b24cc..5f71f381fa8 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin-Testnetzwerk # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin-Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin-DAO-Testnetzwerk +BTC_DAO_TESTNET2=Bitcoin-DAO-Testnetzwerk time.year=Jahr time.month=Monat diff --git a/core/src/main/resources/i18n/displayStrings_el.properties b/core/src/main/resources/i18n/displayStrings_el.properties index f1b8ba880ec..5186f5402ac 100644 --- a/core/src/main/resources/i18n/displayStrings_el.properties +++ b/core/src/main/resources/i18n/displayStrings_el.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet +BTC_DAO_TESTNET2=Bitcoin DAO Testnet time.year=Έτος time.month=Μήνας diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index 5b247b6f2ac..c2f138bf13b 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Red de prueba de Bitcoin # suppress inspection "UnusedProperty" BTC_REGTEST=Regtest Bitcoin # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Red de prueba de Bitcoin +BTC_DAO_TESTNET2=Red de prueba de Bitcoin time.year=Año time.month=Mes diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 9bc58e1b6e2..85c7d3b7c64 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet +BTC_DAO_TESTNET2=Bitcoin DAO Testnet time.year=سال time.month=ماه diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 8d025981151..510f35377f8 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet +BTC_DAO_TESTNET2=Bitcoin DAO Testnet time.year=Année time.month=Mois diff --git a/core/src/main/resources/i18n/displayStrings_hu.properties b/core/src/main/resources/i18n/displayStrings_hu.properties index 099d544e4e5..d2f803a2424 100644 --- a/core/src/main/resources/i18n/displayStrings_hu.properties +++ b/core/src/main/resources/i18n/displayStrings_hu.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet +BTC_DAO_TESTNET2=Bitcoin DAO Testnet time.year=Év time.month=Hónap diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 75932ea6e51..f2519de5cf2 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Testnet do Bitcoin # suppress inspection "UnusedProperty" BTC_REGTEST=Regtest do Bitcoin # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet +BTC_DAO_TESTNET2=Bitcoin DAO Testnet time.year=Ano time.month=Mês diff --git a/core/src/main/resources/i18n/displayStrings_ro.properties b/core/src/main/resources/i18n/displayStrings_ro.properties index c7accfc6e62..2389047457f 100644 --- a/core/src/main/resources/i18n/displayStrings_ro.properties +++ b/core/src/main/resources/i18n/displayStrings_ro.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet +BTC_DAO_TESTNET2=Bitcoin DAO Testnet time.year=An time.month=Lună diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index aa5a462c20a..173b0d600ba 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Биткойн Тест-сеть # suppress inspection "UnusedProperty" BTC_REGTEST=Биткойн режим регрессионного тестирования # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Биткойн DAO Testnet +BTC_DAO_TESTNET2=Биткойн DAO Testnet time.year=Год time.month=Месяц diff --git a/core/src/main/resources/i18n/displayStrings_sr.properties b/core/src/main/resources/i18n/displayStrings_sr.properties index cfe59b33c10..291083d8f83 100644 --- a/core/src/main/resources/i18n/displayStrings_sr.properties +++ b/core/src/main/resources/i18n/displayStrings_sr.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitkoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitkoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet +BTC_DAO_TESTNET2=Bitcoin DAO Testnet time.year=Godina time.month=Mesec diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index d0e0f35c3f4..b1104e2f936 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet +BTC_DAO_TESTNET2=Bitcoin DAO Testnet time.year=ปี time.month=เดือน diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 43295aedb44..f1d48182a4e 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet +BTC_DAO_TESTNET2=Bitcoin DAO Testnet time.year=Năm time.month=Tháng diff --git a/core/src/main/resources/i18n/displayStrings_zh.properties b/core/src/main/resources/i18n/displayStrings_zh.properties index ed707744476..056de4cd92d 100644 --- a/core/src/main/resources/i18n/displayStrings_zh.properties +++ b/core/src/main/resources/i18n/displayStrings_zh.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=比特币测试网络 # suppress inspection "UnusedProperty" BTC_REGTEST=比特币回归测试 # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET=Bitcoin DAO Testnet +BTC_DAO_TESTNET2=Bitcoin DAO Testnet time.year=年线 time.month=月线 From 8016f452f3ee7b25b0e27829d79b54b31b60dbd3 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 26 Mar 2019 20:12:43 -0500 Subject: [PATCH 09/55] Add try/catch to print error in case the file does not exist --- .../p2p/seed/DefaultSeedNodeRepository.java | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepository.java b/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepository.java index aa385dab463..6a8953dc753 100644 --- a/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepository.java +++ b/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepository.java @@ -37,14 +37,19 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; + import javax.annotation.Nullable; +// If a new BaseCurrencyNetwork type gets added we need to add the resource file for it as well! +@Slf4j public class DefaultSeedNodeRepository implements SeedNodeRepository { //TODO add support for localhost addresses private static final Pattern pattern = Pattern.compile("^([a-z0-9]+\\.onion:\\d+)"); private static final String ENDING = ".seednodes"; private static final Collection cache = new HashSet<>(); private final BisqEnvironment bisqEnvironment; + @Nullable private final String seedNodes; @Inject @@ -55,35 +60,40 @@ public DefaultSeedNodeRepository(BisqEnvironment environment, } private void reload() { + try { + // see if there are any seed nodes configured manually + if (seedNodes != null && !seedNodes.isEmpty()) { + cache.clear(); + Arrays.stream(seedNodes.split(",")).forEach(s -> cache.add(new NodeAddress(s))); - // see if there are any seed nodes configured manually - if (seedNodes != null && !seedNodes.isEmpty()) { - cache.clear(); - Arrays.stream(seedNodes.split(",")).forEach(s -> cache.add(new NodeAddress(s))); - - return; - } - - // else, we fetch the seed nodes from our resources - final InputStream fileInputStream = DefaultSeedNodeRepository.class.getClassLoader().getResourceAsStream(BisqEnvironment.getBaseCurrencyNetwork().name().toLowerCase() + ENDING); - final BufferedReader seedNodeFile = new BufferedReader(new InputStreamReader(fileInputStream)); - - // only clear if we have a fresh data source (otherwise, an exception would prevent us from getting here) - cache.clear(); + return; + } - // refill the cache - seedNodeFile.lines().forEach(line -> { - final Matcher matcher = pattern.matcher(line); - if (matcher.find()) - cache.add(new NodeAddress(matcher.group(1))); + // else, we fetch the seed nodes from our resources + InputStream fileInputStream = DefaultSeedNodeRepository.class.getClassLoader().getResourceAsStream(BisqEnvironment.getBaseCurrencyNetwork().name().toLowerCase() + ENDING); + BufferedReader seedNodeFile = new BufferedReader(new InputStreamReader(fileInputStream)); - // Maybe better include in regex... - if (line.startsWith("localhost")) - cache.add(new NodeAddress(line)); - }); + // only clear if we have a fresh data source (otherwise, an exception would prevent us from getting here) + cache.clear(); - // filter - cache.removeAll(bisqEnvironment.getBannedSeedNodes().stream().map(NodeAddress::new).collect(Collectors.toSet())); + // refill the cache + seedNodeFile.lines().forEach(line -> { + final Matcher matcher = pattern.matcher(line); + if (matcher.find()) + cache.add(new NodeAddress(matcher.group(1))); + + // Maybe better include in regex... + if (line.startsWith("localhost")) + cache.add(new NodeAddress(line)); + }); + + // filter + cache.removeAll(bisqEnvironment.getBannedSeedNodes().stream().map(NodeAddress::new).collect(Collectors.toSet())); + } catch (Throwable t) { + log.error(t.toString()); + t.printStackTrace(); + throw t; + } } public Collection getSeedNodeAddresses() { From 793d00699a7bcf2d5519dbe24daba27773d05470 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 26 Mar 2019 20:13:08 -0500 Subject: [PATCH 10/55] Rename BTC_DAO_TESTNET2 to BTC_DAO_REGTEST --- core/src/main/java/bisq/core/app/BisqEnvironment.java | 11 ++++++++--- core/src/main/java/bisq/core/app/BisqExecutable.java | 2 +- core/src/main/java/bisq/core/app/BisqSetup.java | 2 +- .../main/java/bisq/core/btc/BaseCurrencyNetwork.java | 6 +++--- core/src/main/java/bisq/core/btc/BitcoinModule.java | 4 ++-- .../main/java/bisq/core/btc/setup/WalletConfig.java | 2 +- .../main/java/bisq/core/dao/node/full/RpcService.java | 2 +- .../main/java/bisq/core/dao/state/GenesisTxInfo.java | 8 ++++---- core/src/main/java/bisq/core/user/Preferences.java | 4 ++-- ...ao_testnet.seednodes => btc_dao_regtest.seednodes} | 0 .../src/main/resources/i18n/displayStrings.properties | 2 +- .../main/resources/i18n/displayStrings_de.properties | 2 +- .../main/resources/i18n/displayStrings_el.properties | 2 +- .../main/resources/i18n/displayStrings_es.properties | 2 +- .../main/resources/i18n/displayStrings_fa.properties | 2 +- .../main/resources/i18n/displayStrings_fr.properties | 2 +- .../main/resources/i18n/displayStrings_hu.properties | 2 +- .../main/resources/i18n/displayStrings_pt.properties | 2 +- .../main/resources/i18n/displayStrings_ro.properties | 2 +- .../main/resources/i18n/displayStrings_ru.properties | 2 +- .../main/resources/i18n/displayStrings_sr.properties | 2 +- .../main/resources/i18n/displayStrings_th.properties | 2 +- .../main/resources/i18n/displayStrings_vi.properties | 2 +- .../main/resources/i18n/displayStrings_zh.properties | 2 +- .../main/settings/preferences/PreferencesView.java | 2 +- 25 files changed, 38 insertions(+), 33 deletions(-) rename core/src/main/resources/{btc_dao_testnet.seednodes => btc_dao_regtest.seednodes} (100%) diff --git a/core/src/main/java/bisq/core/app/BisqEnvironment.java b/core/src/main/java/bisq/core/app/BisqEnvironment.java index e98faf4e658..eb21b871800 100644 --- a/core/src/main/java/bisq/core/app/BisqEnvironment.java +++ b/core/src/main/java/bisq/core/app/BisqEnvironment.java @@ -361,9 +361,14 @@ public BisqEnvironment(PropertySource commandLineProperties) { final String bannedBtcNodesAsString = getProperty(FilterManager.BANNED_BTC_NODES, ""); bannedBtcNodes = !bannedBtcNodesAsString.isEmpty() ? Arrays.asList(StringUtils.deleteWhitespace(bannedBtcNodesAsString).split(",")) : null; - baseCurrencyNetwork = BaseCurrencyNetwork.valueOf(getProperty(BtcOptionKeys.BASE_CURRENCY_NETWORK, - getDefaultBaseCurrencyNetwork().name()).toUpperCase()); - + try { + // We replaced DAO_TESTNET with DAO_REGTEST. If user had DAO_TESTNET in his property file we + // get an exception and set the baseCurrencyNetwork to BTC_DAO_REGTEST + baseCurrencyNetwork = BaseCurrencyNetwork.valueOf(getProperty(BtcOptionKeys.BASE_CURRENCY_NETWORK, + getDefaultBaseCurrencyNetwork().name()).toUpperCase()); + } catch (Throwable t) { + baseCurrencyNetwork = BaseCurrencyNetwork.BTC_DAO_REGTEST; + } btcNetworkDir = Paths.get(appDataDir, baseCurrencyNetwork.name().toLowerCase()).toString(); File btcNetworkDirFile = new File(btcNetworkDir); if (!btcNetworkDirFile.exists()) diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java index 6bd2108054e..774bd9b1bd5 100644 --- a/core/src/main/java/bisq/core/app/BisqExecutable.java +++ b/core/src/main/java/bisq/core/app/BisqExecutable.java @@ -494,7 +494,7 @@ protected void customizeOptionParsing(OptionParser parser) { format("Base currency network (default: %s)", BisqEnvironment.getDefaultBaseCurrencyNetwork().name())) .withRequiredArg() .ofType(String.class) - .describedAs(format("%s|%s|%s|%s", BTC_MAINNET, BTC_TESTNET, BTC_REGTEST, BTC_DAO_TESTNET2, BTC_DAO_BETANET)); + .describedAs(format("%s|%s|%s|%s", BTC_MAINNET, BTC_TESTNET, BTC_REGTEST, BTC_DAO_REGTEST, BTC_DAO_BETANET)); parser.accepts(BtcOptionKeys.REG_TEST_HOST, format("Bitcoin regtest host when using BTC_REGTEST network (default: %s)", RegTestHost.DEFAULT_HOST)) diff --git a/core/src/main/java/bisq/core/app/BisqSetup.java b/core/src/main/java/bisq/core/app/BisqSetup.java index b8d355a0455..4e0ae4f971e 100644 --- a/core/src/main/java/bisq/core/app/BisqSetup.java +++ b/core/src/main/java/bisq/core/app/BisqSetup.java @@ -418,7 +418,7 @@ private void maybeShowTac() { private void checkIfLocalHostNodeIsRunning() { // For DAO testnet we ignore local btc node - if (BisqEnvironment.getBaseCurrencyNetwork().isDaoTestNet()) { + if (BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest()) { step3(); } else { Thread checkIfLocalHostNodeIsRunningThread = new Thread(() -> { diff --git a/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java b/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java index 9b309b1dd72..01f25255988 100644 --- a/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java +++ b/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java @@ -28,7 +28,7 @@ public enum BaseCurrencyNetwork { BTC_MAINNET(MainNetParams.get(), "BTC", "MAINNET", "Bitcoin"), BTC_TESTNET(TestNet3Params.get(), "BTC", "TESTNET", "Bitcoin"), BTC_REGTEST(RegTestParams.get(), "BTC", "REGTEST", "Bitcoin"), - BTC_DAO_TESTNET2(RegTestParams.get(), "BTC", "REGTEST", "Bitcoin"), // server side regtest + BTC_DAO_REGTEST(RegTestParams.get(), "BTC", "REGTEST", "Bitcoin"), // server side regtest BTC_DAO_BETANET(MainNetParams.get(), "BTC", "MAINNET", "Bitcoin"); // mainnet test genesis @Getter @@ -55,8 +55,8 @@ public boolean isTestnet() { return "BTC_TESTNET".equals(name()); } - public boolean isDaoTestNet() { - return "BTC_DAO_TESTNET2".equals(name()); + public boolean isDaoRegTest() { + return "BTC_DAO_REGTEST".equals(name()); } public boolean isDaoBetaNet() { diff --git a/core/src/main/java/bisq/core/btc/BitcoinModule.java b/core/src/main/java/bisq/core/btc/BitcoinModule.java index 784fcb2cc2c..6818c7be5c1 100644 --- a/core/src/main/java/bisq/core/btc/BitcoinModule.java +++ b/core/src/main/java/bisq/core/btc/BitcoinModule.java @@ -54,11 +54,11 @@ public BitcoinModule(Environment environment) { @Override protected void configure() { - // We we have selected BTC_DAO_TESTNET2 we use our master regtest node, otherwise the specified host or default + // We we have selected BTC_DAO_REGTEST we use our master regtest node, otherwise the specified host or default // (localhost) String regTestHost = environment.getProperty(BtcOptionKeys.REG_TEST_HOST, String.class, ""); if (regTestHost.isEmpty()) { - regTestHost = BisqEnvironment.getBaseCurrencyNetwork().isDaoTestNet() ? + regTestHost = BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest() ? "104.248.31.39" : RegTestHost.DEFAULT_HOST; } diff --git a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java index 443c42f420c..a47d4b99f65 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -235,7 +235,7 @@ private PeerGroup createPeerGroup() { // For dao testnet (server side regtest) we prevent to connect to a localhost node to avoid confusion // if local btc node is not synced with our dao testnet master node. - if (BisqEnvironment.getBaseCurrencyNetwork().isDaoTestNet()) + if (BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest()) peerGroup.setUseLocalhostPeerWhenPossible(false); return peerGroup; diff --git a/core/src/main/java/bisq/core/dao/node/full/RpcService.java b/core/src/main/java/bisq/core/dao/node/full/RpcService.java index 3225905e9f3..e3cac5102d9 100644 --- a/core/src/main/java/bisq/core/dao/node/full/RpcService.java +++ b/core/src/main/java/bisq/core/dao/node/full/RpcService.java @@ -99,7 +99,7 @@ public RpcService(Preferences preferences, boolean isPortSet = rpcPort != null && !rpcPort.isEmpty(); boolean isMainnet = BisqEnvironment.getBaseCurrencyNetwork().isMainnet(); boolean isTestnet = BisqEnvironment.getBaseCurrencyNetwork().isTestnet(); - boolean isDaoTestNet = BisqEnvironment.getBaseCurrencyNetwork().isDaoTestNet(); + boolean isDaoTestNet = BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest(); boolean isDaoBetaNet = BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet(); this.rpcPort = isPortSet ? rpcPort : isMainnet || isDaoBetaNet ? "8332" : diff --git a/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java b/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java index e970b2e6a12..ac3cef3f88b 100644 --- a/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java +++ b/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java @@ -106,7 +106,7 @@ public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); boolean isMainnet = baseCurrencyNetwork.isMainnet(); boolean isTestnet = baseCurrencyNetwork.isTestnet(); - boolean isDaoTestNet = baseCurrencyNetwork.isDaoTestNet(); + boolean isDaoRegTest = baseCurrencyNetwork.isDaoRegTest(); boolean isDaoBetaNet = baseCurrencyNetwork.isDaoBetaNet(); boolean isRegtest = baseCurrencyNetwork.isRegtest(); if (!genesisTxId.isEmpty()) { @@ -115,7 +115,7 @@ public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, this.genesisTxId = MAINNET_GENESIS_TX_ID; } else if (isTestnet) { this.genesisTxId = TESTNET_GENESIS_TX_ID; - } else if (isDaoTestNet) { + } else if (isDaoRegTest) { this.genesisTxId = DAO_TESTNET_GENESIS_TX_ID; } else if (isDaoBetaNet) { this.genesisTxId = DAO_BETANET_GENESIS_TX_ID; @@ -131,7 +131,7 @@ public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, this.genesisBlockHeight = MAINNET_GENESIS_BLOCK_HEIGHT; } else if (isTestnet) { this.genesisBlockHeight = TESTNET_GENESIS_BLOCK_HEIGHT; - } else if (isDaoTestNet) { + } else if (isDaoRegTest) { this.genesisBlockHeight = DAO_TESTNET_GENESIS_BLOCK_HEIGHT; } else if (isDaoBetaNet) { this.genesisBlockHeight = DAO_BETANET_GENESIS_BLOCK_HEIGHT; @@ -147,7 +147,7 @@ public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, this.genesisTotalSupply = MAINNET_GENESIS_TOTAL_SUPPLY.value; } else if (isTestnet) { this.genesisTotalSupply = TESTNET_GENESIS_TOTAL_SUPPLY.value; - } else if (isDaoTestNet) { + } else if (isDaoRegTest) { this.genesisTotalSupply = DAO_TESTNET_GENESIS_TOTAL_SUPPLY.value; } else if (isDaoBetaNet) { this.genesisTotalSupply = DAO_BETANET_GENESIS_TOTAL_SUPPLY.value; diff --git a/core/src/main/java/bisq/core/user/Preferences.java b/core/src/main/java/bisq/core/user/Preferences.java index 962b781a5f0..1a2ddfcc195 100644 --- a/core/src/main/java/bisq/core/user/Preferences.java +++ b/core/src/main/java/bisq/core/user/Preferences.java @@ -651,7 +651,7 @@ public BlockChainExplorer getBlockChainExplorer() { case BTC_TESTNET: case BTC_REGTEST: return prefPayload.getBlockChainExplorerTestNet(); - case BTC_DAO_TESTNET2: + case BTC_DAO_REGTEST: return BTC_DAO_TEST_NET_EXPLORERS.get(0); case BTC_DAO_BETANET: return prefPayload.getBlockChainExplorerMainNet(); @@ -668,7 +668,7 @@ public ArrayList getBlockChainExplorers() { case BTC_TESTNET: case BTC_REGTEST: return BTC_TEST_NET_EXPLORERS; - case BTC_DAO_TESTNET2: + case BTC_DAO_REGTEST: return BTC_DAO_TEST_NET_EXPLORERS; case BTC_DAO_BETANET: return BTC_MAIN_NET_EXPLORERS; diff --git a/core/src/main/resources/btc_dao_testnet.seednodes b/core/src/main/resources/btc_dao_regtest.seednodes similarity index 100% rename from core/src/main/resources/btc_dao_testnet.seednodes rename to core/src/main/resources/btc_dao_regtest.seednodes diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 50139b5a4b1..e84da4f9732 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2407,7 +2407,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET2=Bitcoin DAO Testnet +BTC_DAO_REGTEST=Bitcoin DAO Regtest # suppress inspection "UnusedProperty" BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 5f71f381fa8..2cec4d0069f 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin-Testnetzwerk # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin-Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET2=Bitcoin-DAO-Testnetzwerk +BTC_DAO_REGTEST=Bitcoin-DAO-Testnetzwerk time.year=Jahr time.month=Monat diff --git a/core/src/main/resources/i18n/displayStrings_el.properties b/core/src/main/resources/i18n/displayStrings_el.properties index 5186f5402ac..a4fde956ccd 100644 --- a/core/src/main/resources/i18n/displayStrings_el.properties +++ b/core/src/main/resources/i18n/displayStrings_el.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET2=Bitcoin DAO Testnet +BTC_DAO_REGTEST=Bitcoin DAO Testnet time.year=Έτος time.month=Μήνας diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index c2f138bf13b..be5f74cc947 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Red de prueba de Bitcoin # suppress inspection "UnusedProperty" BTC_REGTEST=Regtest Bitcoin # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET2=Red de prueba de Bitcoin +BTC_DAO_REGTEST=Red de prueba de Bitcoin time.year=Año time.month=Mes diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 85c7d3b7c64..6d9265fc2ed 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET2=Bitcoin DAO Testnet +BTC_DAO_REGTEST=Bitcoin DAO Testnet time.year=سال time.month=ماه diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 510f35377f8..cd7ca49b6d5 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET2=Bitcoin DAO Testnet +BTC_DAO_REGTEST=Bitcoin DAO Testnet time.year=Année time.month=Mois diff --git a/core/src/main/resources/i18n/displayStrings_hu.properties b/core/src/main/resources/i18n/displayStrings_hu.properties index d2f803a2424..82245d0ac88 100644 --- a/core/src/main/resources/i18n/displayStrings_hu.properties +++ b/core/src/main/resources/i18n/displayStrings_hu.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET2=Bitcoin DAO Testnet +BTC_DAO_REGTEST=Bitcoin DAO Testnet time.year=Év time.month=Hónap diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index f2519de5cf2..ed3682decc2 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Testnet do Bitcoin # suppress inspection "UnusedProperty" BTC_REGTEST=Regtest do Bitcoin # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET2=Bitcoin DAO Testnet +BTC_DAO_REGTEST=Bitcoin DAO Testnet time.year=Ano time.month=Mês diff --git a/core/src/main/resources/i18n/displayStrings_ro.properties b/core/src/main/resources/i18n/displayStrings_ro.properties index 2389047457f..e20785d7c9b 100644 --- a/core/src/main/resources/i18n/displayStrings_ro.properties +++ b/core/src/main/resources/i18n/displayStrings_ro.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET2=Bitcoin DAO Testnet +BTC_DAO_REGTEST=Bitcoin DAO Testnet time.year=An time.month=Lună diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index 173b0d600ba..5dba7cf6e45 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Биткойн Тест-сеть # suppress inspection "UnusedProperty" BTC_REGTEST=Биткойн режим регрессионного тестирования # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET2=Биткойн DAO Testnet +BTC_DAO_REGTEST=Биткойн DAO Testnet time.year=Год time.month=Месяц diff --git a/core/src/main/resources/i18n/displayStrings_sr.properties b/core/src/main/resources/i18n/displayStrings_sr.properties index 291083d8f83..c80e99dbea7 100644 --- a/core/src/main/resources/i18n/displayStrings_sr.properties +++ b/core/src/main/resources/i18n/displayStrings_sr.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitkoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitkoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET2=Bitcoin DAO Testnet +BTC_DAO_REGTEST=Bitcoin DAO Testnet time.year=Godina time.month=Mesec diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index b1104e2f936..cbb5a276c5b 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET2=Bitcoin DAO Testnet +BTC_DAO_REGTEST=Bitcoin DAO Testnet time.year=ปี time.month=เดือน diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index f1d48182a4e..75417bd13e6 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET2=Bitcoin DAO Testnet +BTC_DAO_REGTEST=Bitcoin DAO Testnet time.year=Năm time.month=Tháng diff --git a/core/src/main/resources/i18n/displayStrings_zh.properties b/core/src/main/resources/i18n/displayStrings_zh.properties index 056de4cd92d..7c9ce531803 100644 --- a/core/src/main/resources/i18n/displayStrings_zh.properties +++ b/core/src/main/resources/i18n/displayStrings_zh.properties @@ -2020,7 +2020,7 @@ BTC_TESTNET=比特币测试网络 # suppress inspection "UnusedProperty" BTC_REGTEST=比特币回归测试 # suppress inspection "UnusedProperty" -BTC_DAO_TESTNET2=Bitcoin DAO Testnet +BTC_DAO_REGTEST=Bitcoin DAO Testnet time.year=年线 time.month=月线 diff --git a/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java b/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java index 9cc73f18778..852e67de5d2 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java @@ -593,7 +593,7 @@ private void activateGeneralOptions() { // We only show mainnet and dao testnet. Testnet is rather un-usable for application testing when asics // create 10000s of blocks per day. baseCurrencyNetworks = baseCurrencyNetworks.stream() - .filter(e -> e.isMainnet() || e.isDaoTestNet() || e.isDaoBetaNet()) + .filter(e -> e.isMainnet() || e.isDaoRegTest() || e.isDaoBetaNet()) .collect(Collectors.toList()); selectBaseCurrencyNetworkComboBox.setItems(FXCollections.observableArrayList(baseCurrencyNetworks)); selectBaseCurrencyNetworkComboBox.setOnAction(e -> onSelectNetwork()); From 1bd6f03e15e2a1422bc9a6fc6283f5d3b081161f Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 26 Mar 2019 20:43:08 -0500 Subject: [PATCH 11/55] Add new genesis tx for DAO_REGTEST --- .../main/java/bisq/core/dao/state/GenesisTxInfo.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java b/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java index ac3cef3f88b..30164330159 100644 --- a/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java +++ b/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java @@ -52,9 +52,9 @@ public class GenesisTxInfo { private static final int TESTNET_GENESIS_BLOCK_HEIGHT = 1446300; // 2018-12-02 private static final Coin TESTNET_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5"); // 2.5M BSQ / 2.50000000 BTC - private static final String DAO_TESTNET_GENESIS_TX_ID = "cb316a186b9e88d1b8e1ce8dc79cc6a2080cc7bbc6df94f2be325d8253417af1"; - private static final int DAO_TESTNET_GENESIS_BLOCK_HEIGHT = 104; // 2019-02-19 - private static final Coin DAO_TESTNET_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5"); // 2.5M BSQ / 2.50000000 BTC + private static final String DAO_REGTEST_GENESIS_TX_ID = "d594ad0c5de53e261b5784e5eb2acec8b807c45b74450401f488d36b8acf2e14"; + private static final int DAO_REGTEST_GENESIS_BLOCK_HEIGHT = 104; // 2019-03-26 + private static final Coin DAO_REGTEST_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5"); // 2.5M BSQ / 2.50000000 BTC private static final String DAO_BETANET_GENESIS_TX_ID = "0bd66d8ff26476b55dfaf2a5db0c659a5d8635566488244df25606db63a08bd9"; private static final int DAO_BETANET_GENESIS_BLOCK_HEIGHT = 567405; // 2019-03-16 @@ -116,7 +116,7 @@ public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, } else if (isTestnet) { this.genesisTxId = TESTNET_GENESIS_TX_ID; } else if (isDaoRegTest) { - this.genesisTxId = DAO_TESTNET_GENESIS_TX_ID; + this.genesisTxId = DAO_REGTEST_GENESIS_TX_ID; } else if (isDaoBetaNet) { this.genesisTxId = DAO_BETANET_GENESIS_TX_ID; } else if (isRegtest) { @@ -132,7 +132,7 @@ public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, } else if (isTestnet) { this.genesisBlockHeight = TESTNET_GENESIS_BLOCK_HEIGHT; } else if (isDaoRegTest) { - this.genesisBlockHeight = DAO_TESTNET_GENESIS_BLOCK_HEIGHT; + this.genesisBlockHeight = DAO_REGTEST_GENESIS_BLOCK_HEIGHT; } else if (isDaoBetaNet) { this.genesisBlockHeight = DAO_BETANET_GENESIS_BLOCK_HEIGHT; } else if (isRegtest) { @@ -148,7 +148,7 @@ public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, } else if (isTestnet) { this.genesisTotalSupply = TESTNET_GENESIS_TOTAL_SUPPLY.value; } else if (isDaoRegTest) { - this.genesisTotalSupply = DAO_TESTNET_GENESIS_TOTAL_SUPPLY.value; + this.genesisTotalSupply = DAO_REGTEST_GENESIS_TOTAL_SUPPLY.value; } else if (isDaoBetaNet) { this.genesisTotalSupply = DAO_BETANET_GENESIS_TOTAL_SUPPLY.value; } else if (isRegtest) { From 999fe85afc61a34f338133d6deaa6048b35f4f22 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 26 Mar 2019 20:46:30 -0500 Subject: [PATCH 12/55] Reduce durations of phases for DAO_REGTEST --- .../java/bisq/core/dao/governance/param/Param.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/param/Param.java b/core/src/main/java/bisq/core/dao/governance/param/Param.java index c8aed6948d3..78cec37e58e 100644 --- a/core/src/main/java/bisq/core/dao/governance/param/Param.java +++ b/core/src/main/java/bisq/core/dao/governance/param/Param.java @@ -142,7 +142,7 @@ public enum Param { "4" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "144" : // daoBetaNet; 1 day - "380", // testnet or dao testnet (server side regtest); 2.6 days + "134", // testnet or dao regtest (server side regtest); 2.6 days ParamType.BLOCK, 2, 2), PHASE_BREAK1(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "149" : // mainnet; 1 day @@ -150,7 +150,7 @@ public enum Param { "1" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "10" : // daoBetaNet - "10", // testnet or dao testnet (server side regtest) + "10", // testnet or dao regtest (server side regtest) ParamType.BLOCK, 2, 2), PHASE_BLIND_VOTE(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "451" : // mainnet; 3 days @@ -158,7 +158,7 @@ public enum Param { "2" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "144" : // daoBetaNet; 1 day - "300", // testnet or dao testnet (server side regtest); 2 days + "134", // testnet or dao regtest (server side regtest); 2 days ParamType.BLOCK, 2, 2), PHASE_BREAK2(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "9" : // mainnet @@ -166,7 +166,7 @@ public enum Param { "1" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "10" : // daoBetaNet - "10", // testnet or dao testnet (server side regtest) + "10", // testnet or dao regtest (server side regtest) ParamType.BLOCK, 2, 2), PHASE_VOTE_REVEAL(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "451" : // mainnet; 3 days @@ -174,7 +174,7 @@ public enum Param { "2" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "144" : // daoBetaNet; 1 day - "300", // testnet or dao testnet (server side regtest); 2 days + "132", // testnet or dao regtest (server side regtest); 2 days ParamType.BLOCK, 2, 2), PHASE_BREAK3(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "9" : // mainnet @@ -182,7 +182,7 @@ public enum Param { "1" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "10" : // daoBetaNet - "10", // testnet or dao testnet (server side regtest) + "10", // testnet or dao regtest (server side regtest) ParamType.BLOCK, 2, 2), PHASE_RESULT(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "10" : // mainnet @@ -190,7 +190,7 @@ public enum Param { "2" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "10" : // daoBetaNet - "2", // testnet or dao testnet (server side regtest) + "2", // testnet or dao regtest (server side regtest) ParamType.BLOCK, 2, 2); @Getter From fa8b8c57641c0242195e3d0efb8a8346ff2b2d0f Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 26 Mar 2019 21:14:30 -0500 Subject: [PATCH 13/55] use new DAO_REGTEST server and seeds --- core/src/main/java/bisq/core/btc/BitcoinModule.java | 2 +- core/src/main/resources/btc_dao_regtest.seednodes | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/BitcoinModule.java b/core/src/main/java/bisq/core/btc/BitcoinModule.java index 6818c7be5c1..8c1f87d4e67 100644 --- a/core/src/main/java/bisq/core/btc/BitcoinModule.java +++ b/core/src/main/java/bisq/core/btc/BitcoinModule.java @@ -59,7 +59,7 @@ protected void configure() { String regTestHost = environment.getProperty(BtcOptionKeys.REG_TEST_HOST, String.class, ""); if (regTestHost.isEmpty()) { regTestHost = BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest() ? - "104.248.31.39" : + "134.209.242.206" : RegTestHost.DEFAULT_HOST; } diff --git a/core/src/main/resources/btc_dao_regtest.seednodes b/core/src/main/resources/btc_dao_regtest.seednodes index fa63d9f5aed..25b99272068 100644 --- a/core/src/main/resources/btc_dao_regtest.seednodes +++ b/core/src/main/resources/btc_dao_regtest.seednodes @@ -1,5 +1,6 @@ # nodeaddress.onion:port [(@owner)] -fjr5w4eckjghqtnu.onion:8003 -3d56s6acbi3vk52v.onion:8003 -74w2sttlo4qk6go3.onion:8003 -jmc5ajqvtnzqaggm.onion:8003 +2bnvhfkdrnx5hrlv:onion:8003 +b3jnw7fyph2jsu6n:onion:8003 + +# omentgpxrxy5lehq:onion:8003 +# r7cucuwouvhdhdgo:onion:8003 From c1edc622c9495771d748dbed8d0dbecc9843f55d Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 26 Mar 2019 21:34:26 -0500 Subject: [PATCH 14/55] Fix incorrect seed address --- core/src/main/resources/btc_dao_regtest.seednodes | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/resources/btc_dao_regtest.seednodes b/core/src/main/resources/btc_dao_regtest.seednodes index 25b99272068..b4650b93cdb 100644 --- a/core/src/main/resources/btc_dao_regtest.seednodes +++ b/core/src/main/resources/btc_dao_regtest.seednodes @@ -1,6 +1,6 @@ # nodeaddress.onion:port [(@owner)] -2bnvhfkdrnx5hrlv:onion:8003 -b3jnw7fyph2jsu6n:onion:8003 +2bnvhfkdrnx5hrlv.onion:8003 +b3jnw7fyph2jsu6n.onion:8003 -# omentgpxrxy5lehq:onion:8003 -# r7cucuwouvhdhdgo:onion:8003 +# omentgpxrxy5lehq.onion:8003 +# r7cucuwouvhdhdgo.onion:8003 From 73b0a17dd5afd2f378bcedcb81c387fe4d36959a Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Tue, 26 Mar 2019 21:35:52 -0500 Subject: [PATCH 15/55] Add log for seeds --- .../bisq/core/network/p2p/seed/DefaultSeedNodeRepository.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepository.java b/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepository.java index 6a8953dc753..6b3a021789f 100644 --- a/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepository.java +++ b/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeRepository.java @@ -78,7 +78,7 @@ private void reload() { // refill the cache seedNodeFile.lines().forEach(line -> { - final Matcher matcher = pattern.matcher(line); + Matcher matcher = pattern.matcher(line); if (matcher.find()) cache.add(new NodeAddress(matcher.group(1))); @@ -89,6 +89,8 @@ private void reload() { // filter cache.removeAll(bisqEnvironment.getBannedSeedNodes().stream().map(NodeAddress::new).collect(Collectors.toSet())); + + log.info("Seed nodes: {}", cache); } catch (Throwable t) { log.error(t.toString()); t.printStackTrace(); From 8704ba9aa5f1cdb6e4c9e89b52fef14dad684939 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 27 Mar 2019 14:14:48 -0500 Subject: [PATCH 16/55] Remove unused param --- .../core/dao/governance/votereveal/VoteRevealService.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java index dad35af296f..9cdf6ab6810 100644 --- a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java +++ b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java @@ -32,7 +32,6 @@ import bisq.core.dao.governance.myvote.MyVote; import bisq.core.dao.governance.myvote.MyVoteListService; import bisq.core.dao.governance.period.PeriodService; -import bisq.core.dao.node.BsqNodeProvider; import bisq.core.dao.state.DaoStateListener; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.blockchain.Block; @@ -102,8 +101,7 @@ public VoteRevealService(DaoStateService daoStateService, BsqWalletService bsqWalletService, BtcWalletService btcWalletService, P2PService p2PService, - WalletsManager walletsManager, - BsqNodeProvider bsqNodeProvider) { + WalletsManager walletsManager) { this.daoStateService = daoStateService; this.blindVoteListService = blindVoteListService; this.periodService = periodService; From af3aedb51bba6374971eca9c152e7b69496e0edc Mon Sep 17 00:00:00 2001 From: sqrrm Date: Wed, 27 Mar 2019 14:50:47 -0500 Subject: [PATCH 17/55] Update core/src/main/java/bisq/core/dao/governance/param/Param.java Co-Authored-By: ManfredKarrer --- core/src/main/java/bisq/core/dao/governance/param/Param.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/dao/governance/param/Param.java b/core/src/main/java/bisq/core/dao/governance/param/Param.java index 78cec37e58e..f106653354c 100644 --- a/core/src/main/java/bisq/core/dao/governance/param/Param.java +++ b/core/src/main/java/bisq/core/dao/governance/param/Param.java @@ -142,7 +142,7 @@ public enum Param { "4" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "144" : // daoBetaNet; 1 day - "134", // testnet or dao regtest (server side regtest); 2.6 days + "134", // testnet or dao regtest (server side regtest); 0.93 days ParamType.BLOCK, 2, 2), PHASE_BREAK1(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "149" : // mainnet; 1 day From f0787d8009e98e3a7f163d1927a009939ad4301c Mon Sep 17 00:00:00 2001 From: sqrrm Date: Wed, 27 Mar 2019 14:50:56 -0500 Subject: [PATCH 18/55] Update core/src/main/java/bisq/core/dao/governance/param/Param.java Co-Authored-By: ManfredKarrer --- core/src/main/java/bisq/core/dao/governance/param/Param.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/dao/governance/param/Param.java b/core/src/main/java/bisq/core/dao/governance/param/Param.java index f106653354c..023640acba9 100644 --- a/core/src/main/java/bisq/core/dao/governance/param/Param.java +++ b/core/src/main/java/bisq/core/dao/governance/param/Param.java @@ -158,7 +158,7 @@ public enum Param { "2" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "144" : // daoBetaNet; 1 day - "134", // testnet or dao regtest (server side regtest); 2 days + "134", // testnet or dao regtest (server side regtest); 0.93 days ParamType.BLOCK, 2, 2), PHASE_BREAK2(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "9" : // mainnet From 9202d28c791c2e52e89c312535504df913739551 Mon Sep 17 00:00:00 2001 From: sqrrm Date: Wed, 27 Mar 2019 14:51:04 -0500 Subject: [PATCH 19/55] Update core/src/main/java/bisq/core/dao/governance/param/Param.java Co-Authored-By: ManfredKarrer --- core/src/main/java/bisq/core/dao/governance/param/Param.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/dao/governance/param/Param.java b/core/src/main/java/bisq/core/dao/governance/param/Param.java index 023640acba9..102364b2fd5 100644 --- a/core/src/main/java/bisq/core/dao/governance/param/Param.java +++ b/core/src/main/java/bisq/core/dao/governance/param/Param.java @@ -174,7 +174,7 @@ public enum Param { "2" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "144" : // daoBetaNet; 1 day - "132", // testnet or dao regtest (server side regtest); 2 days + "132", // testnet or dao regtest (server side regtest); 0.93 days ParamType.BLOCK, 2, 2), PHASE_BREAK3(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "9" : // mainnet From 499d91e7d570bc5bc8c1393342713ef7ace270e5 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 27 Mar 2019 21:34:49 -0500 Subject: [PATCH 20/55] Improve formatting --- .../desktop/main/dao/governance/result/VoteResultView.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java index 1cb3a49e929..9351c8603a7 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java @@ -272,10 +272,11 @@ private void onResultsListItemSelected(CycleListItem item) { .append(Res.getWithCol("dao.results.votes.table.header.stake")).append(" ") .append(bsqFormatter.formatCoinWithCode(Coin.valueOf(e.getStake()))).append("\n"); e.getBallotList().stream().forEach(ballot -> { - sb.append(Res.getWithCol("shared.name")).append(" ") - .append(ballot.getProposal().getName()).append("\n"); + sb.append(Res.getWithCol("dao.results.invalidVotes.proposal")).append("\n\t") + .append(Res.getWithCol("shared.name")).append(" ") + .append(ballot.getProposal().getName()).append("\n\t"); sb.append(Res.getWithCol("dao.bond.table.column.link")).append(" ") - .append(ballot.getProposal().getLink()).append("\n"); + .append(ballot.getProposal().getLink()).append("\n\t"); Vote vote = ballot.getVote(); String voteString = vote == null ? Res.get("dao.proposal.display.myVote.ignored") : vote.isAccepted() ? From f3ad4ea5b52c1846482620ece9421a1fcd9d4c0b Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 27 Mar 2019 21:35:05 -0500 Subject: [PATCH 21/55] Cleanup, add string --- .../main/java/bisq/core/dao/state/model/governance/Ballot.java | 3 +-- .../java/bisq/core/dao/state/model/governance/Proposal.java | 2 +- core/src/main/resources/i18n/displayStrings.properties | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/Ballot.java b/core/src/main/java/bisq/core/dao/state/model/governance/Ballot.java index 2e862e00035..b3b48e7f5c3 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/Ballot.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/Ballot.java @@ -64,8 +64,7 @@ public Ballot(Proposal proposal) { // PROTO BUFFER /////////////////////////////////////////////////////////////////////////////////////////// - public Ballot(Proposal proposal, - @Nullable Vote vote) { + public Ballot(Proposal proposal, @Nullable Vote vote) { this.proposal = proposal; this.vote = vote; } diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java index 7b2081f45db..fd53adc226e 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java @@ -143,7 +143,7 @@ public Date getCreationDateAsDate() { @Override public String toString() { return "Proposal{" + - "\n uid='" + txId + '\'' + + "\n txId='" + txId + '\'' + ",\n name='" + name + '\'' + ",\n link='" + link + '\'' + ",\n txId='" + txId + '\'' + diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 2a072c3d3c2..9309f519183 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1421,6 +1421,7 @@ dao.results.cycle.duration.value={0} block(s) dao.results.cycle.value.postFix.isDefaultValue=(default value) dao.results.cycle.value.postFix.hasChanged=(has been changed in voting) +dao.results.invalidVotes.proposal=Proposal dao.results.invalidVotes=We had invalid votes in that voting cycle. That can happen if a vote was \ not distributed well in the P2P network.\n{0} From 39ee4c079f1bfcb9f6230358ec0dd0ba2de75e91 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 27 Mar 2019 21:39:10 -0500 Subject: [PATCH 22/55] Protect against proposal withhold attack Set proposals which have been not included in the blind vote but which have been later published and are part if the cycles proposals to rejected. A malicious voter could manipulate the software to withhold publishing of his proposal and be the only voter on it. At the last block in the blind vote phase he could publish the proposal but others cannot vote anymore as they likely have already voted. In the vote result the other voters would have treated it like ignored and if the voter had enough BSQ to pass the quorum he could get accepted his proposal. With this change we set all proposals which are not part in the blind vote data but found in the cycle's ballot list as rejected. --- .../voteresult/VoteResultService.java | 50 +++++++++++++++---- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java index b1be48bc1c7..8ecf189ea0e 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java @@ -358,29 +358,46 @@ private DecryptedBallotsWithMerits getDecryptedBallotsWithMerits( private BallotList createBallotList(VoteWithProposalTxIdList voteWithProposalTxIdList) throws VoteResultException.MissingBallotException { - // We convert the list to a map with proposalTxId as key and the vote as value - Map voteByTxIdMap = voteWithProposalTxIdList.stream() - .filter(voteWithProposalTxId -> voteWithProposalTxId.getVote() != null) - .collect(Collectors.toMap(VoteWithProposalTxId::getProposalTxId, VoteWithProposalTxId::getVote)); + // voteWithProposalTxIdList is the list of ProposalTxId + vote from the blind vote (decrypted vote data) - // We make a map with proposalTxId as key and the ballot as value out of our stored ballot list - Map ballotByTxIdMap = ballotListService.getValidatedBallotList().stream() + // We convert the list to a map with proposalTxId as key and the vote as value. As the vote can be null we + // wrap it into an optional. + Map> voteByTxIdMap = voteWithProposalTxIdList.stream() + .collect(Collectors.toMap(VoteWithProposalTxId::getProposalTxId, e -> Optional.ofNullable(e.getVote()))); + + // We make a map with proposalTxId as key and the ballot as value out of our stored ballot list. + // This can contain ballots which have been added later and have a null value for the vote. + Map ballotByTxIdMap = ballotListService.getValidBallotsOfCycle().stream() .collect(Collectors.toMap(Ballot::getTxId, ballot -> ballot)); + // It could be that we missed some proposalPayloads. + // If we have votes with proposals which are not found in our ballots we add it to missingBallots. List missingBallots = new ArrayList<>(); + List ballots = voteByTxIdMap.entrySet().stream() .map(entry -> { String txId = entry.getKey(); if (ballotByTxIdMap.containsKey(txId)) { - // why not use proposalList? Ballot ballot = ballotByTxIdMap.get(txId); // We create a new Ballot with the proposal from the ballot list and the vote from our decrypted votes - Vote vote = entry.getValue(); // We clone the ballot instead applying the vote to the existing ballot from ballotListService // The items from ballotListService.getBallotList() contains my votes. - // Maybe we should cross verify if the vote we had in our local list matches my own vote we - // received from the network? - return new Ballot(ballot.getProposal(), vote); + + if (ballot.getVote() != null) { + // If we had set a vote it was an own active vote + if (!entry.getValue().isPresent()) { + log.warn("We found a local vote but don't have that vote in the data from the " + + "blind vote. ballot={}", ballot); + } + if (ballot.getVote() != entry.getValue().get()) { + log.warn("We found a local vote but the vote from the " + + "blind vote does not match. ballot={}, vote from blindVote data={}", + ballot, entry.getValue().get()); + } + } + + // We only return accpeted or rejected votes + return entry.getValue().map(vote -> new Ballot(ballot.getProposal(), vote)).orElse(null); } else { // We got a vote but we don't have the ballot (which includes the proposal) // We add it to the missing list to handle it as exception later. We want all missing data so we @@ -396,6 +413,17 @@ private BallotList createBallotList(VoteWithProposalTxIdList voteWithProposalTxI if (!missingBallots.isEmpty()) throw new VoteResultException.MissingBallotException(ballots, missingBallots); + // If we received a proposal after we had already voted we consider it as an proposla withhold attack and + // treat the proposal as it was voted with a rejected vote. + ballotByTxIdMap.entrySet().stream() + .filter(e -> !voteByTxIdMap.keySet().contains(e.getKey())) + .map(Map.Entry::getValue) + .forEach(ballot -> { + log.warn("We have a proposal which was not part of our blind vote and reject it. " + + "Proposal ={}" + ballot.getProposal()); + ballots.add(new Ballot(ballot.getProposal(), new Vote(false))); + }); + // Let's keep the data more deterministic by sorting it by txId. Though we are not using the sorting. ballots.sort(Comparator.comparing(Ballot::getTxId)); return new BallotList(ballots); From c3a8c2798a3545dff1b2d94c8fd011f53b4eafee Mon Sep 17 00:00:00 2001 From: sqrrm Date: Thu, 28 Mar 2019 16:26:36 -0500 Subject: [PATCH 23/55] Update core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java Co-Authored-By: ManfredKarrer --- .../bisq/core/dao/governance/voteresult/VoteResultService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java index 8ecf189ea0e..11f9ea7b945 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java @@ -396,7 +396,7 @@ private BallotList createBallotList(VoteWithProposalTxIdList voteWithProposalTxI } } - // We only return accpeted or rejected votes + // We only return accepted or rejected votes return entry.getValue().map(vote -> new Ballot(ballot.getProposal(), vote)).orElse(null); } else { // We got a vote but we don't have the ballot (which includes the proposal) From 24811ffab9421a5334a6bf222ce279ec9c0551ba Mon Sep 17 00:00:00 2001 From: sqrrm Date: Thu, 28 Mar 2019 16:26:50 -0500 Subject: [PATCH 24/55] Update core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java Co-Authored-By: ManfredKarrer --- .../bisq/core/dao/governance/voteresult/VoteResultService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java index 11f9ea7b945..a044782bc25 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java @@ -413,7 +413,7 @@ private BallotList createBallotList(VoteWithProposalTxIdList voteWithProposalTxI if (!missingBallots.isEmpty()) throw new VoteResultException.MissingBallotException(ballots, missingBallots); - // If we received a proposal after we had already voted we consider it as an proposla withhold attack and + // If we received a proposal after we had already voted we consider it as an proposal withhold attack and // treat the proposal as it was voted with a rejected vote. ballotByTxIdMap.entrySet().stream() .filter(e -> !voteByTxIdMap.keySet().contains(e.getKey())) From 695ed137e22889bdb999c4dc00cc184d0b04ee62 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 28 Mar 2019 19:51:31 -0500 Subject: [PATCH 25/55] Fix if else case --- .../bisq/core/dao/governance/voteresult/VoteResultService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java index 8ecf189ea0e..301b188fec9 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java @@ -388,8 +388,7 @@ private BallotList createBallotList(VoteWithProposalTxIdList voteWithProposalTxI if (!entry.getValue().isPresent()) { log.warn("We found a local vote but don't have that vote in the data from the " + "blind vote. ballot={}", ballot); - } - if (ballot.getVote() != entry.getValue().get()) { + } else if (ballot.getVote() != entry.getValue().get()) { log.warn("We found a local vote but the vote from the " + "blind vote does not match. ballot={}, vote from blindVote data={}", ballot, entry.getValue().get()); From acf26482dcac4892f63124dfc8a679346fd34787 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 28 Mar 2019 20:30:38 -0500 Subject: [PATCH 26/55] Use lockupTxId instead of uid --- .../bisq/desktop/main/dao/governance/ProposalDisplay.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java index 9b49c0155c3..8037f0b00ed 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java @@ -355,13 +355,12 @@ public BondedRoleType fromString(String string) { confiscateBondComboBox.setConverter(new StringConverter<>() { @Override public String toString(Bond bond) { - String bondDetails; + String details = " (" + Res.get("dao.bond.table.column.lockupTxId") + ": " + bond.getLockupTxId() + ")"; if (bond instanceof BondedRole) { - bondDetails = bond.getBondedAsset().getDisplayString(); + return bond.getBondedAsset().getDisplayString() + details; } else { - bondDetails = Res.get("dao.bond.bondedReputation"); + return Res.get("dao.bond.bondedReputation") + details; } - return bondDetails + " (" + Res.get("shared.id") + ": " + bond.getBondedAsset().getUid() + ")"; } @Override From bb7ff47d9a17679073db5f8fd3ca322134f1d3fa Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 29 Mar 2019 13:42:19 -0500 Subject: [PATCH 27/55] Add random delay for proposalPayload publishing We want to avoid that all nodes publish at the same time all proposals, so we add a random delay from 100 ms - 5 sec. A more sophisticated protection would be probably good but that delay should help to avoid network spikes and is simple enough to not add risks that the publishing would fail. --- .../governance/proposal/ProposalService.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java index cb264b875f8..9e1c3f82559 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java @@ -41,6 +41,8 @@ import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService; import bisq.network.p2p.storage.persistence.ProtectedDataStoreService; +import bisq.common.UserThread; + import org.bitcoinj.core.Coin; import com.google.inject.Inject; @@ -52,6 +54,7 @@ import java.util.List; import java.util.Optional; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import lombok.Getter; @@ -219,12 +222,14 @@ private void publishToAppendOnlyDataStore() { .filter(proposal -> validatorProvider.getValidator(proposal).isValidAndConfirmed(proposal)) .map(ProposalPayload::new) .forEach(proposalPayload -> { - boolean success = p2PService.addPersistableNetworkPayload(proposalPayload, true); - if (success) - log.info("We published a ProposalPayload to the P2P network as append-only data. proposalTxId={}", - proposalPayload.getProposal().getTxId()); - else - log.warn("publishToAppendOnlyDataStore failed for proposal " + proposalPayload.getProposal()); + UserThread.runAfterRandomDelay(() -> { + boolean success = p2PService.addPersistableNetworkPayload(proposalPayload, true); + if (success) + log.info("We published a ProposalPayload to the P2P network as append-only data. proposalTxId={}", + proposalPayload.getProposal().getTxId()); + else + log.warn("publishToAppendOnlyDataStore failed for proposal " + proposalPayload.getProposal()); + }, 100, 5000, TimeUnit.MILLISECONDS); }); } From 30059ebb445c8da918015739df4578dc7f3d4970 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 29 Mar 2019 14:29:55 -0500 Subject: [PATCH 28/55] Cleanup --- .../blindvote/network/RepublishGovernanceDataHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/network/RepublishGovernanceDataHandler.java b/core/src/main/java/bisq/core/dao/governance/blindvote/network/RepublishGovernanceDataHandler.java index 569796343b8..d5f77c6035c 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/network/RepublishGovernanceDataHandler.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/network/RepublishGovernanceDataHandler.java @@ -92,7 +92,8 @@ public void sendRepublishRequest() { private void sendRepublishRequest(NodeAddress nodeAddress) { RepublishGovernanceDataRequest republishGovernanceDataRequest = new RepublishGovernanceDataRequest(); if (timeoutTimer == null) { - timeoutTimer = UserThread.runAfter(() -> { // setup before sending to avoid race conditions + timeoutTimer = UserThread.runAfter(() -> { + // setup before sending to avoid race conditions if (!stopped) { String errorMessage = "A timeout occurred at sending republishGovernanceDataRequest:" + " to nodeAddress:" + nodeAddress; From 995844feb33c714d1178af21ce5488294895dd24 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 29 Mar 2019 16:44:34 -0500 Subject: [PATCH 29/55] Remove setFitToRowsForTableView, update merit at activate --- .../governance/proposals/ProposalsView.java | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java index 1a5190642dc..a1f658709bc 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java @@ -54,6 +54,7 @@ import bisq.core.util.BSFormatter; import bisq.core.util.BsqFormatter; +import bisq.common.UserThread; import bisq.common.app.DevEnv; import bisq.common.util.Tuple2; import bisq.common.util.Tuple3; @@ -148,7 +149,7 @@ public class ProposalsView extends ActivatableView implements Bs int extraRows = screenSize <= INITIAL_WINDOW_HEIGHT ? 0 : (int) ((screenSize - INITIAL_WINDOW_HEIGHT) / pixelsPerProposalTableRow); return extraRows == 0 ? initialProposalTableViewHeight : Math.ceil(initialProposalTableViewHeight + (extraRows * pixelsPerProposalTableRow)); }; - private ChangeListener bisqWindowVerticalSizeListener; + private ChangeListener sceneHeightListener; private TableGroupHeadline proposalsHeadline; @@ -187,14 +188,7 @@ public void initialize() { ballotListChangeListener = c -> updateListItems(); proposalListChangeListener = c -> updateListItems(); - bisqWindowVerticalSizeListener = (observable, oldValue, newValue) -> { - double newTableViewHeight = proposalTableViewHeight.apply(newValue.doubleValue()); - if (tableView.getHeight() != newTableViewHeight) { - tableView.setMinHeight(newTableViewHeight); - double diff = newTableViewHeight - tableView.getHeight(); - proposalsHeadline.setMaxHeight(proposalsHeadline.getHeight() + diff); - } - }; + sceneHeightListener = (observable, oldValue, newValue) -> updateTableHeight(newValue.doubleValue()); stakeListener = (observable, oldValue, newValue) -> updateViews(); } @@ -225,10 +219,14 @@ protected void activate() { bsqWalletService.getUnlockingBondsBalance()); updateListItems(); - GUIUtil.setFitToRowsForTableView(tableView, 38, 28, 4, 4); + tableView.setPrefHeight(100); updateViews(); - root.getScene().heightProperty().addListener(bisqWindowVerticalSizeListener); + updateListItems(); + applyMerit(); + + root.getScene().heightProperty().addListener(sceneHeightListener); + UserThread.execute(() -> updateTableHeight(root.getScene().getHeight())); } @Override @@ -591,6 +589,15 @@ private boolean isBlindVotePhaseButNotLastBlock() { return daoFacade.isInPhaseButNotLastBlock(DaoPhase.Phase.BLIND_VOTE); } + private void updateTableHeight(double height) { + double newTableViewHeight = proposalTableViewHeight.apply(height); + if (tableView.getHeight() != newTableViewHeight) { + tableView.setMinHeight(newTableViewHeight); + double diff = newTableViewHeight - tableView.getHeight(); + proposalsHeadline.setMaxHeight(proposalsHeadline.getHeight() + diff); + } + } + /////////////////////////////////////////////////////////////////////////////////////////// // Create views From a08b91055ecfe59b7d136d3b8346c49afb930258 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 29 Mar 2019 19:42:32 -0500 Subject: [PATCH 30/55] Add support for displaying burned BSQ from invalid txs --- .../main/java/bisq/core/dao/DaoFacade.java | 8 ++++ .../bisq/core/dao/node/parser/TxParser.java | 10 ++--- .../bisq/core/dao/state/DaoStateService.java | 26 ++++++++++++ .../resources/i18n/displayStrings.properties | 6 +++ .../economy/dashboard/BsqDashboardView.java | 4 +- .../main/dao/economy/supply/SupplyView.java | 42 ++++++++++++------- .../transactions/BSQTransactionsView.java | 7 +++- 7 files changed, 80 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/DaoFacade.java b/core/src/main/java/bisq/core/dao/DaoFacade.java index a5681e47958..a3c5cc65278 100644 --- a/core/src/main/java/bisq/core/dao/DaoFacade.java +++ b/core/src/main/java/bisq/core/dao/DaoFacade.java @@ -544,6 +544,14 @@ public long getTotalAmountOfConfiscatedTxOutputs() { return daoStateService.getTotalAmountOfConfiscatedTxOutputs(); } + public long getBurnedBsqOfAllInvalidTxs() { + return daoStateService.getBurnedBsqOfAllInvalidTxs(); + } + + public List getInvalidTxs() { + return daoStateService.getInvalidTxs(); + } + public long getTotalAmountOfUnspentTxOutputs() { // Does not consider confiscated outputs (they stay as utxo) return daoStateService.getUnspentTxOutputMap().values().stream().mapToLong(BaseTxOutput::getValue).sum(); diff --git a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java index 697b0c6cef4..aa92f20dc6a 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java +++ b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java @@ -160,12 +160,12 @@ private Optional findTx(RawTx rawTx) { if (isTxInvalid(tempTx, bsqOutputFound, hasBurntBond)) { tempTx.setTxType(TxType.INVALID); + // We consider all BSQ inputs as burned if the tx is invalid. + // It might be that the invalid tx had a BSQ fee. To avoid that we count the burned BSQ twice we set the + // burnedFee to 0. + tempTx.setBurntFee(0); txOutputParser.invalidateUTXOCandidates(); - - if (hasBurntBsq) { - log.warn("We have destroyed BSQ because of an invalid tx. Burned BSQ={}. tx={}", - burntBsq / 100D, tempTx); - } + log.warn("We have destroyed BSQ because of an invalid tx. Burned BSQ={}. tx={}", accumulatedInputValue / 100D, tempTx); } else if (txType == TxType.IRREGULAR) { log.warn("We have an irregular tx {}", tempTx); txOutputParser.commitUTXOCandidates(); diff --git a/core/src/main/java/bisq/core/dao/state/DaoStateService.java b/core/src/main/java/bisq/core/dao/state/DaoStateService.java index 0050af09cd2..12030a3cb0e 100644 --- a/core/src/main/java/bisq/core/dao/state/DaoStateService.java +++ b/core/src/main/java/bisq/core/dao/state/DaoStateService.java @@ -21,6 +21,7 @@ import bisq.core.dao.governance.bond.BondConsensus; import bisq.core.dao.governance.param.Param; import bisq.core.dao.state.model.DaoState; +import bisq.core.dao.state.model.blockchain.BaseTxOutput; import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.model.blockchain.SpentInfo; import bisq.core.dao.state.model.blockchain.Tx; @@ -355,6 +356,10 @@ public Optional getTx(String txId) { return getTxStream().filter(tx -> tx.getId().equals(txId)).findAny(); } + public List getInvalidTxs() { + return getTxStream().filter(tx -> tx.getTxType() == TxType.INVALID).collect(Collectors.toList()); + } + public boolean containsTx(String txId) { return getTx(txId).isPresent(); } @@ -811,6 +816,27 @@ public long getTotalAmountOfConfiscatedTxOutputs() { .sum(); } + public long getBurnedBsqOfAllInvalidTxs() { + return getTxStream() + .filter(e -> e.getTxType() == TxType.INVALID) + .mapToLong(this::getBurnedBsqOfInvalidTx) + .sum(); + } + + public long getBurnedBsqOfInvalidTx(Tx tx) { + return tx.getTxInputs().stream() + .map(TxInput::getConnectedTxOutputKey) + .flatMap(txOutputKey -> getTxOutput(txOutputKey).stream()) + .filter(txOutput -> txOutput.getTxOutputType() == TxOutputType.GENESIS_OUTPUT || + txOutput.getTxOutputType() == TxOutputType.BSQ_OUTPUT || + txOutput.getTxOutputType() == TxOutputType.BLIND_VOTE_LOCK_STAKE_OUTPUT || + txOutput.getTxOutputType() == TxOutputType.VOTE_REVEAL_UNLOCK_STAKE_OUTPUT || + txOutput.getTxOutputType() == TxOutputType.LOCKUP_OUTPUT || + txOutput.getTxOutputType() == TxOutputType.UNLOCK_OUTPUT) + .mapToLong(BaseTxOutput::getValue) + .sum(); + } + // Confiscate bond public void confiscateBond(String lockupTxId) { Optional optionalTxOutput = getLockupTxOutput(lockupTxId); diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index d8a0f527591..d541fa7cd86 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1896,6 +1896,10 @@ dao.monitor.daoState.table.blockHeight=Block height dao.monitor.daoState.table.hash=Hash of DAO state dao.monitor.daoState.table.prev=Previous hash dao.monitor.daoState.conflictTable.headline=DAO state hashes from peers in conflict +dao.monitor.daoState.utxoConflicts=UTXO conflicts +dao.monitor.daoState.utxoConflicts.blockHeight=Block height: {0} +dao.monitor.daoState.utxoConflicts.sumUtxo=Sum of all UTXO: {0} BSQ +dao.monitor.daoState.utxoConflicts.sumBsq=Sum of all BSQ: {0} BSQ dao.monitor.proposal.headline=Proposals state dao.monitor.proposal.daoStateInSync=Your local proposals state is in consensus with the network @@ -1939,6 +1943,7 @@ dao.factsAndFigures.supply.totalLockedUpAmount=Locked up in bonds dao.factsAndFigures.supply.totalUnlockingAmount=Unlocking BSQ from bonds dao.factsAndFigures.supply.totalUnlockedAmount=Unlocked BSQ from bonds dao.factsAndFigures.supply.totalConfiscatedAmount=Confiscated BSQ from bonds +dao.factsAndFigures.supply.invalidTxs=Burned BSQ (invalid transactions) dao.factsAndFigures.supply.burntAmount=Burned BSQ (fees) dao.factsAndFigures.transactions.genesis=Genesis transaction @@ -1950,6 +1955,7 @@ dao.factsAndFigures.transactions.utxo=No. of all unspent transaction outputs dao.factsAndFigures.transactions.compensationIssuanceTx=No. of all compensation request issuance transactions dao.factsAndFigures.transactions.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions dao.factsAndFigures.transactions.burntTx=No. of all fee payments transactions +dao.factsAndFigures.transactions.invalidTx=No. of all invalid transactions #################################################################### # Windows diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java index 65986ee0291..e884e6d7cda 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java @@ -293,12 +293,14 @@ private void updateWithBsqBlockChainData() { Coin issuedAmountFromReimbursementRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT)); Coin burntFee = Coin.valueOf(daoFacade.getTotalBurntFee()); Coin totalConfiscatedAmount = Coin.valueOf(daoFacade.getTotalAmountOfConfiscatedTxOutputs()); + Coin burnedBsqOfAllInvalidTxs = Coin.valueOf(daoFacade.getBurnedBsqOfAllInvalidTxs()); availableAmount = issuedAmountFromGenesis .add(issuedAmountFromCompRequests) .add(issuedAmountFromReimbursementRequests) .subtract(burntFee) - .subtract(totalConfiscatedAmount); + .subtract(totalConfiscatedAmount) + .subtract(burnedBsqOfAllInvalidTxs); availableAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(availableAmount)); } diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/SupplyView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/SupplyView.java index 1d28e15ab0a..a339f4b18c5 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/SupplyView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/SupplyView.java @@ -27,6 +27,7 @@ import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.model.blockchain.Tx; +import bisq.core.dao.state.model.blockchain.TxType; import bisq.core.dao.state.model.governance.Issuance; import bisq.core.dao.state.model.governance.IssuanceType; import bisq.core.locale.GlobalSettings; @@ -64,8 +65,10 @@ import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -88,7 +91,7 @@ public class SupplyView extends ActivatableView implements DaoSt private int gridRow = 0; private TextField genesisIssueAmountTextField, compRequestIssueAmountTextField, reimbursementAmountTextField, burntAmountTextField, totalLockedUpAmountTextField, totalUnlockingAmountTextField, - totalUnlockedAmountTextField, totalConfiscatedAmountTextField; + totalUnlockedAmountTextField, totalConfiscatedAmountTextField, burnedBsqOfAllInvalidTxsTextField; private XYChart.Series seriesBSQIssued, seriesBSQBurnt; private static final Map ADJUSTERS = new HashMap<>(); @@ -165,11 +168,10 @@ private void createSupplyIncreasedInformation() { private void createSupplyReducedInformation() { addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.factsAndFigures.supply.burnt"), Layout.GROUP_DISTANCE); - Tuple3 burntAmountTuple = addTopLabelReadOnlyTextField(root, gridRow, - Res.get("dao.factsAndFigures.supply.burntAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE); - burntAmountTextField = burntAmountTuple.second; - - GridPane.setColumnSpan(burntAmountTuple.third, 2); + burntAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, + Res.get("dao.factsAndFigures.supply.burntAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; + burnedBsqOfAllInvalidTxsTextField = addTopLabelReadOnlyTextField(root, gridRow, 1, + Res.get("dao.factsAndFigures.supply.invalidTxs"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; seriesBSQBurnt = new XYChart.Series<>(); createChart(seriesBSQBurnt, Res.get("dao.factsAndFigures.supply.burnt")); @@ -190,7 +192,6 @@ private void createSupplyLockedInformation() { Res.get("dao.factsAndFigures.supply.totalUnlockedAmount")).second; totalConfiscatedAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, 1, Res.get("dao.factsAndFigures.supply.totalConfiscatedAmount")).second; - } private void createChart(XYChart.Series series, String seriesLabel) { @@ -276,12 +277,15 @@ private void updateWithBsqBlockChainData() { Coin totalUnlockingAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockingTxOutputs()); Coin totalUnlockedAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockedTxOutputs()); Coin totalConfiscatedAmount = Coin.valueOf(daoFacade.getTotalAmountOfConfiscatedTxOutputs()); + Coin burnedBsqOfAllInvalidTxs = Coin.valueOf(daoFacade.getBurnedBsqOfAllInvalidTxs()); burntAmountTextField.setText("-" + bsqFormatter.formatAmountWithGroupSeparatorAndCode(burntFee)); totalLockedUpAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalLockedUpAmount)); totalUnlockingAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockingAmount)); totalUnlockedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockedAmount)); totalConfiscatedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalConfiscatedAmount)); + String minusSign = burnedBsqOfAllInvalidTxs.isPositive() ? "-" : ""; + burnedBsqOfAllInvalidTxsTextField.setText(minusSign + bsqFormatter.formatAmountWithGroupSeparatorAndCode(burnedBsqOfAllInvalidTxs)); updateCharts(); } @@ -290,31 +294,40 @@ private void updateCharts() { seriesBSQIssued.getData().clear(); seriesBSQBurnt.getData().clear(); - Map> feesBurntByMonth = daoStateService.getBurntFeeTxs().stream() + Set burntTxs = new HashSet<>(daoStateService.getBurntFeeTxs()); + burntTxs.addAll(daoStateService.getInvalidTxs()); + + Map> burntBsqByMonth = burntTxs.stream() .sorted(Comparator.comparing(Tx::getTime)) .collect(Collectors.groupingBy(item -> new Date(item.getTime()).toLocalDate() .with(ADJUSTERS.get(MONTH)))); - List> updatedBurntBSQ = feesBurntByMonth.keySet().stream() + List> updatedBurntBsq = burntBsqByMonth.keySet().stream() .map(date -> { ZonedDateTime zonedDateTime = date.atStartOfDay(ZoneId.systemDefault()); - return new XYChart.Data(zonedDateTime.toInstant().getEpochSecond(), feesBurntByMonth.get(date) + return new XYChart.Data(zonedDateTime.toInstant().getEpochSecond(), burntBsqByMonth.get(date) .stream() - .mapToDouble(Tx::getBurntFee) + .mapToDouble(tx -> { + if (tx.getTxType() == TxType.INVALID) { + return daoStateService.getBurnedBsqOfInvalidTx(tx); + } else { + return tx.getBurntFee(); + } + }) .sum() ); }) .collect(Collectors.toList()); - seriesBSQBurnt.getData().setAll(updatedBurntBSQ); + seriesBSQBurnt.getData().setAll(updatedBurntBsq); Stream bsqByCompensation = daoStateService.getIssuanceSet(IssuanceType.COMPENSATION).stream() .sorted(Comparator.comparing(Issuance::getChainHeight)); - Stream bsqByReImbursement = daoStateService.getIssuanceSet(IssuanceType.REIMBURSEMENT).stream() + Stream bsqByReimbursement = daoStateService.getIssuanceSet(IssuanceType.REIMBURSEMENT).stream() .sorted(Comparator.comparing(Issuance::getChainHeight)); - Map> bsqAddedByVote = Stream.concat(bsqByCompensation, bsqByReImbursement) + Map> bsqAddedByVote = Stream.concat(bsqByCompensation, bsqByReimbursement) .collect(Collectors.groupingBy(item -> new Date(daoFacade.getBlockTime(item.getChainHeight())).toLocalDate() .with(ADJUSTERS.get(MONTH)))); @@ -329,7 +342,6 @@ private void updateCharts() { .collect(Collectors.toList()); seriesBSQIssued.getData().setAll(updatedAddedBSQ); - } } diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java index 3e4e6a49e78..c51ee6cdb8a 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java @@ -53,7 +53,8 @@ public class BSQTransactionsView extends ActivatableView impleme private int gridRow = 0; private TextField allTxTextField, burntTxTextField, utxoTextField, compensationIssuanceTxTextField, - reimbursementIssuanceTxTextField; + reimbursementIssuanceTxTextField, + invalidTxTextField; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor, lifecycle @@ -108,7 +109,8 @@ public void initialize() { Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; burntTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.factsAndFigures.transactions.burntTx")).second; - + invalidTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, + Res.get("dao.factsAndFigures.transactions.invalidTx")).second; } @Override @@ -144,6 +146,7 @@ private void updateWithBsqBlockChainData() { compensationIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.COMPENSATION))); reimbursementIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.REIMBURSEMENT))); burntTxTextField.setText(String.valueOf(daoFacade.getFeeTxs().size())); + invalidTxTextField.setText(String.valueOf(daoFacade.getInvalidTxs().size())); } } From bec73dce5362465255bae0c23cb90b44bec922fb Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 29 Mar 2019 19:42:56 -0500 Subject: [PATCH 31/55] Add popup in case the utxo balance does not match the bsq balance --- .../monitoring/DaoStateMonitoringService.java | 12 ++++-- .../dao/monitoring/model/UtxoMismatch.java | 33 +++++++++++++++ .../monitor/daostate/DaoStateMonitorView.java | 41 +++++++++++++++++++ 3 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 core/src/main/java/bisq/core/dao/monitoring/model/UtxoMismatch.java diff --git a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java index f99d7ea3551..5abf8629798 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java +++ b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java @@ -20,6 +20,7 @@ import bisq.core.dao.DaoSetupService; import bisq.core.dao.monitoring.model.DaoStateBlock; import bisq.core.dao.monitoring.model.DaoStateHash; +import bisq.core.dao.monitoring.model.UtxoMismatch; import bisq.core.dao.monitoring.network.DaoStateNetworkService; import bisq.core.dao.monitoring.network.messages.GetDaoStateHashesRequest; import bisq.core.dao.monitoring.network.messages.NewDaoStateHashMessage; @@ -40,6 +41,9 @@ import org.apache.commons.lang3.ArrayUtils; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + import java.util.LinkedList; import java.util.List; import java.util.Optional; @@ -88,6 +92,8 @@ public interface Listener { private boolean parseBlockChainComplete; @Getter private boolean isInConflict; + @Getter + private ObservableList utxoMismatches = FXCollections.observableArrayList(); /////////////////////////////////////////////////////////////////////////////////////////// @@ -142,13 +148,13 @@ public void onDaoStateChanged(Block block) { long totalBurntFee = daoStateService.getTotalBurntFee(); long compensationIssuance = daoStateService.getTotalIssuedAmount(IssuanceType.COMPENSATION); long reimbursementIssuance = daoStateService.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT); - long totalConfiscatedAmount = daoStateService.getTotalAmountOfConfiscatedTxOutputs(); + long totalInvalidAmount = daoStateService.getBurnedBsqOfAllInvalidTxs(); // confiscated funds are still in the utxo set long sumUtxo = daoStateService.getUnspentTxOutputMap().values().stream().mapToLong(BaseTxOutput::getValue).sum(); - long sumBsq = genesisTotalSupply + compensationIssuance + reimbursementIssuance - totalBurntFee; + long sumBsq = genesisTotalSupply + compensationIssuance + reimbursementIssuance - totalBurntFee - totalInvalidAmount; if (sumBsq != sumUtxo) { - throw new RuntimeException("There is a mismatch between the UTXO set and the DAO state. Please contact the Bisq devlopers."); + utxoMismatches.add(new UtxoMismatch(block.getHeight(), sumUtxo, sumBsq)); } } diff --git a/core/src/main/java/bisq/core/dao/monitoring/model/UtxoMismatch.java b/core/src/main/java/bisq/core/dao/monitoring/model/UtxoMismatch.java new file mode 100644 index 00000000000..1509023c922 --- /dev/null +++ b/core/src/main/java/bisq/core/dao/monitoring/model/UtxoMismatch.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ + +package bisq.core.dao.monitoring.model; + +import lombok.Value; + +@Value +public class UtxoMismatch { + private final int height; + private final long sumUtxo; + private final long sumBsq; + + public UtxoMismatch(int height, long sumUtxo, long sumBsq) { + this.height = height; + this.sumUtxo = sumUtxo; + this.sumBsq = sumBsq; + } +} diff --git a/desktop/src/main/java/bisq/desktop/main/dao/monitor/daostate/DaoStateMonitorView.java b/desktop/src/main/java/bisq/desktop/main/dao/monitor/daostate/DaoStateMonitorView.java index e5b9cb79d1e..722a44684f4 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/monitor/daostate/DaoStateMonitorView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/monitor/daostate/DaoStateMonitorView.java @@ -19,6 +19,7 @@ import bisq.desktop.common.view.FxmlView; import bisq.desktop.main.dao.monitor.StateMonitorView; +import bisq.desktop.main.overlays.Overlay; import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.util.FormBuilder; @@ -28,11 +29,16 @@ import bisq.core.dao.monitoring.DaoStateMonitoringService; import bisq.core.dao.monitoring.model.DaoStateBlock; import bisq.core.dao.monitoring.model.DaoStateHash; +import bisq.core.dao.monitoring.model.UtxoMismatch; import bisq.core.dao.state.DaoStateService; import bisq.core.locale.Res; +import bisq.common.util.Utilities; + import javax.inject.Inject; +import javafx.collections.ListChangeListener; + import java.util.Map; import java.util.stream.Collectors; @@ -40,6 +46,9 @@ public class DaoStateMonitorView extends StateMonitorView implements DaoStateMonitoringService.Listener { private final DaoStateMonitoringService daoStateMonitoringService; + private ListChangeListener utxoMismatchListChangeListener; + private Overlay warningPopup; + /////////////////////////////////////////////////////////////////////////////////////////// // Constructor, lifecycle @@ -59,6 +68,8 @@ private DaoStateMonitorView(DaoStateService daoStateService, @Override public void initialize() { + utxoMismatchListChangeListener = c -> updateUtxoMismatches(); + FormBuilder.addTitledGroupBg(root, gridRow, 3, Res.get("dao.monitor.daoState.headline")); statusTextField = FormBuilder.addTopLabelTextField(root, ++gridRow, @@ -71,7 +82,11 @@ public void initialize() { @Override protected void activate() { super.activate(); + daoStateMonitoringService.addListener(this); + daoStateMonitoringService.getUtxoMismatches().addListener(utxoMismatchListChangeListener); + + updateUtxoMismatches(); resyncButton.setOnAction(e -> daoFacade.resyncDao(() -> new Popup<>().attention(Res.get("setting.preferences.dao.resync.popup")) @@ -84,7 +99,9 @@ protected void activate() { @Override protected void deactivate() { super.deactivate(); + daoStateMonitoringService.removeListener(this); + daoStateMonitoringService.getUtxoMismatches().removeListener(utxoMismatchListChangeListener); } @@ -185,4 +202,28 @@ protected void onDataUpdate() { protected void requestHashesFromGenesisBlockHeight(String peerAddress) { daoStateMonitoringService.requestHashesFromGenesisBlockHeight(peerAddress); } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// + + private void updateUtxoMismatches() { + if (!daoStateMonitoringService.getUtxoMismatches().isEmpty()) { + StringBuilder sb = new StringBuilder(); + daoStateMonitoringService.getUtxoMismatches().forEach(e -> { + sb.append("\n").append(Res.get("dao.monitor.daoState.utxoConflicts.blockHeight", e.getHeight())).append("\n") + .append(Res.get("dao.monitor.daoState.utxoConflicts.sumUtxo", e.getSumUtxo() / 100)).append("\n") + .append(Res.get("dao.monitor.daoState.utxoConflicts.sumBsq", e.getSumBsq() / 100)); + }); + + if (warningPopup == null) { + warningPopup = new Popup<>().headLine(Res.get("dao.monitor.daoState.utxoConflicts")) + .warning(Utilities.toTruncatedString(sb.toString(), 500, false)).onClose(() -> { + warningPopup = null; + }); + warningPopup.show(); + } + } + } } From 4b11e582a8987c47687762e9fecb1aded2b63e9a Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 30 Mar 2019 15:55:38 -0500 Subject: [PATCH 32/55] Set MAINNET_GENESIS_TOTAL_SUPPLY of old mainnet genesis --- core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java b/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java index 30164330159..10eecd484f4 100644 --- a/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java +++ b/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java @@ -46,7 +46,7 @@ public class GenesisTxInfo { private static final String MAINNET_GENESIS_TX_ID = "81855816eca165f17f0668898faa8724a105196e90ffc4993f4cac980176674e"; private static final int MAINNET_GENESIS_BLOCK_HEIGHT = 524717; // 2018-05-27 - private static final Coin MAINNET_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5"); // 2.5M BSQ / 2.50000000 BTC + private static final Coin MAINNET_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("0.001"); private static final String TESTNET_GENESIS_TX_ID = "09e70ce0ab7a962a82a2ca84c9ae8a89140bf1c3fb6f7efad6162e39e4b362ae"; private static final int TESTNET_GENESIS_BLOCK_HEIGHT = 1446300; // 2018-12-02 From d5c7e0a66035c3f538432b1a1851c2cdf3aaae16 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 30 Mar 2019 16:01:08 -0500 Subject: [PATCH 33/55] Improve Merit handling - We add a filter to the getMeritStake method to avoid an exception at getWeightedMeritAmount which would set merits to 0 in case the issuance height blind would be larger than the vote height. I saw that error log but could not reproduce it afterwards. It basically protects that we do not add the issuance of the current cycle to the merit used in the blind vote of that cycle. - We add 1 block in the getCurrentlyAvailableMerit method to get the same merit value if the blind vote was included in the next block. --- .../dao/governance/merit/MeritConsensus.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/merit/MeritConsensus.java b/core/src/main/java/bisq/core/dao/governance/merit/MeritConsensus.java index b77347495ce..29d0a03167b 100644 --- a/core/src/main/java/bisq/core/dao/governance/merit/MeritConsensus.java +++ b/core/src/main/java/bisq/core/dao/governance/merit/MeritConsensus.java @@ -54,20 +54,19 @@ public static MeritList decryptMeritList(byte[] encryptedMeritList, SecretKey se } public static long getMeritStake(String blindVoteTxId, MeritList meritList, DaoStateService daoStateService) { - int txChainHeight = daoStateService.getTx(blindVoteTxId).map(Tx::getBlockHeight).orElse(0); - return getMeritStake(blindVoteTxId, meritList, txChainHeight); - } - - private static long getMeritStake(String blindVoteTxId, MeritList meritList, int txChainHeight) { // We need to take the chain height when the blindVoteTx got published so we get the same merit for the vote even at // later blocks (merit decreases with each block). - if (txChainHeight == 0) { + int blindVoteTxHeight = daoStateService.getTx(blindVoteTxId).map(Tx::getBlockHeight).orElse(0); + if (blindVoteTxHeight == 0) { log.error("Error at getMeritStake: blindVoteTx not found in daoStateService. blindVoteTxId=" + blindVoteTxId); return 0; } + // We only use past issuance. In case we would calculate the merit after the vote result phase we have the + // issuance from the same cycle but we must not add that to the merit. return meritList.getList().stream() .filter(merit -> isSignatureValid(merit.getSignature(), merit.getIssuance().getPubKey(), blindVoteTxId)) + .filter(merit -> merit.getIssuance().getChainHeight() <= blindVoteTxHeight) .mapToLong(merit -> { try { Issuance issuance = merit.getIssuance(); @@ -75,7 +74,7 @@ private static long getMeritStake(String blindVoteTxId, MeritList meritList, int "issuance must be of type COMPENSATION"); return getWeightedMeritAmount(issuance.getAmount(), issuance.getChainHeight(), - txChainHeight, + blindVoteTxHeight, BLOCKS_PER_YEAR); } catch (Throwable t) { log.error("Error at getMeritStake: error={}, merit={}", t.toString(), merit); @@ -145,17 +144,20 @@ public static long getWeightedMeritAmount(long amount, int issuanceHeight, int b public static long getCurrentlyAvailableMerit(MeritList meritList, int currentChainHeight) { // We need to take the chain height when the blindVoteTx got published so we get the same merit for the vote even at // later blocks (merit decreases with each block). + // We add 1 block to currentChainHeight so that the displayed merit would match the merit in case we get the + // blind vote tx into the next block. + int height = currentChainHeight + 1; return meritList.getList().stream() .mapToLong(merit -> { try { Issuance issuance = merit.getIssuance(); checkArgument(issuance.getIssuanceType() == IssuanceType.COMPENSATION, "issuance must be of type COMPENSATION"); int issuanceHeight = issuance.getChainHeight(); - checkArgument(issuanceHeight <= currentChainHeight, + checkArgument(issuanceHeight <= height, "issuanceHeight must not be larger as currentChainHeight"); return getWeightedMeritAmount(issuance.getAmount(), issuanceHeight, - currentChainHeight, + height, BLOCKS_PER_YEAR); } catch (Throwable t) { log.error("Error at getCurrentlyAvailableMerit: " + t.toString()); From a28805b983577bdf8113518d9bd85bd80c3c8613 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 30 Mar 2019 16:02:20 -0500 Subject: [PATCH 34/55] Use onParseBlockCompleteAfterBatchProcessing to avoid sequence issues When using onParseBlockChainComplete and onParseBlockCompleteAfterBatchProcessing the onParseBlockCompleteAfterBatchProcessing is called earlier as onParseBlockChainComplete which caused incorrect state update in the UI. --- .../ballot/BallotListPresentation.java | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java b/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java index ada2b27499a..e7e148061a3 100644 --- a/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java +++ b/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java @@ -45,7 +45,6 @@ public class BallotListPresentation implements BallotListService.BallotListChangeListener, DaoStateListener { private final BallotListService ballotListService; private final PeriodService periodService; - private final DaoStateService daoStateService; private final ProposalValidatorProvider proposalValidatorProvider; @Getter @@ -65,31 +64,20 @@ public BallotListPresentation(BallotListService ballotListService, ProposalValidatorProvider proposalValidatorProvider) { this.ballotListService = ballotListService; this.periodService = periodService; - this.daoStateService = daoStateService; this.proposalValidatorProvider = proposalValidatorProvider; daoStateService.addDaoStateListener(this); ballotListService.addListener(this); } + /////////////////////////////////////////////////////////////////////////////////////////// // DaoStateListener /////////////////////////////////////////////////////////////////////////////////////////// - @Override - public void onNewBlockHeight(int blockHeight) { - if (daoStateService.isParseBlockChainComplete()) { - ballotsOfCycle.setPredicate(ballot -> periodService.isTxInCorrectCycle(ballot.getTxId(), blockHeight)); - } - } - - @Override - public void onParseBlockChainComplete() { - ballotsOfCycle.setPredicate(ballot -> periodService.isTxInCorrectCycle(ballot.getTxId(), daoStateService.getChainHeight())); - } - @Override public void onParseBlockCompleteAfterBatchProcessing(Block block) { + ballotsOfCycle.setPredicate(ballot -> periodService.isTxInCorrectCycle(ballot.getTxId(), block.getHeight())); onListChanged(ballotListService.getValidatedBallotList()); } From a71a573dd9e36314940755a2b7a196b896aa9a69 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 30 Mar 2019 16:51:55 -0500 Subject: [PATCH 35/55] Add better comments, cleanup --- .../governance/blindvote/BlindVoteListService.java | 12 ++++++------ .../monitoring/BlindVoteStateMonitoringService.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteListService.java b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteListService.java index eb920dcbc61..bafaeb98aac 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteListService.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteListService.java @@ -136,20 +136,20 @@ private void fillListFromAppendOnlyDataStore() { p2PService.getP2PDataStorage().getAppendOnlyDataStoreMap().values().forEach(e -> onAppendOnlyDataAdded(e, false)); } - private void onAppendOnlyDataAdded(PersistableNetworkPayload persistableNetworkPayload, boolean doLog) { + private void onAppendOnlyDataAdded(PersistableNetworkPayload persistableNetworkPayload, boolean fromBroadcastMessage) { if (persistableNetworkPayload instanceof BlindVotePayload) { BlindVotePayload blindVotePayload = (BlindVotePayload) persistableNetworkPayload; if (!blindVotePayloads.contains(blindVotePayload)) { BlindVote blindVote = blindVotePayload.getBlindVote(); String txId = blindVote.getTxId(); - // We don't check the phase and the cycle as we want to add all object independently when we receive it - // (or when we start the app to fill our list from the data we gor from the seed node). + // We don't validate as we might receive blindVotes from other cycles or phases at startup. + // Beside that we might receive payloads we requested at the vote result phase in case we missed some + // payloads. We prefer here resilience over protection against late publishing attacks. if (blindVoteValidator.areDataFieldsValid(blindVote)) { - // We don't validate as we might receive blindVotes from other cycles or phases at startup. - blindVotePayloads.add(blindVotePayload); - if (doLog) { + if (fromBroadcastMessage) { log.info("We received a blindVotePayload. blindVoteTxId={}", txId); } + blindVotePayloads.add(blindVotePayload); } else { log.warn("We received an invalid blindVotePayload. blindVoteTxId={}", txId); } diff --git a/core/src/main/java/bisq/core/dao/monitoring/BlindVoteStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/BlindVoteStateMonitoringService.java index 7690d7cbe22..6daf9e95ef7 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/BlindVoteStateMonitoringService.java +++ b/core/src/main/java/bisq/core/dao/monitoring/BlindVoteStateMonitoringService.java @@ -159,7 +159,7 @@ public void onParseBlockChainComplete() { // We wait for processing messages until we have completed batch processing - // We request data from last 5 cycles. We ignore possible duration changes done by voting as that request + // We request data from last 5 cycles. We ignore possible duration changes done by voting. // period is arbitrary anyway... Cycle currentCycle = periodService.getCurrentCycle(); checkNotNull(currentCycle, "currentCycle must not be null"); From 1e6f0bf8e9120056b0456957bcfa956c1f8d463f Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 30 Mar 2019 16:53:07 -0500 Subject: [PATCH 36/55] Refactoring: Rename method --- .../governance/proposal/MyProposalListService.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java b/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java index 3649cedcb12..a52daf337c5 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java @@ -93,7 +93,7 @@ public MyProposalListService(P2PService p2PService, signaturePubKey = keyRing.getPubKeyRing().getSignaturePubKey(); - numConnectedPeersListener = (observable, oldValue, newValue) -> rePublishOnceWellConnected(); + numConnectedPeersListener = (observable, oldValue, newValue) -> rePublishMyProposalsOnceWellConnected(); daoStateService.addDaoStateListener(this); p2PService.getNumConnectedPeers().addListener(numConnectedPeersListener); } @@ -122,7 +122,7 @@ public void readPersisted() { @Override public void onParseBlockChainComplete() { - rePublishOnceWellConnected(); + rePublishMyProposalsOnceWellConnected(); } @@ -216,18 +216,18 @@ private boolean addToP2PNetworkAsProtectedData(Proposal proposal) { return p2PService.addProtectedStorageEntry(new TempProposalPayload(proposal, signaturePubKey), true); } - private void rePublishOnceWellConnected() { + private void rePublishMyProposalsOnceWellConnected() { int minPeers = BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? 4 : 1; if ((p2PService.getNumConnectedPeers().get() >= minPeers && p2PService.isBootstrapped()) || BisqEnvironment.getBaseCurrencyNetwork().isRegtest()) { p2PService.getNumConnectedPeers().removeListener(numConnectedPeersListener); - rePublish(); + rePublishMyProposals(); } } - private void rePublish() { + private void rePublishMyProposals() { myProposalList.forEach(proposal -> { - final String txId = proposal.getTxId(); + String txId = proposal.getTxId(); if (periodService.isTxInPhaseAndCycle(txId, DaoPhase.Phase.PROPOSAL, periodService.getChainHeight())) { boolean result = addToP2PNetworkAsProtectedData(proposal); if (!result) From b39c8bed41900d6b91c92b49645e4747c93c1a80 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 30 Mar 2019 16:53:33 -0500 Subject: [PATCH 37/55] Refactoring: Rename method --- core/src/main/java/bisq/core/dao/governance/myvote/MyVote.java | 2 +- .../main/java/bisq/core/dao/governance/myvote/MyVoteList.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/myvote/MyVote.java b/core/src/main/java/bisq/core/dao/governance/myvote/MyVote.java index 92c3ac8d925..507eaaadb70 100644 --- a/core/src/main/java/bisq/core/dao/governance/myvote/MyVote.java +++ b/core/src/main/java/bisq/core/dao/governance/myvote/MyVote.java @@ -126,7 +126,7 @@ public static MyVote fromProto(PB.MyVote proto) { // API /////////////////////////////////////////////////////////////////////////////////////////// - public String getTxId() { + public String getBlindVoteTxId() { return blindVote.getTxId(); } diff --git a/core/src/main/java/bisq/core/dao/governance/myvote/MyVoteList.java b/core/src/main/java/bisq/core/dao/governance/myvote/MyVoteList.java index fd7421e6645..169aeb21e03 100644 --- a/core/src/main/java/bisq/core/dao/governance/myvote/MyVoteList.java +++ b/core/src/main/java/bisq/core/dao/governance/myvote/MyVoteList.java @@ -64,7 +64,7 @@ public static PersistableEnvelope fromProto(PB.MyVoteList proto) { @Override public String toString() { return "List of TxId's in MyVoteList: " + getList().stream() - .map(MyVote::getTxId) + .map(MyVote::getBlindVoteTxId) .collect(Collectors.toList()); } } From e2d9fe1b5f8884cd6e5378ab4b7809a63c4b9cf1 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 30 Mar 2019 16:53:55 -0500 Subject: [PATCH 38/55] Fix comment --- .../core/dao/monitoring/ProposalStateMonitoringService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/dao/monitoring/ProposalStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/ProposalStateMonitoringService.java index bfd837b0704..91b62c649c0 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/ProposalStateMonitoringService.java +++ b/core/src/main/java/bisq/core/dao/monitoring/ProposalStateMonitoringService.java @@ -161,7 +161,7 @@ public void onParseBlockChainComplete() { // We wait for processing messages until we have completed batch processing - // We request data from last 5 cycles. We ignore possible duration changes done by voting as that request + // We request data from last 5 cycles. We ignore possible duration changes done by voting. // period is arbitrary anyway... Cycle currentCycle = periodService.getCurrentCycle(); checkNotNull(currentCycle, "currentCycle must not be null"); From 69b134b99e9b353b5db167b955738466a2847c58 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 30 Mar 2019 16:57:47 -0500 Subject: [PATCH 39/55] Refactoring: Rename method --- .../dao/governance/votereveal/VoteRevealService.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java index 9cdf6ab6810..4caca5f5230 100644 --- a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java +++ b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java @@ -180,9 +180,9 @@ private void maybeRevealVotes(int chainHeight) { // If we would create the tx in the last block it would be confirmed in the best case in th next // block which would be already the break and would invalidate the vote reveal. boolean isLastBlockInPhase = chainHeight == periodService.getLastBlockOfPhase(chainHeight, DaoPhase.Phase.VOTE_REVEAL); - boolean isBlindVoteTxInCorrectPhaseAndCycle = periodService.isTxInPhaseAndCycle(myVote.getTxId(), DaoPhase.Phase.BLIND_VOTE, chainHeight); + boolean isBlindVoteTxInCorrectPhaseAndCycle = periodService.isTxInPhaseAndCycle(myVote.getBlindVoteTxId(), DaoPhase.Phase.BLIND_VOTE, chainHeight); if (isInVoteRevealPhase && !isLastBlockInPhase && isBlindVoteTxInCorrectPhaseAndCycle) { - log.info("We call revealVote at blockHeight {} for blindVoteTxId {}", chainHeight, myVote.getTxId()); + log.info("We call revealVote at blockHeight {} for blindVoteTxId {}", chainHeight, myVote.getBlindVoteTxId()); // Standard case that we are in the correct phase and cycle and create the reveal tx. revealVote(myVote, true); } else { @@ -193,7 +193,7 @@ private void maybeRevealVotes(int chainHeight) { boolean missedPhaseSameCycle = isAfterVoteRevealPhase && isBlindVoteTxInCorrectPhaseAndCycle; // If we missed the cycle we don't care about the phase anymore. - boolean isBlindVoteTxInPastCycle = periodService.isTxInPastCycle(myVote.getTxId(), chainHeight); + boolean isBlindVoteTxInPastCycle = periodService.isTxInPastCycle(myVote.getBlindVoteTxId(), chainHeight); if (missedPhaseSameCycle || isBlindVoteTxInPastCycle) { // Exceptional case that the user missed the vote reveal phase. We still publish the vote @@ -206,7 +206,7 @@ private void maybeRevealVotes(int chainHeight) { // publish the vote reveal tx but are aware that is is invalid. log.warn("We missed the vote reveal phase but publish now the tx to unlock our locked " + "BSQ from the blind vote tx. BlindVoteTxId={}, blockHeight={}", - myVote.getTxId(), chainHeight); + myVote.getBlindVoteTxId(), chainHeight); // We handle the exception here inside the stream iteration as we have not get triggered from an // outside user intent anyway. We keep errors in a observable list so clients can observe that to @@ -234,7 +234,7 @@ private void revealVote(MyVote myVote, boolean isInVoteRevealPhase) { // myVote is already tested if it is in current cycle at maybeRevealVotes // We expect that the blind vote tx and stake output is available. If not we throw an exception. TxOutput stakeTxOutput = daoStateService.getUnspentBlindVoteStakeTxOutputs().stream() - .filter(txOutput -> txOutput.getTxId().equals(myVote.getTxId())) + .filter(txOutput -> txOutput.getTxId().equals(myVote.getBlindVoteTxId())) .findFirst() .orElseThrow(() -> new VoteRevealException("stakeTxOutput is not found for myVote.", myVote)); @@ -254,7 +254,7 @@ private void revealVote(MyVote myVote, boolean isInVoteRevealPhase) { } catch (IOException | WalletException | TransactionVerificationException | InsufficientMoneyException e) { voteRevealExceptions.add(new VoteRevealException("Exception at calling revealVote.", - e, myVote.getTxId())); + e, myVote.getBlindVoteTxId())); } catch (VoteRevealException e) { voteRevealExceptions.add(e); } From d422a732e7e4b76906fdbae053506b8bb0978ee6 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 30 Mar 2019 16:58:28 -0500 Subject: [PATCH 40/55] Handle merits better We did not update the merit correctly in case there was no proposal selected. --- .../governance/proposals/ProposalsView.java | 78 +++++++++++-------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java index a1f658709bc..7a7da8268fb 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/proposals/ProposalsView.java @@ -40,6 +40,7 @@ import bisq.core.btc.wallet.BsqWalletService; import bisq.core.dao.DaoFacade; import bisq.core.dao.governance.blindvote.BlindVoteConsensus; +import bisq.core.dao.governance.blindvote.MyBlindVoteListService; import bisq.core.dao.governance.myvote.MyVote; import bisq.core.dao.state.DaoStateListener; import bisq.core.dao.state.DaoStateService; @@ -113,6 +114,7 @@ public class ProposalsView extends ActivatableView implements Bs private final BsqWalletService bsqWalletService; private final PhasesView phasesView; private final DaoStateService daoStateService; + private final MyBlindVoteListService myBlindVoteListService; private final Preferences preferences; private final BsqFormatter bsqFormatter; private final BSFormatter btcFormatter; @@ -162,6 +164,7 @@ private ProposalsView(DaoFacade daoFacade, BsqWalletService bsqWalletService, PhasesView phasesView, DaoStateService daoStateService, + MyBlindVoteListService myBlindVoteListService, Preferences preferences, BsqFormatter bsqFormatter, BSFormatter btcFormatter, @@ -170,6 +173,7 @@ private ProposalsView(DaoFacade daoFacade, this.bsqWalletService = bsqWalletService; this.phasesView = phasesView; this.daoStateService = daoStateService; + this.myBlindVoteListService = myBlindVoteListService; this.preferences = preferences; this.bsqFormatter = bsqFormatter; this.btcFormatter = btcFormatter; @@ -197,15 +201,12 @@ public void initialize() { protected void activate() { phasesView.activate(); - phaseSubscription = EasyBind.subscribe(daoFacade.phaseProperty(), this::onPhaseChanged); selectedProposalSubscription = EasyBind.subscribe(tableView.getSelectionModel().selectedItemProperty(), this::onSelectProposal); sortedList.comparatorProperty().bind(tableView.comparatorProperty()); - - daoFacade.getActiveOrMyUnconfirmedProposals().addListener(proposalListChangeListener); - daoFacade.getAllBallots().addListener(ballotListChangeListener); - daoFacade.addBsqStateListener(this); - bsqWalletService.addBsqBalanceListener(this); + tableView.setPrefHeight(100); + root.getScene().heightProperty().addListener(sceneHeightListener); + UserThread.execute(() -> updateTableHeight(root.getScene().getHeight())); stakeInputTextField.textProperty().addListener(stakeListener); voteButton.setOnAction(e -> onVote()); @@ -218,22 +219,21 @@ protected void activate() { bsqWalletService.getLockupBondsBalance(), bsqWalletService.getUnlockingBondsBalance()); - updateListItems(); - tableView.setPrefHeight(100); - updateViews(); - - updateListItems(); - applyMerit(); + if (daoStateService.isParseBlockChainComplete()) { + addListenersAfterParseBlockChainComplete(); - root.getScene().heightProperty().addListener(sceneHeightListener); - UserThread.execute(() -> updateTableHeight(root.getScene().getHeight())); + updateListItems(); + applyMerit(); + updateViews(); + } } @Override protected void deactivate() { phasesView.deactivate(); - phaseSubscription.unsubscribe(); + if (phaseSubscription != null) + phaseSubscription.unsubscribe(); selectedProposalSubscription.unsubscribe(); sortedList.comparatorProperty().unbind(); @@ -284,20 +284,36 @@ public void onUpdateBalances(Coin availableConfirmedBalance, @Override public void onParseBlockCompleteAfterBatchProcessing(Block block) { - updateViews(); + updateListItems(); + applyMerit(); } @Override public void onParseBlockChainComplete() { - updateListItems(); - applyMerit(); + addListenersAfterParseBlockChainComplete(); } /////////////////////////////////////////////////////////////////////////////////////////// - // Protected + // Private /////////////////////////////////////////////////////////////////////////////////////////// + private void addListenersAfterParseBlockChainComplete() { + daoFacade.getActiveOrMyUnconfirmedProposals().addListener(proposalListChangeListener); + daoFacade.getAllBallots().addListener(ballotListChangeListener); + daoFacade.addBsqStateListener(this); + bsqWalletService.addBsqBalanceListener(this); + + phaseSubscription = EasyBind.subscribe(daoFacade.phaseProperty(), this::onPhaseChanged); + } + + private void updateListItems() { + listItems.forEach(ProposalsListItem::cleanup); + listItems.clear(); + + fillListItems(); + } + private void fillListItems() { if (daoFacade.phaseProperty().get().ordinal() < DaoPhase.Phase.BLIND_VOTE.ordinal()) { // proposal phase @@ -316,16 +332,8 @@ private void fillListItems() { updateViews(); } - private void updateListItems() { - listItems.forEach(ProposalsListItem::cleanup); - listItems.clear(); - - fillListItems(); - } - private void showVoteOnProposalWindow(Proposal proposal, @Nullable Ballot ballot, @Nullable EvaluatedProposal evaluatedProposal) { - if (!shownVoteOnProposalWindowForTxId.equals(proposal.getTxId())) { shownVoteOnProposalWindowForTxId = proposal.getTxId(); @@ -404,13 +412,15 @@ private void applyMerit() { // use the merit based on all past issuance with the time decay applied. // The merit from the vote stays the same over blocks, the merit from daoFacade.getMeritAndStake() // decreases with every block a bit (over 2 years it goes to zero). - boolean hasConfirmedVoteTxInCycle = daoFacade.getMyVoteListForCycle().stream() - .map(myVote -> daoFacade.getTx(myVote.getTxId())) - .findAny() - .isPresent(); + Optional optionalMyVote = daoFacade.getMyVoteListForCycle().stream() + .filter(myVote -> daoFacade.getTx(myVote.getBlindVoteTxId()).isPresent()) + .findAny(); + boolean hasConfirmedMyVoteInCycle = optionalMyVote.isPresent(); long merit; - if (selectedItem != null && hasConfirmedVoteTxInCycle) { + if (selectedItem != null && hasConfirmedMyVoteInCycle) { merit = daoFacade.getMeritAndStakeForProposal(selectedItem.getProposal().getTxId()).first; + } else if (selectedItem == null && hasConfirmedMyVoteInCycle) { + merit = optionalMyVote.get().getMerit(myBlindVoteListService, daoStateService); } else { merit = daoFacade.getAvailableMerit(); } @@ -540,8 +550,8 @@ private void updateViews() { Coin stake = Coin.valueOf(myVote.getBlindVote().getStake()); stakeInputTextField.setText(bsqFormatter.formatCoinWithCode(stake)); - if (myVote.getTxId() != null) { - blindVoteTxIdTextField.setup(myVote.getTxId()); + if (myVote.getBlindVoteTxId() != null) { + blindVoteTxIdTextField.setup(myVote.getBlindVoteTxId()); blindVoteTxIdContainer.setVisible(true); blindVoteTxIdContainer.setManaged(true); } From 823cec086c75994f0b4428e51d10d9ea18e9e4ac Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 30 Mar 2019 18:45:58 -0500 Subject: [PATCH 41/55] Improve handling fo p2p network data broadcasts --- .../blindvote/MyBlindVoteListService.java | 15 ++-- .../proposal/MyProposalListService.java | 24 +++---- .../governance/proposal/ProposalService.java | 69 ++++++++++-------- .../voteresult/MissingDataRequestService.java | 71 ++++++++++--------- .../votereveal/VoteRevealService.java | 13 +++- 5 files changed, 110 insertions(+), 82 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java b/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java index 443f6d0a9db..6c3fbd76fe1 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java @@ -135,7 +135,7 @@ public MyBlindVoteListService(P2PService p2PService, this.myVoteListService = myVoteListService; this.myProposalListService = myProposalListService; - numConnectedPeersListener = (observable, oldValue, newValue) -> rePublishOnceWellConnected(); + numConnectedPeersListener = (observable, oldValue, newValue) -> rePublishMyBlindVoteOnceWellConnected(); } @@ -176,7 +176,7 @@ public void readPersisted() { @Override public void onParseBlockChainComplete() { - rePublishOnceWellConnected(); + rePublishMyBlindVoteOnceWellConnected(); } @@ -351,15 +351,16 @@ private Transaction getBlindVoteTx(Coin stake, Coin fee, byte[] opReturnData) return bsqWalletService.signTx(txWithBtcFee); } - private void rePublishOnceWellConnected() { + private void rePublishMyBlindVoteOnceWellConnected() { + // We republish at each startup at any block during the cycle. We filter anyway for valid blind votes + // of that cycle so it is 1 blind vote getting rebroadcast at each startup to my neighbors. int minPeers = BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? 4 : 1; if ((p2PService.getNumConnectedPeers().get() >= minPeers && p2PService.isBootstrapped()) || BisqEnvironment.getBaseCurrencyNetwork().isRegtest()) { - int chainHeight = periodService.getChainHeight(); myBlindVoteList.stream() .filter(blindVote -> periodService.isTxInPhaseAndCycle(blindVote.getTxId(), DaoPhase.Phase.BLIND_VOTE, - chainHeight)) + periodService.getChainHeight())) .forEach(blindVote -> addToP2PNetwork(blindVote, null)); // We delay removal of listener as we call that inside listener itself. @@ -369,13 +370,15 @@ private void rePublishOnceWellConnected() { private void addToP2PNetwork(BlindVote blindVote, @Nullable ErrorMessageHandler errorMessageHandler) { BlindVotePayload blindVotePayload = new BlindVotePayload(blindVote); + // We use reBroadcast flag here as we only broadcast our own blindVote and want to be sure it gets distributed + // well. boolean success = p2PService.addPersistableNetworkPayload(blindVotePayload, true); if (success) { log.info("We added a blindVotePayload to the P2P network as append only data. blindVoteTxId={}", blindVote.getTxId()); } else { - final String msg = "Adding of blindVotePayload to P2P network failed. blindVoteTxId=" + blindVote.getTxId(); + String msg = "Adding of blindVotePayload to P2P network failed. blindVoteTxId=" + blindVote.getTxId(); log.error(msg); if (errorMessageHandler != null) errorMessageHandler.handleErrorMessage(msg); diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java b/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java index a52daf337c5..0357ce7958e 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/MyProposalListService.java @@ -30,6 +30,7 @@ import bisq.network.p2p.P2PService; +import bisq.common.UserThread; import bisq.common.app.DevEnv; import bisq.common.crypto.KeyRing; import bisq.common.handlers.ErrorMessageHandler; @@ -217,25 +218,22 @@ private boolean addToP2PNetworkAsProtectedData(Proposal proposal) { } private void rePublishMyProposalsOnceWellConnected() { + // We republish at each startup at any block during the cycle. We filter anyway for valid blind votes + // of that cycle so it is 1 blind vote getting rebroadcast at each startup to my neighbors. int minPeers = BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? 4 : 1; if ((p2PService.getNumConnectedPeers().get() >= minPeers && p2PService.isBootstrapped()) || BisqEnvironment.getBaseCurrencyNetwork().isRegtest()) { - p2PService.getNumConnectedPeers().removeListener(numConnectedPeersListener); - rePublishMyProposals(); + myProposalList.stream() + .filter(proposal -> periodService.isTxInPhaseAndCycle(proposal.getTxId(), + DaoPhase.Phase.PROPOSAL, + periodService.getChainHeight())) + .forEach(this::addToP2PNetworkAsProtectedData); + + // We delay removal of listener as we call that inside listener itself. + UserThread.execute(() -> p2PService.getNumConnectedPeers().removeListener(numConnectedPeersListener)); } } - private void rePublishMyProposals() { - myProposalList.forEach(proposal -> { - String txId = proposal.getTxId(); - if (periodService.isTxInPhaseAndCycle(txId, DaoPhase.Phase.PROPOSAL, periodService.getChainHeight())) { - boolean result = addToP2PNetworkAsProtectedData(proposal); - if (!result) - log.warn("Adding of proposal to P2P network failed.\nproposal=" + proposal); - } - }); - } - private void persist() { storage.queueUpForSave(); } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java index 9e1c3f82559..464d840ef96 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java @@ -41,8 +41,6 @@ import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService; import bisq.network.p2p.storage.persistence.ProtectedDataStoreService; -import bisq.common.UserThread; - import org.bitcoinj.core.Coin; import com.google.inject.Inject; @@ -54,7 +52,6 @@ import java.util.List; import java.util.Optional; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import lombok.Getter; @@ -162,11 +159,12 @@ public void onAdded(PersistableNetworkPayload payload) { @Override public void onParseBlockCompleteAfterBatchProcessing(Block block) { - int heightForRepublishing = periodService.getFirstBlockOfPhase(daoStateService.getChainHeight(), DaoPhase.Phase.BREAK1); - if (block.getHeight() == heightForRepublishing) { + // We try to broadcast at any block in the break1 phase. If we have received the data already we do not + // broadcast so we do not flood the network. + if (periodService.isInPhase(block.getHeight(), DaoPhase.Phase.BREAK1)) { // We only republish if we are completed with parsing old blocks, otherwise we would republish old // proposals all the time - publishToAppendOnlyDataStore(); + maybePublishToAppendOnlyDataStore(); fillListFromAppendOnlyDataStore(); } } @@ -217,38 +215,49 @@ private void fillListFromAppendOnlyDataStore() { p2PService.getP2PDataStorage().getAppendOnlyDataStoreMap().values().forEach(e -> onAppendOnlyDataAdded(e, false)); } - private void publishToAppendOnlyDataStore() { + private void maybePublishToAppendOnlyDataStore() { + // We set reBroadcast to false to avoid to flood the network. + // If we have 20 proposals and 200 nodes with 10 neighbor peers we would send 40 000 messages if we would set + // reBroadcast to ! tempProposals.stream() .filter(proposal -> validatorProvider.getValidator(proposal).isValidAndConfirmed(proposal)) .map(ProposalPayload::new) .forEach(proposalPayload -> { - UserThread.runAfterRandomDelay(() -> { - boolean success = p2PService.addPersistableNetworkPayload(proposalPayload, true); - if (success) - log.info("We published a ProposalPayload to the P2P network as append-only data. proposalTxId={}", - proposalPayload.getProposal().getTxId()); - else - log.warn("publishToAppendOnlyDataStore failed for proposal " + proposalPayload.getProposal()); - }, 100, 5000, TimeUnit.MILLISECONDS); + boolean success = p2PService.addPersistableNetworkPayload(proposalPayload, false); + if (success) { + log.info("We published a ProposalPayload to the P2P network as append-only data. proposalTxId={}", + proposalPayload.getProposal().getTxId()); + } + // If we had data already we did not broadcast and success is false }); } - private void onProtectedDataAdded(ProtectedStorageEntry entry, boolean doLog) { + private void onProtectedDataAdded(ProtectedStorageEntry entry, boolean fromBroadcastMessage) { ProtectedStoragePayload protectedStoragePayload = entry.getProtectedStoragePayload(); if (protectedStoragePayload instanceof TempProposalPayload) { Proposal proposal = ((TempProposalPayload) protectedStoragePayload).getProposal(); // We do not validate if we are in current cycle and if tx is confirmed yet as the tx might be not - // available/confirmed. But we check if we are in the proposal phase. - if (!tempProposals.contains(proposal)) { - if (validatorProvider.getValidator(proposal).isValidOrUnconfirmed(proposal)) { - if (doLog) { - log.info("We received a TempProposalPayload and store it to our protectedStoreList. proposalTxId={}", - proposal.getTxId()); + // available/confirmed. + // We check if we are in the proposal or break1 phase. We are tolerant to accept tempProposals in the break1 + // phase to avoid risks that a proposal published very closely to the end of the proposal phase will not be + // sufficiently broadcast. + // When we receive tempProposals from the seed node at startup we only keep those which are in the current + // proposal/break1 phase if we are in that phase. We ignore tempProposals in case we are not in the + // proposal/break1 phase as they are not used anyway but the proposalPayloads will be relevant once we + // left the proposal/break1 phase. + if (periodService.isInPhase(daoStateService.getChainHeight(), DaoPhase.Phase.PROPOSAL) || + periodService.isInPhase(daoStateService.getChainHeight(), DaoPhase.Phase.BREAK1)) { + if (!tempProposals.contains(proposal)) { + if (validatorProvider.getValidator(proposal).areDataFieldsValid(proposal)) { + if (fromBroadcastMessage) { + log.info("We received a TempProposalPayload and store it to our protectedStoreList. proposalTxId={}", + proposal.getTxId()); + } + tempProposals.add(proposal); + } else { + log.debug("We received an invalid proposal from the P2P network. Proposal.txId={}, blockHeight={}", + proposal.getTxId(), daoStateService.getChainHeight()); } - tempProposals.add(proposal); - } else { - log.debug("We received an invalid proposal from the P2P network. Proposal.txId={}, blockHeight={}", - proposal.getTxId(), daoStateService.getChainHeight()); } } } @@ -280,13 +289,17 @@ private void onProtectedDataRemoved(ProtectedStorageEntry entry) { } } - private void onAppendOnlyDataAdded(PersistableNetworkPayload persistableNetworkPayload, boolean doLog) { + private void onAppendOnlyDataAdded(PersistableNetworkPayload persistableNetworkPayload, boolean fromBroadcastMessage) { if (persistableNetworkPayload instanceof ProposalPayload) { ProposalPayload proposalPayload = (ProposalPayload) persistableNetworkPayload; if (!proposalPayloads.contains(proposalPayload)) { Proposal proposal = proposalPayload.getProposal(); + + // We don't validate phase and cycle as we might receive proposals from other cycles or phases at startup. + // Beside that we might receive payloads we requested at the vote result phase in case we missed some + // payloads. We prefer here resilience over protection against late publishing attacks. if (validatorProvider.getValidator(proposal).areDataFieldsValid(proposal)) { - if (doLog) { + if (fromBroadcastMessage) { log.info("We received a ProposalPayload and store it to our appendOnlyStoreList. proposalTxId={}", proposal.getTxId()); } diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/MissingDataRequestService.java b/core/src/main/java/bisq/core/dao/governance/voteresult/MissingDataRequestService.java index 6ee09711b15..8042ac5c735 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/MissingDataRequestService.java +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/MissingDataRequestService.java @@ -43,6 +43,7 @@ public class MissingDataRequestService implements DaoSetupService { private final BlindVoteListService blindVoteListService; private final ProposalService proposalService; private final P2PService p2PService; + private boolean reRepublishAllGovernanceDataDone; @Inject public MissingDataRequestService(RepublishGovernanceDataHandler republishGovernanceDataHandler, @@ -77,38 +78,44 @@ public void sendRepublishRequest() { republishGovernanceDataHandler.sendRepublishRequest(); } + // Can be triggered with shortcut ctrl+UP or alt+UP public void reRepublishAllGovernanceData() { - ObservableList proposalPayloads = proposalService.getProposalPayloads(); - proposalPayloads.forEach(proposalPayload -> { - // We want a random delay between 0.1 and 30 sec. depending on the number of items - int delay = Math.max(100, Math.min(30_000, new Random().nextInt(proposalPayloads.size() * 500))); - UserThread.runAfter(() -> { - boolean success = p2PService.addPersistableNetworkPayload(proposalPayload, true); - String txId = proposalPayload.getProposal().getTxId(); - if (success) { - log.debug("We received a RepublishGovernanceDataRequest and re-published a proposalPayload to " + - "the P2P network as append only data. proposalTxId={}", txId); - } else { - log.error("Adding of proposalPayload to P2P network failed. proposalTxId={}", txId); - } - }, delay, TimeUnit.MILLISECONDS); - }); - - ObservableList blindVotePayloads = blindVoteListService.getBlindVotePayloads(); - blindVotePayloads - .forEach(blindVotePayload -> { - // We want a random delay between 0.1 and 30 sec. depending on the number of items - int delay = Math.max(100, Math.min(30_000, new Random().nextInt(blindVotePayloads.size() * 500))); - UserThread.runAfter(() -> { - boolean success = p2PService.addPersistableNetworkPayload(blindVotePayload, true); - String txId = blindVotePayload.getBlindVote().getTxId(); - if (success) { - log.debug("We received a RepublishGovernanceDataRequest and re-published a blindVotePayload to " + - "the P2P network as append only data. blindVoteTxId={}", txId); - } else { - log.error("Adding of blindVotePayload to P2P network failed. blindVoteTxId={}", txId); - } - }, delay, TimeUnit.MILLISECONDS); - }); + // We only want to do it once in case we would get flooded with requests. + if (!reRepublishAllGovernanceDataDone) { + reRepublishAllGovernanceDataDone = true; + ObservableList proposalPayloads = proposalService.getProposalPayloads(); + proposalPayloads.forEach(proposalPayload -> { + // We want a random delay between 0.1 and 300 sec. depending on the number of items. + // We send all proposals including those from old cycles. + int delay = Math.max(100, Math.min(300_000, new Random().nextInt(proposalPayloads.size() * 1000))); + UserThread.runAfter(() -> { + boolean success = p2PService.addPersistableNetworkPayload(proposalPayload, true); + String txId = proposalPayload.getProposal().getTxId(); + if (success) { + log.debug("We received a RepublishGovernanceDataRequest and re-published a proposalPayload to " + + "the P2P network as append only data. proposalTxId={}", txId); + } else { + log.error("Adding of proposalPayload to P2P network failed. proposalTxId={}", txId); + } + }, delay, TimeUnit.MILLISECONDS); + }); + + ObservableList blindVotePayloads = blindVoteListService.getBlindVotePayloads(); + blindVotePayloads.forEach(blindVotePayload -> { + // We want a random delay between 0.1 and 300 sec. depending on the number of items. + // We send all blindVotes including those from old cycles. + int delay = Math.max(100, Math.min(300_000, new Random().nextInt(blindVotePayloads.size() * 1000))); + UserThread.runAfter(() -> { + boolean success = p2PService.addPersistableNetworkPayload(blindVotePayload, true); + String txId = blindVotePayload.getBlindVote().getTxId(); + if (success) { + log.debug("We received a RepublishGovernanceDataRequest and re-published a blindVotePayload to " + + "the P2P network as append only data. blindVoteTxId={}", txId); + } else { + log.error("Adding of blindVotePayload to P2P network failed. blindVoteTxId={}", txId); + } + }, delay, TimeUnit.MILLISECONDS); + }); + } } } diff --git a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java index 4caca5f5230..b43848a378b 100644 --- a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java +++ b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java @@ -41,6 +41,7 @@ import bisq.network.p2p.P2PService; +import bisq.common.UserThread; import bisq.common.util.Utilities; import org.bitcoinj.core.InsufficientMoneyException; @@ -285,12 +286,18 @@ private Transaction getVoteRevealTx(TxOutput stakeTxOutput, byte[] opReturnData) } private void rePublishBlindVotePayloadList(List blindVoteList) { + // If we have 20 blind votes from 20 voters we would have 400 messages sent to their 10 neighbor peers. + // Most of the neighbors will already have the data so they will not continue broadcast. + // To not flood the network too much we use a long random delay to spread the load over 5 minutes. + // As this is only for extra resilience we don't care so much for the case that the user might shut down the + // app before we are finished with our delayed broadcast. + // We cannot set reBroadcast to false as otherwise it would not have any effect as we have the data already and + // broadcast would only be triggered at new data. blindVoteList.stream() .map(BlindVotePayload::new) .forEach(blindVotePayload -> { - boolean success = p2PService.addPersistableNetworkPayload(blindVotePayload, true); - if (!success) - log.warn("publishToAppendOnlyDataStore failed for blindVote " + blindVotePayload.getBlindVote()); + UserThread.runAfterRandomDelay(() -> p2PService.addPersistableNetworkPayload(blindVotePayload, true), + 1, 300); }); } } From d5fc7cb97e9feba47112f7f7870f600845ca9db2 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 30 Mar 2019 19:35:12 -0500 Subject: [PATCH 42/55] Use burnedBsq field in Tx for burnedFee and invalidatedBsq In case of an invalid tx we burn all available BSQ input. We only know that at parsing time. We renamed the burntFee field to burntBsq to make it more generic and use it for the burnt fee in case if a normal tx and as invalidatedBsq in case of an invalid tx. --- common/src/main/proto/pb.proto | 2 +- .../main/java/bisq/core/dao/DaoFacade.java | 11 +++++-- .../monitoring/DaoStateMonitoringService.java | 5 ++- .../node/explorer/ExportJsonFilesService.java | 6 ++-- .../bisq/core/dao/node/explorer/JsonTx.java | 22 +++++++++++-- .../core/dao/node/explorer/JsonTxOutput.java | 31 ++++++++++++++++++- .../bisq/core/dao/node/parser/TempTx.java | 17 ++++------ .../bisq/core/dao/node/parser/TxParser.java | 6 ++-- .../bisq/core/dao/state/DaoStateService.java | 27 ++++------------ .../core/dao/state/model/blockchain/Tx.java | 31 +++++++++++++------ .../economy/dashboard/BsqDashboardView.java | 9 +++--- .../main/dao/economy/supply/SupplyView.java | 27 ++++++---------- .../transactions/BSQTransactionsView.java | 12 +++---- 13 files changed, 121 insertions(+), 85 deletions(-) diff --git a/common/src/main/proto/pb.proto b/common/src/main/proto/pb.proto index 2b9cf09813c..65afa4df9f8 100644 --- a/common/src/main/proto/pb.proto +++ b/common/src/main/proto/pb.proto @@ -1420,7 +1420,7 @@ message Tx { // Because of the way how PB implements inheritence we need to use the super class as type repeated BaseTxOutput tx_outputs = 1; TxType txType = 2; - int64 burnt_fee = 3; + int64 burnt_bsq = 3; } enum TxType { diff --git a/core/src/main/java/bisq/core/dao/DaoFacade.java b/core/src/main/java/bisq/core/dao/DaoFacade.java index a3c5cc65278..26d1415c03f 100644 --- a/core/src/main/java/bisq/core/dao/DaoFacade.java +++ b/core/src/main/java/bisq/core/dao/DaoFacade.java @@ -544,8 +544,13 @@ public long getTotalAmountOfConfiscatedTxOutputs() { return daoStateService.getTotalAmountOfConfiscatedTxOutputs(); } - public long getBurnedBsqOfAllInvalidTxs() { - return daoStateService.getBurnedBsqOfAllInvalidTxs(); + public long getTotalAmountOfInvalidatedBsq() { + return daoStateService.getTotalAmountOfInvalidatedBsq(); + } + + // Contains burned fee and invalidated bsq due invalid txs + public long getTotalAmountOfBurntBsq() { + return daoStateService.getTotalAmountOfBurntBsq(); } public List getInvalidTxs() { @@ -603,7 +608,7 @@ public int getNumIssuanceTransactions(IssuanceType issuanceType) { return daoStateService.getIssuanceSet(issuanceType).size(); } - public Set getFeeTxs() { + public Set getBurntFeeTxs() { return daoStateService.getBurntFeeTxs(); } diff --git a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java index 5abf8629798..41533981efd 100644 --- a/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java +++ b/core/src/main/java/bisq/core/dao/monitoring/DaoStateMonitoringService.java @@ -145,13 +145,12 @@ public void onParseBlockChainComplete() { @Override public void onDaoStateChanged(Block block) { long genesisTotalSupply = daoStateService.getGenesisTotalSupply().value; - long totalBurntFee = daoStateService.getTotalBurntFee(); long compensationIssuance = daoStateService.getTotalIssuedAmount(IssuanceType.COMPENSATION); long reimbursementIssuance = daoStateService.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT); - long totalInvalidAmount = daoStateService.getBurnedBsqOfAllInvalidTxs(); + long totalAmountOfBurntBsq = daoStateService.getTotalAmountOfBurntBsq(); // confiscated funds are still in the utxo set long sumUtxo = daoStateService.getUnspentTxOutputMap().values().stream().mapToLong(BaseTxOutput::getValue).sum(); - long sumBsq = genesisTotalSupply + compensationIssuance + reimbursementIssuance - totalBurntFee - totalInvalidAmount; + long sumBsq = genesisTotalSupply + compensationIssuance + reimbursementIssuance - totalAmountOfBurntBsq; if (sumBsq != sumUtxo) { utxoMismatches.add(new UtxoMismatch(block.getHeight(), sumUtxo, sumBsq)); diff --git a/core/src/main/java/bisq/core/dao/node/explorer/ExportJsonFilesService.java b/core/src/main/java/bisq/core/dao/node/explorer/ExportJsonFilesService.java index 712d32a4c64..d747395653f 100644 --- a/core/src/main/java/bisq/core/dao/node/explorer/ExportJsonFilesService.java +++ b/core/src/main/java/bisq/core/dao/node/explorer/ExportJsonFilesService.java @@ -193,7 +193,8 @@ private JsonTx getJsonTx(Tx tx) { getJsonTxOutputs(tx), jsonTxType, jsonTxTypeDisplayString, - daoStateService.getBurntFee(tx.getId()), + tx.getBurntFee(), + tx.getInvalidatedBsq(), tx.getUnlockBlockHeight()); } @@ -239,7 +240,8 @@ private List getJsonTxOutputs(Tx tx) { btcAmount, tx.getBlockHeight(), isBsqTxOutputType, - daoStateService.getBurntFee(tx.getId()), + tx.getBurntFee(), + tx.getInvalidatedBsq(), txOutput.getAddress(), scriptPubKey, spentInfo, diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonTx.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonTx.java index a23ff2413d1..145826bf60a 100644 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonTx.java +++ b/core/src/main/java/bisq/core/dao/node/explorer/JsonTx.java @@ -36,9 +36,26 @@ class JsonTx { private final JsonTxType txType; private final String txTypeDisplayString; private final long burntFee; + private final long invalidatedBsq; // If not set it is -1. LockTime of 0 is a valid value. private final int unlockBlockHeight; + JsonTx(String id, int blockHeight, String blockHash, long time, List inputs, + List outputs, JsonTxType txType, String txTypeDisplayString, long burntFee, + long invalidatedBsq, int unlockBlockHeight) { + this.id = id; + this.blockHeight = blockHeight; + this.blockHash = blockHash; + this.time = time; + this.inputs = inputs; + this.outputs = outputs; + this.txType = txType; + this.txTypeDisplayString = txTypeDisplayString; + this.burntFee = burntFee; + this.invalidatedBsq = invalidatedBsq; + this.unlockBlockHeight = unlockBlockHeight; + } + // Enums must not be used directly for hashCode or equals as it delivers the Object.hashCode (internal address)! // The equals and hashCode methods cannot be overwritten in Enums. @Override @@ -50,6 +67,7 @@ public boolean equals(Object o) { return blockHeight == jsonTx.blockHeight && time == jsonTx.time && burntFee == jsonTx.burntFee && + invalidatedBsq == jsonTx.invalidatedBsq && unlockBlockHeight == jsonTx.unlockBlockHeight && Objects.equals(txVersion, jsonTx.txVersion) && Objects.equals(id, jsonTx.id) && @@ -62,7 +80,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - - return Objects.hash(super.hashCode(), txVersion, id, blockHeight, blockHash, time, inputs, outputs, txType.name(), txTypeDisplayString, burntFee, unlockBlockHeight); + return Objects.hash(super.hashCode(), txVersion, id, blockHeight, blockHash, time, inputs, outputs, + txType.name(), txTypeDisplayString, burntFee, invalidatedBsq, unlockBlockHeight); } } diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutput.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutput.java index a2cd3ea4b3e..8d6dfc4e76a 100644 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutput.java +++ b/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutput.java @@ -35,6 +35,7 @@ class JsonTxOutput { private final int height; private final boolean isVerified; // isBsqTxOutputType private final long burntFee; + private final long invalidatedBsq; private final String address; @Nullable private final JsonScriptPubKey scriptPubKey; @@ -50,6 +51,31 @@ class JsonTxOutput { private final int lockTime; private final boolean isUnspent; + JsonTxOutput(String txId, int index, long bsqAmount, long btcAmount, int height, boolean isVerified, long burntFee, + long invalidatedBsq, String address, JsonScriptPubKey scriptPubKey, JsonSpentInfo spentInfo, + long time, JsonTxType txType, String txTypeDisplayString, JsonTxOutputType txOutputType, + String txOutputTypeDisplayString, String opReturn, int lockTime, boolean isUnspent) { + this.txId = txId; + this.index = index; + this.bsqAmount = bsqAmount; + this.btcAmount = btcAmount; + this.height = height; + this.isVerified = isVerified; + this.burntFee = burntFee; + this.invalidatedBsq = invalidatedBsq; + this.address = address; + this.scriptPubKey = scriptPubKey; + this.spentInfo = spentInfo; + this.time = time; + this.txType = txType; + this.txTypeDisplayString = txTypeDisplayString; + this.txOutputType = txOutputType; + this.txOutputTypeDisplayString = txOutputTypeDisplayString; + this.opReturn = opReturn; + this.lockTime = lockTime; + this.isUnspent = isUnspent; + } + String getId() { return txId + ":" + index; } @@ -68,6 +94,7 @@ public boolean equals(Object o) { height == that.height && isVerified == that.isVerified && burntFee == that.burntFee && + invalidatedBsq == that.invalidatedBsq && time == that.time && lockTime == that.lockTime && isUnspent == that.isUnspent && @@ -86,6 +113,8 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(super.hashCode(), txVersion, txId, index, bsqAmount, btcAmount, height, isVerified, burntFee, address, scriptPubKey, spentInfo, time, txType.name(), txTypeDisplayString, txOutputType, txOutputTypeDisplayString, opReturn, lockTime, isUnspent); + return Objects.hash(super.hashCode(), txVersion, txId, index, bsqAmount, btcAmount, height, isVerified, + burntFee, invalidatedBsq, address, scriptPubKey, spentInfo, time, txType.name(), txTypeDisplayString, + txOutputType, txOutputTypeDisplayString, opReturn, lockTime, isUnspent); } } diff --git a/core/src/main/java/bisq/core/dao/node/parser/TempTx.java b/core/src/main/java/bisq/core/dao/node/parser/TempTx.java index a2e37ff2f22..67d6c1c593a 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/TempTx.java +++ b/core/src/main/java/bisq/core/dao/node/parser/TempTx.java @@ -55,12 +55,7 @@ static TempTx fromRawTx(RawTx rawTx) { // Mutable data @Nullable private TxType txType; - private long burntFee; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// + private long burntBsq; private TempTx(String txVersion, String id, @@ -70,7 +65,7 @@ private TempTx(String txVersion, ImmutableList txInputs, ImmutableList tempTxOutputs, @Nullable TxType txType, - long burntFee) { + long burntBsq) { super(txVersion, id, blockHeight, @@ -79,7 +74,7 @@ private TempTx(String txVersion, txInputs); this.tempTxOutputs = tempTxOutputs; this.txType = txType; - this.burntFee = burntFee; + this.burntBsq = burntBsq; } @Override @@ -87,7 +82,7 @@ public String toString() { return "TempTx{" + "\n txOutputs=" + tempTxOutputs + ",\n txType=" + txType + - ",\n burntFee=" + burntFee + + ",\n burntBsq=" + burntBsq + "\n} " + super.toString(); } @@ -103,13 +98,13 @@ public boolean equals(Object o) { String name = txType != null ? txType.name() : ""; String name1 = tempTx.txType != null ? tempTx.txType.name() : ""; boolean isTxTypeEquals = name.equals(name1); - return burntFee == tempTx.burntFee && + return burntBsq == tempTx.burntBsq && Objects.equals(tempTxOutputs, tempTx.tempTxOutputs) && isTxTypeEquals; } @Override public int hashCode() { - return Objects.hash(super.hashCode(), tempTxOutputs, txType, burntFee); + return Objects.hash(super.hashCode(), tempTxOutputs, txType, burntBsq); } } diff --git a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java index aa92f20dc6a..ef700aabf03 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java +++ b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java @@ -142,7 +142,7 @@ private Optional findTx(RawTx rawTx) { long burntBsq = remainingInputValue + burntBondValue; boolean hasBurntBsq = burntBsq > 0; if (hasBurntBsq) - tempTx.setBurntFee(burntBsq); + tempTx.setBurntBsq(burntBsq); //**************************************************************************************** @@ -161,9 +161,7 @@ private Optional findTx(RawTx rawTx) { if (isTxInvalid(tempTx, bsqOutputFound, hasBurntBond)) { tempTx.setTxType(TxType.INVALID); // We consider all BSQ inputs as burned if the tx is invalid. - // It might be that the invalid tx had a BSQ fee. To avoid that we count the burned BSQ twice we set the - // burnedFee to 0. - tempTx.setBurntFee(0); + tempTx.setBurntBsq(accumulatedInputValue); txOutputParser.invalidateUTXOCandidates(); log.warn("We have destroyed BSQ because of an invalid tx. Burned BSQ={}. tx={}", accumulatedInputValue / 100D, tempTx); } else if (txType == TxType.IRREGULAR) { diff --git a/core/src/main/java/bisq/core/dao/state/DaoStateService.java b/core/src/main/java/bisq/core/dao/state/DaoStateService.java index 12030a3cb0e..d3c3b9f203d 100644 --- a/core/src/main/java/bisq/core/dao/state/DaoStateService.java +++ b/core/src/main/java/bisq/core/dao/state/DaoStateService.java @@ -21,7 +21,6 @@ import bisq.core.dao.governance.bond.BondConsensus; import bisq.core.dao.governance.param.Param; import bisq.core.dao.state.model.DaoState; -import bisq.core.dao.state.model.blockchain.BaseTxOutput; import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.model.blockchain.SpentInfo; import bisq.core.dao.state.model.blockchain.Tx; @@ -387,9 +386,7 @@ public boolean hasTxBurntFee(String txId) { } public long getTotalBurntFee() { - return getTxStream() - .mapToLong(Tx::getBurntFee) - .sum(); + return getTxStream().mapToLong(Tx::getBurntFee).sum(); } public Set getBurntFeeTxs() { @@ -816,25 +813,13 @@ public long getTotalAmountOfConfiscatedTxOutputs() { .sum(); } - public long getBurnedBsqOfAllInvalidTxs() { - return getTxStream() - .filter(e -> e.getTxType() == TxType.INVALID) - .mapToLong(this::getBurnedBsqOfInvalidTx) - .sum(); + public long getTotalAmountOfInvalidatedBsq() { + return getTxStream().mapToLong(Tx::getInvalidatedBsq).sum(); } - public long getBurnedBsqOfInvalidTx(Tx tx) { - return tx.getTxInputs().stream() - .map(TxInput::getConnectedTxOutputKey) - .flatMap(txOutputKey -> getTxOutput(txOutputKey).stream()) - .filter(txOutput -> txOutput.getTxOutputType() == TxOutputType.GENESIS_OUTPUT || - txOutput.getTxOutputType() == TxOutputType.BSQ_OUTPUT || - txOutput.getTxOutputType() == TxOutputType.BLIND_VOTE_LOCK_STAKE_OUTPUT || - txOutput.getTxOutputType() == TxOutputType.VOTE_REVEAL_UNLOCK_STAKE_OUTPUT || - txOutput.getTxOutputType() == TxOutputType.LOCKUP_OUTPUT || - txOutput.getTxOutputType() == TxOutputType.UNLOCK_OUTPUT) - .mapToLong(BaseTxOutput::getValue) - .sum(); + // Contains burnt fee and invalidated bsq due invalid txs + public long getTotalAmountOfBurntBsq() { + return getTxStream().mapToLong(Tx::getBurntBsq).sum(); } // Confiscate bond diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/Tx.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/Tx.java index 16365306113..63ca99189a5 100644 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/Tx.java +++ b/core/src/main/java/bisq/core/dao/state/model/blockchain/Tx.java @@ -57,13 +57,14 @@ public static Tx fromTempTx(TempTx tempTx) { tempTx.getTxInputs(), txOutputs, tempTx.getTxType(), - tempTx.getBurntFee()); + tempTx.getBurntBsq()); } private final ImmutableList txOutputs; @Nullable private final TxType txType; - private final long burntFee; + // Can be burned fee or in case of an invalid tx the burned BSQ from all BSQ inputs + private final long burntBsq; /////////////////////////////////////////////////////////////////////////////////////////// @@ -78,7 +79,7 @@ private Tx(String txVersion, ImmutableList txInputs, ImmutableList txOutputs, @Nullable TxType txType, - long burntFee) { + long burntBsq) { super(txVersion, id, blockHeight, @@ -87,7 +88,7 @@ private Tx(String txVersion, txInputs); this.txOutputs = txOutputs; this.txType = txType; - this.burntFee = burntFee; + this.burntBsq = burntBsq; } @@ -97,7 +98,7 @@ public PB.BaseTx toProtoMessage() { .addAllTxOutputs(txOutputs.stream() .map(TxOutput::toProtoMessage) .collect(Collectors.toList())) - .setBurntFee(burntFee); + .setBurntBsq(burntBsq); Optional.ofNullable(txType).ifPresent(txType -> builder.setTxType(txType.toProtoMessage())); return getBaseTxBuilder().setTx(builder).build(); } @@ -122,7 +123,7 @@ public static Tx fromProto(PB.BaseTx protoBaseTx) { txInputs, outputs, TxType.fromProto(protoTx.getTxType()), - protoTx.getBurntFee()); + protoTx.getBurntBsq()); } @@ -135,6 +136,18 @@ public TxOutput getLastTxOutput() { } + public long getBurntBsq() { + return burntBsq; + } + + public long getBurntFee() { + return txType == TxType.INVALID ? 0 : burntBsq; + } + + public long getInvalidatedBsq() { + return txType == TxType.INVALID ? burntBsq : 0; + } + public int getLockTime() { return getLockupOutput().getLockTime(); } @@ -158,7 +171,7 @@ public String toString() { return "Tx{" + "\n txOutputs=" + txOutputs + ",\n txType=" + txType + - ",\n burntFee=" + burntFee + + ",\n burntBsq=" + burntBsq + "\n} " + super.toString(); } @@ -175,13 +188,13 @@ public boolean equals(Object o) { String name1 = tx.txType != null ? tx.txType.name() : ""; boolean isTxTypeEquals = name.equals(name1); - return burntFee == tx.burntFee && + return burntBsq == tx.burntBsq && Objects.equals(txOutputs, tx.txOutputs) && isTxTypeEquals; } @Override public int hashCode() { - return Objects.hash(super.hashCode(), txOutputs, txType, burntFee); + return Objects.hash(super.hashCode(), txOutputs, txType, burntBsq); } } diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java index e884e6d7cda..0de2e7e6252 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/economy/dashboard/BsqDashboardView.java @@ -291,16 +291,15 @@ private void updateWithBsqBlockChainData() { Coin issuedAmountFromGenesis = daoFacade.getGenesisTotalSupply(); Coin issuedAmountFromCompRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.COMPENSATION)); Coin issuedAmountFromReimbursementRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT)); - Coin burntFee = Coin.valueOf(daoFacade.getTotalBurntFee()); Coin totalConfiscatedAmount = Coin.valueOf(daoFacade.getTotalAmountOfConfiscatedTxOutputs()); - Coin burnedBsqOfAllInvalidTxs = Coin.valueOf(daoFacade.getBurnedBsqOfAllInvalidTxs()); + // Contains burnt fee and invalidated bsq due invalid txs + Coin totalAmountOfBurntBsq = Coin.valueOf(daoFacade.getTotalAmountOfBurntBsq()); availableAmount = issuedAmountFromGenesis .add(issuedAmountFromCompRequests) .add(issuedAmountFromReimbursementRequests) - .subtract(burntFee) - .subtract(totalConfiscatedAmount) - .subtract(burnedBsqOfAllInvalidTxs); + .subtract(totalAmountOfBurntBsq) + .subtract(totalConfiscatedAmount); availableAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(availableAmount)); } diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/SupplyView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/SupplyView.java index a339f4b18c5..43b0b77b033 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/SupplyView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/economy/supply/SupplyView.java @@ -27,7 +27,6 @@ import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.model.blockchain.Tx; -import bisq.core.dao.state.model.blockchain.TxType; import bisq.core.dao.state.model.governance.Issuance; import bisq.core.dao.state.model.governance.IssuanceType; import bisq.core.locale.GlobalSettings; @@ -90,8 +89,8 @@ public class SupplyView extends ActivatableView implements DaoSt private int gridRow = 0; private TextField genesisIssueAmountTextField, compRequestIssueAmountTextField, reimbursementAmountTextField, - burntAmountTextField, totalLockedUpAmountTextField, totalUnlockingAmountTextField, - totalUnlockedAmountTextField, totalConfiscatedAmountTextField, burnedBsqOfAllInvalidTxsTextField; + totalBurntFeeAmountTextField, totalLockedUpAmountTextField, totalUnlockingAmountTextField, + totalUnlockedAmountTextField, totalConfiscatedAmountTextField, totalAmountOfInvalidatedBsqTextField; private XYChart.Series seriesBSQIssued, seriesBSQBurnt; private static final Map ADJUSTERS = new HashMap<>(); @@ -168,9 +167,9 @@ private void createSupplyIncreasedInformation() { private void createSupplyReducedInformation() { addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.factsAndFigures.supply.burnt"), Layout.GROUP_DISTANCE); - burntAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, + totalBurntFeeAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.factsAndFigures.supply.burntAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - burnedBsqOfAllInvalidTxsTextField = addTopLabelReadOnlyTextField(root, gridRow, 1, + totalAmountOfInvalidatedBsqTextField = addTopLabelReadOnlyTextField(root, gridRow, 1, Res.get("dao.factsAndFigures.supply.invalidTxs"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; seriesBSQBurnt = new XYChart.Series<>(); @@ -272,20 +271,20 @@ private void updateWithBsqBlockChainData() { Coin issuedAmountFromReimbursementRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT)); reimbursementAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(issuedAmountFromReimbursementRequests)); - Coin burntFee = Coin.valueOf(daoFacade.getTotalBurntFee()); + Coin totalBurntFee = Coin.valueOf(daoFacade.getTotalBurntFee()); Coin totalLockedUpAmount = Coin.valueOf(daoFacade.getTotalLockupAmount()); Coin totalUnlockingAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockingTxOutputs()); Coin totalUnlockedAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockedTxOutputs()); Coin totalConfiscatedAmount = Coin.valueOf(daoFacade.getTotalAmountOfConfiscatedTxOutputs()); - Coin burnedBsqOfAllInvalidTxs = Coin.valueOf(daoFacade.getBurnedBsqOfAllInvalidTxs()); + Coin totalAmountOfInvalidatedBsq = Coin.valueOf(daoFacade.getTotalAmountOfInvalidatedBsq()); - burntAmountTextField.setText("-" + bsqFormatter.formatAmountWithGroupSeparatorAndCode(burntFee)); + totalBurntFeeAmountTextField.setText("-" + bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalBurntFee)); totalLockedUpAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalLockedUpAmount)); totalUnlockingAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockingAmount)); totalUnlockedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockedAmount)); totalConfiscatedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalConfiscatedAmount)); - String minusSign = burnedBsqOfAllInvalidTxs.isPositive() ? "-" : ""; - burnedBsqOfAllInvalidTxsTextField.setText(minusSign + bsqFormatter.formatAmountWithGroupSeparatorAndCode(burnedBsqOfAllInvalidTxs)); + String minusSign = totalAmountOfInvalidatedBsq.isPositive() ? "-" : ""; + totalAmountOfInvalidatedBsqTextField.setText(minusSign + bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalAmountOfInvalidatedBsq)); updateCharts(); } @@ -307,13 +306,7 @@ private void updateCharts() { ZonedDateTime zonedDateTime = date.atStartOfDay(ZoneId.systemDefault()); return new XYChart.Data(zonedDateTime.toInstant().getEpochSecond(), burntBsqByMonth.get(date) .stream() - .mapToDouble(tx -> { - if (tx.getTxType() == TxType.INVALID) { - return daoStateService.getBurnedBsqOfInvalidTx(tx); - } else { - return tx.getBurntFee(); - } - }) + .mapToDouble(Tx::getBurntBsq) .sum() ); }) diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java index c51ee6cdb8a..a39dfb1102d 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java @@ -51,10 +51,10 @@ public class BSQTransactionsView extends ActivatableView impleme private final Preferences preferences; private int gridRow = 0; - private TextField allTxTextField, burntTxTextField, + private TextField allTxTextField, burntFeeTxsTextField, utxoTextField, compensationIssuanceTxTextField, reimbursementIssuanceTxTextField, - invalidTxTextField; + invalidTxsTextField; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor, lifecycle @@ -107,9 +107,9 @@ public void initialize() { reimbursementIssuanceTxTextField = addTopLabelReadOnlyTextField(root, gridRow, columnIndex, Res.get("dao.factsAndFigures.transactions.reimbursementIssuanceTx"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - burntTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, + burntFeeTxsTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.factsAndFigures.transactions.burntTx")).second; - invalidTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, + invalidTxsTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.factsAndFigures.transactions.invalidTx")).second; } @@ -145,8 +145,8 @@ private void updateWithBsqBlockChainData() { utxoTextField.setText(String.valueOf(daoFacade.getUnspentTxOutputs().size())); compensationIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.COMPENSATION))); reimbursementIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.REIMBURSEMENT))); - burntTxTextField.setText(String.valueOf(daoFacade.getFeeTxs().size())); - invalidTxTextField.setText(String.valueOf(daoFacade.getInvalidTxs().size())); + burntFeeTxsTextField.setText(String.valueOf(daoFacade.getBurntFeeTxs().size())); + invalidTxsTextField.setText(String.valueOf(daoFacade.getInvalidTxs().size())); } } From fe646e5436ea096f2373ab1e6054c39c5e8711ad Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 31 Mar 2019 13:37:55 -0500 Subject: [PATCH 43/55] Add string validations --- .../bisq/core/dao/governance/proposal/ProposalValidator.java | 5 +++++ .../proposal/confiscatebond/ConfiscateBondValidator.java | 2 ++ .../dao/governance/proposal/param/ChangeParamValidator.java | 1 + .../proposal/removeAsset/RemoveAssetValidator.java | 5 +++++ 4 files changed, 13 insertions(+) diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java index 66acd772ec3..d303246d944 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java @@ -32,6 +32,7 @@ import lombok.extern.slf4j.Slf4j; +import static com.google.common.base.Preconditions.checkArgument; import static org.apache.commons.lang3.Validate.notEmpty; /** @@ -61,6 +62,10 @@ public void validateDataFields(Proposal proposal) throws ProposalValidationExcep try { notEmpty(proposal.getName(), "name must not be empty"); notEmpty(proposal.getLink(), "link must not be empty"); + checkArgument(proposal.getName().length() <= 200, "Name must not exceed 200 chars"); + checkArgument(proposal.getLink().length() <= 200, "Link must not exceed 200 chars"); + if (proposal.getTxId() != null) + checkArgument(proposal.getTxId().length() == 64, "Tx ID must be 64 chars"); } catch (Throwable throwable) { throw new ProposalValidationException(throwable); } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondValidator.java index dd6d68225db..02124b7949f 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/confiscatebond/ConfiscateBondValidator.java @@ -29,6 +29,7 @@ import lombok.extern.slf4j.Slf4j; +import static com.google.common.base.Preconditions.checkArgument; import static org.apache.commons.lang3.Validate.notEmpty; /** @@ -48,6 +49,7 @@ public void validateDataFields(Proposal proposal) throws ProposalValidationExcep super.validateDataFields(proposal); ConfiscateBondProposal confiscateBondProposal = (ConfiscateBondProposal) proposal; notEmpty(confiscateBondProposal.getLockupTxId(), "LockupTxId must not be empty"); + checkArgument(confiscateBondProposal.getLockupTxId().length() == 64, "LockupTxId must be 64 chars"); } catch (ProposalValidationException e) { throw e; } catch (Throwable throwable) { diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamValidator.java index 8e86d9976a1..e209bc204f7 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/param/ChangeParamValidator.java @@ -65,6 +65,7 @@ public void validateDataFields(Proposal proposal) throws ProposalValidationExcep super.validateDataFields(proposal); ChangeParamProposal changeParamProposal = (ChangeParamProposal) proposal; validateParamValue(changeParamProposal.getParam(), changeParamProposal.getParamValue(), getBlockHeight(proposal)); + checkArgument(changeParamProposal.getParamValue().length() <= 200, "ParamValue must not exceed 200 chars"); } catch (ProposalValidationException e) { throw e; } catch (Throwable throwable) { diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java index 9a6f59d7c2f..49b780ee904 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java @@ -29,6 +29,7 @@ import lombok.extern.slf4j.Slf4j; +import static com.google.common.base.Preconditions.checkArgument; import static org.apache.commons.lang3.Validate.notEmpty; /** @@ -49,6 +50,10 @@ public void validateDataFields(Proposal proposal) throws ProposalValidationExcep RemoveAssetProposal removeAssetProposal = (RemoveAssetProposal) proposal; notEmpty(removeAssetProposal.getTickerSymbol(), "TickerSymbol must not be empty"); + + // Usually ticker is very short but our protection intention here is mainly to avoid that someone cause + // damage by inserting a super long string. + checkArgument(removeAssetProposal.getTickerSymbol().length() <= 100, "TickerSymbol must not exceed 100 chars"); } catch (ProposalValidationException e) { throw e; } catch (Throwable throwable) { From e624625bbb048b81661fd8a17baf2cf03670c075 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 31 Mar 2019 15:46:09 -0500 Subject: [PATCH 44/55] Fix missing close handler and avoid nullPointer exception --- .../bisq/desktop/main/dao/governance/ProposalDisplay.java | 4 ++-- .../desktop/main/overlays/windows/ProposalResultsWindow.java | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java index 8037f0b00ed..da3d883c7a9 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/ProposalDisplay.java @@ -144,7 +144,7 @@ public class ProposalDisplay { private VBox linkWithIconContainer, comboBoxValueContainer, myVoteBox, voteResultBox; private int votingBoxRowSpan; - private Optional navigateHandlerOptional; + private Optional navigateHandlerOptional = Optional.empty(); public ProposalDisplay(GridPane gridPane, BsqFormatter bsqFormatter, @@ -647,7 +647,7 @@ public void removeAllFields() { } public void onNavigate(Runnable navigateHandler) { - this.navigateHandlerOptional = Optional.of(navigateHandler); + navigateHandlerOptional = Optional.of(navigateHandler); } public int incrementAndGetGridRow() { diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/ProposalResultsWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/ProposalResultsWindow.java index 494a3d29244..e557ce8bdf9 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/ProposalResultsWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/ProposalResultsWindow.java @@ -134,6 +134,7 @@ private void addContent(EvaluatedProposal evaluatedProposal, Ballot ballot) { proposalDisplay.createAllFields("", rowIndex, -Layout.FIRST_ROW_DISTANCE, proposal.getType(), false, "last"); proposalDisplay.setEditable(false); + proposalDisplay.onNavigate(this::doClose); proposalDisplay.applyProposalPayload(proposal); proposalDisplay.applyEvaluatedProposal(evaluatedProposal); From 122bc80cdd8a72ada83dfd8092d6017f7d07d22c Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 31 Mar 2019 15:47:21 -0500 Subject: [PATCH 45/55] Use null instead of empty string for txId --- .../core/dao/state/model/governance/ChangeParamProposal.java | 2 +- .../core/dao/state/model/governance/CompensationProposal.java | 2 +- .../core/dao/state/model/governance/ConfiscateBondProposal.java | 2 +- .../bisq/core/dao/state/model/governance/GenericProposal.java | 2 +- .../core/dao/state/model/governance/ReimbursementProposal.java | 2 +- .../core/dao/state/model/governance/RemoveAssetProposal.java | 2 +- .../java/bisq/core/dao/state/model/governance/RoleProposal.java | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/ChangeParamProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/ChangeParamProposal.java index 895dd14fc31..b78decf2cf5 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/ChangeParamProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/ChangeParamProposal.java @@ -55,7 +55,7 @@ public ChangeParamProposal(String name, paramValue, Version.PROPOSAL, new Date().getTime(), - "", + null, extraDataMap); } diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/CompensationProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/CompensationProposal.java index 29e1812a652..45d749923f4 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/CompensationProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/CompensationProposal.java @@ -62,7 +62,7 @@ public CompensationProposal(String name, requestedBsq.value, Version.COMPENSATION_REQUEST, new Date().getTime(), - "", + null, extraDataMap); } diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/ConfiscateBondProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/ConfiscateBondProposal.java index 6d578b685da..38eb9ef320d 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/ConfiscateBondProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/ConfiscateBondProposal.java @@ -53,7 +53,7 @@ public ConfiscateBondProposal(String name, lockupTxId, Version.PROPOSAL, new Date().getTime(), - "", + null, extraDataMap); } diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/GenericProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/GenericProposal.java index 3963c539b48..92002f559f5 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/GenericProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/GenericProposal.java @@ -50,7 +50,7 @@ public GenericProposal(String name, link, Version.PROPOSAL, new Date().getTime(), - "", + null, extraDataMap); } diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/ReimbursementProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/ReimbursementProposal.java index b5b0bb91a4f..40146666101 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/ReimbursementProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/ReimbursementProposal.java @@ -62,7 +62,7 @@ public ReimbursementProposal(String name, requestedBsq.value, Version.REIMBURSEMENT_REQUEST, new Date().getTime(), - "", + null, extraDataMap); } diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/RemoveAssetProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/RemoveAssetProposal.java index 0b4ebe14b11..f4452e10163 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/RemoveAssetProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/RemoveAssetProposal.java @@ -53,7 +53,7 @@ public RemoveAssetProposal(String name, tickerSymbol, Version.PROPOSAL, new Date().getTime(), - "", + null, extraDataMap); } diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/RoleProposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/RoleProposal.java index bc783bec78e..c4caa7adb68 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/RoleProposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/RoleProposal.java @@ -54,7 +54,7 @@ public RoleProposal(Role role, Map extraDataMap) { role.getBondedRoleType().getUnlockTimeInBlocks(), Version.PROPOSAL, new Date().getTime(), - "", + null, extraDataMap); } From 73db81a34fc022bfc844a896ad4749d0361db509 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 31 Mar 2019 15:49:37 -0500 Subject: [PATCH 46/55] Add more validation - Check max length of strings and byte arrays - Check that tx ID has 64 chars - Add ExtraDataMapValidator for validating extraDataMap fields --- .../common/util/ExtraDataMapValidator.java | 75 +++++++++++++++++++ .../blindvote/BlindVoteValidator.java | 17 ++++- .../proposal/ProposalValidator.java | 4 + .../storage/temp/TempProposalPayload.java | 3 +- .../dao/state/model/governance/Proposal.java | 5 +- 5 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 common/src/main/java/bisq/common/util/ExtraDataMapValidator.java diff --git a/common/src/main/java/bisq/common/util/ExtraDataMapValidator.java b/common/src/main/java/bisq/common/util/ExtraDataMapValidator.java new file mode 100644 index 00000000000..d0f10bc5baf --- /dev/null +++ b/common/src/main/java/bisq/common/util/ExtraDataMapValidator.java @@ -0,0 +1,75 @@ +/* + * 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 . + */ + +package bisq.common.util; + +import java.util.HashMap; +import java.util.Map; + +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkArgument; + +/** + * Validator for extraDataMap fields used in network payloads. + * Ensures that we don't get the network attacked by huge data inserted there. + */ +@Slf4j +public class ExtraDataMapValidator { + // ExtraDataMap is only used for exceptional cases to not break backward compatibility. + // We don't expect many entries there. + public final static int MAX_SIZE = 10; + public final static int MAX_KEY_LENGTH = 100; + public final static int MAX_VALUE_LENGTH = 100000; // 100 kb + + public static Map getValidatedExtraDataMap(@Nullable Map extraDataMap) { + return getValidatedExtraDataMap(extraDataMap, MAX_SIZE, MAX_KEY_LENGTH, MAX_VALUE_LENGTH); + } + + public static Map getValidatedExtraDataMap(@Nullable Map extraDataMap, int maxSize, int maxKeyLength, int maxValueLength) { + if (extraDataMap == null) + return null; + + try { + checkArgument(extraDataMap.entrySet().size() <= maxSize, "Size of map must not exceed " + maxSize); + extraDataMap.forEach((key, value) -> { + checkArgument(key.length() <= maxKeyLength, "Length of key must not exceed " + maxKeyLength); + checkArgument(value.length() <= maxValueLength, "Length of value must not exceed " + maxValueLength); + }); + return extraDataMap; + } catch (Throwable t) { + return new HashMap<>(); + } + } + + public static void validate(@Nullable Map extraDataMap) { + validate(extraDataMap, MAX_SIZE, MAX_KEY_LENGTH, MAX_VALUE_LENGTH); + } + + public static void validate(@Nullable Map extraDataMap, int maxSize, int maxKeyLength, int maxValueLength) { + if (extraDataMap == null) + return; + + checkArgument(extraDataMap.entrySet().size() <= maxSize, "Size of map must not exceed " + maxSize); + extraDataMap.forEach((key, value) -> { + checkArgument(key.length() <= maxKeyLength, "Length of key must not exceed " + maxKeyLength); + checkArgument(value.length() <= maxValueLength, "Length of value must not exceed " + maxValueLength); + }); + } +} diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java index e16542887f4..4b1035a25e8 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java @@ -17,12 +17,15 @@ package bisq.core.dao.governance.blindvote; +import bisq.core.btc.wallet.Restrictions; import bisq.core.dao.governance.period.PeriodService; import bisq.core.dao.governance.proposal.ProposalValidationException; import bisq.core.dao.state.DaoStateService; import bisq.core.dao.state.model.blockchain.Tx; import bisq.core.dao.state.model.governance.DaoPhase; +import bisq.common.util.ExtraDataMapValidator; + import javax.inject.Inject; import java.util.Optional; @@ -58,12 +61,20 @@ private void validateDataFields(BlindVote blindVote) throws ProposalValidationEx checkNotNull(blindVote.getEncryptedVotes(), "encryptedProposalList must not be null"); checkArgument(blindVote.getEncryptedVotes().length > 0, "encryptedProposalList must not be empty"); - checkNotNull(blindVote.getTxId(), "txId must not be null"); - checkArgument(!blindVote.getTxId().isEmpty(), "txId must not be empty"); - checkArgument(blindVote.getStake() > 0, "stake must be positive"); + checkArgument(blindVote.getEncryptedVotes().length <= 100000, + "encryptedProposalList must not exceed 100kb"); + + checkNotNull(blindVote.getTxId(), "Tx ID must not be null"); + checkArgument(blindVote.getTxId().length() == 64, "Tx ID must be 64 chars"); + checkArgument(blindVote.getStake() >= Restrictions.getMinNonDustOutput().value, "Stake must be at least MinNonDustOutput"); + checkNotNull(blindVote.getEncryptedMeritList(), "getEncryptedMeritList must not be null"); checkArgument(blindVote.getEncryptedMeritList().length > 0, "getEncryptedMeritList must not be empty"); + checkArgument(blindVote.getEncryptedMeritList().length <= 100000, + "getEncryptedMeritList must not exceed 100kb"); + + ExtraDataMapValidator.validate(blindVote.getExtraDataMap()); } catch (Throwable e) { log.warn(e.toString()); throw new ProposalValidationException(e); diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java index d303246d944..e0f0118b1cc 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java @@ -28,6 +28,8 @@ import bisq.core.dao.state.model.governance.Proposal; import bisq.core.dao.state.model.governance.ReimbursementProposal; +import bisq.common.util.ExtraDataMapValidator; + import java.util.Optional; import lombok.extern.slf4j.Slf4j; @@ -66,6 +68,8 @@ public void validateDataFields(Proposal proposal) throws ProposalValidationExcep checkArgument(proposal.getLink().length() <= 200, "Link must not exceed 200 chars"); if (proposal.getTxId() != null) checkArgument(proposal.getTxId().length() == 64, "Tx ID must be 64 chars"); + + ExtraDataMapValidator.validate(proposal.getExtraDataMap()); } catch (Throwable throwable) { throw new ProposalValidationException(throwable); } diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java b/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java index 8ba16390920..38b993f1e6d 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java @@ -28,6 +28,7 @@ import bisq.common.app.Capability; import bisq.common.crypto.Sig; import bisq.common.proto.persistable.PersistablePayload; +import bisq.common.util.ExtraDataMapValidator; import io.bisq.generated.protobuffer.PB; @@ -88,7 +89,7 @@ private TempProposalPayload(Proposal proposal, @Nullable Map extraDataMap) { this.proposal = proposal; this.ownerPubKeyEncoded = ownerPubPubKeyEncoded; - this.extraDataMap = extraDataMap; + this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); ownerPubKey = Sig.getPublicKeyFromBytes(ownerPubKeyEncoded); } diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java index fd53adc226e..395b29c9597 100644 --- a/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java +++ b/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java @@ -26,6 +26,7 @@ import bisq.common.proto.ProtobufferRuntimeException; import bisq.common.proto.network.NetworkPayload; import bisq.common.proto.persistable.PersistablePayload; +import bisq.common.util.ExtraDataMapValidator; import io.bisq.generated.protobuffer.PB; @@ -64,13 +65,13 @@ protected Proposal(String name, byte version, long creationDate, @Nullable String txId, - @SuppressWarnings("NullableProblems") Map extraDataMap) { + @Nullable Map extraDataMap) { this.name = name; this.link = link; this.version = version; this.creationDate = creationDate; this.txId = txId; - this.extraDataMap = extraDataMap; + this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); } From c7bd2ee4b85ec150bad176160b1ca5efb33026a4 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 31 Mar 2019 15:55:55 -0500 Subject: [PATCH 47/55] Add BTC_DAO_TESTNET again to keep supporting current dao testnet - It is more safe to separate the BTC_DAO_TESTNET and BTC_DAO_REGTEST by the network ID as that prevents on the P2P network layer that the network could interconnect. We would have risked that we receive network data from the other network as users would use the persisted peers for connections. --- .../java/bisq/core/app/BisqEnvironment.java | 11 ++----- .../java/bisq/core/app/BisqExecutable.java | 2 +- .../main/java/bisq/core/app/BisqSetup.java | 2 +- .../bisq/core/btc/BaseCurrencyNetwork.java | 9 ++++-- .../java/bisq/core/btc/BitcoinModule.java | 8 +++-- .../bisq/core/btc/setup/WalletConfig.java | 2 +- .../bisq/core/dao/governance/param/Param.java | 28 ++++++++++++----- .../bisq/core/dao/node/full/RpcService.java | 2 -- .../bisq/core/dao/state/GenesisTxInfo.java | 31 +++++++++++++------ .../main/java/bisq/core/user/Preferences.java | 8 +++-- .../main/resources/btc_dao_regtest.seednodes | 8 ++--- .../main/resources/btc_dao_testnet.seednodes | 5 +++ .../settings/preferences/PreferencesView.java | 2 +- 13 files changed, 76 insertions(+), 42 deletions(-) create mode 100644 core/src/main/resources/btc_dao_testnet.seednodes diff --git a/core/src/main/java/bisq/core/app/BisqEnvironment.java b/core/src/main/java/bisq/core/app/BisqEnvironment.java index eb21b871800..e98faf4e658 100644 --- a/core/src/main/java/bisq/core/app/BisqEnvironment.java +++ b/core/src/main/java/bisq/core/app/BisqEnvironment.java @@ -361,14 +361,9 @@ public BisqEnvironment(PropertySource commandLineProperties) { final String bannedBtcNodesAsString = getProperty(FilterManager.BANNED_BTC_NODES, ""); bannedBtcNodes = !bannedBtcNodesAsString.isEmpty() ? Arrays.asList(StringUtils.deleteWhitespace(bannedBtcNodesAsString).split(",")) : null; - try { - // We replaced DAO_TESTNET with DAO_REGTEST. If user had DAO_TESTNET in his property file we - // get an exception and set the baseCurrencyNetwork to BTC_DAO_REGTEST - baseCurrencyNetwork = BaseCurrencyNetwork.valueOf(getProperty(BtcOptionKeys.BASE_CURRENCY_NETWORK, - getDefaultBaseCurrencyNetwork().name()).toUpperCase()); - } catch (Throwable t) { - baseCurrencyNetwork = BaseCurrencyNetwork.BTC_DAO_REGTEST; - } + baseCurrencyNetwork = BaseCurrencyNetwork.valueOf(getProperty(BtcOptionKeys.BASE_CURRENCY_NETWORK, + getDefaultBaseCurrencyNetwork().name()).toUpperCase()); + btcNetworkDir = Paths.get(appDataDir, baseCurrencyNetwork.name().toLowerCase()).toString(); File btcNetworkDirFile = new File(btcNetworkDir); if (!btcNetworkDirFile.exists()) diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java index 774bd9b1bd5..fcc3142e5c8 100644 --- a/core/src/main/java/bisq/core/app/BisqExecutable.java +++ b/core/src/main/java/bisq/core/app/BisqExecutable.java @@ -494,7 +494,7 @@ protected void customizeOptionParsing(OptionParser parser) { format("Base currency network (default: %s)", BisqEnvironment.getDefaultBaseCurrencyNetwork().name())) .withRequiredArg() .ofType(String.class) - .describedAs(format("%s|%s|%s|%s", BTC_MAINNET, BTC_TESTNET, BTC_REGTEST, BTC_DAO_REGTEST, BTC_DAO_BETANET)); + .describedAs(format("%s|%s|%s|%s", BTC_MAINNET, BTC_TESTNET, BTC_REGTEST, BTC_DAO_TESTNET, BTC_DAO_BETANET, BTC_DAO_REGTEST)); parser.accepts(BtcOptionKeys.REG_TEST_HOST, format("Bitcoin regtest host when using BTC_REGTEST network (default: %s)", RegTestHost.DEFAULT_HOST)) diff --git a/core/src/main/java/bisq/core/app/BisqSetup.java b/core/src/main/java/bisq/core/app/BisqSetup.java index 4e0ae4f971e..07668b9b241 100644 --- a/core/src/main/java/bisq/core/app/BisqSetup.java +++ b/core/src/main/java/bisq/core/app/BisqSetup.java @@ -418,7 +418,7 @@ private void maybeShowTac() { private void checkIfLocalHostNodeIsRunning() { // For DAO testnet we ignore local btc node - if (BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest()) { + if (BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest() || BisqEnvironment.getBaseCurrencyNetwork().isDaoTestNet()) { step3(); } else { Thread checkIfLocalHostNodeIsRunningThread = new Thread(() -> { diff --git a/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java b/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java index 01f25255988..c4468563299 100644 --- a/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java +++ b/core/src/main/java/bisq/core/btc/BaseCurrencyNetwork.java @@ -28,8 +28,9 @@ public enum BaseCurrencyNetwork { BTC_MAINNET(MainNetParams.get(), "BTC", "MAINNET", "Bitcoin"), BTC_TESTNET(TestNet3Params.get(), "BTC", "TESTNET", "Bitcoin"), BTC_REGTEST(RegTestParams.get(), "BTC", "REGTEST", "Bitcoin"), - BTC_DAO_REGTEST(RegTestParams.get(), "BTC", "REGTEST", "Bitcoin"), // server side regtest - BTC_DAO_BETANET(MainNetParams.get(), "BTC", "MAINNET", "Bitcoin"); // mainnet test genesis + BTC_DAO_TESTNET(RegTestParams.get(), "BTC", "REGTEST", "Bitcoin"), // server side regtest until v0.9.5 + BTC_DAO_BETANET(MainNetParams.get(), "BTC", "MAINNET", "Bitcoin"), // mainnet test genesis + BTC_DAO_REGTEST(RegTestParams.get(), "BTC", "REGTEST", "Bitcoin"); // server side regtest after v0.9.5, had breaking code changes so we started over again @Getter private final NetworkParameters parameters; @@ -55,6 +56,10 @@ public boolean isTestnet() { return "BTC_TESTNET".equals(name()); } + public boolean isDaoTestNet() { + return "BTC_DAO_TESTNET".equals(name()); + } + public boolean isDaoRegTest() { return "BTC_DAO_REGTEST".equals(name()); } diff --git a/core/src/main/java/bisq/core/btc/BitcoinModule.java b/core/src/main/java/bisq/core/btc/BitcoinModule.java index 8c1f87d4e67..6b888dff7d7 100644 --- a/core/src/main/java/bisq/core/btc/BitcoinModule.java +++ b/core/src/main/java/bisq/core/btc/BitcoinModule.java @@ -58,9 +58,11 @@ protected void configure() { // (localhost) String regTestHost = environment.getProperty(BtcOptionKeys.REG_TEST_HOST, String.class, ""); if (regTestHost.isEmpty()) { - regTestHost = BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest() ? - "134.209.242.206" : - RegTestHost.DEFAULT_HOST; + regTestHost = BisqEnvironment.getBaseCurrencyNetwork().isDaoTestNet() ? + "104.248.31.39" : + BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest() ? + "134.209.242.206" : + RegTestHost.DEFAULT_HOST; } RegTestHost.HOST = regTestHost; diff --git a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java index b114085fa7d..6ecdbf453ca 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -235,7 +235,7 @@ private PeerGroup createPeerGroup() { // For dao testnet (server side regtest) we prevent to connect to a localhost node to avoid confusion // if local btc node is not synced with our dao testnet master node. - if (BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest()) + if (BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest() || BisqEnvironment.getBaseCurrencyNetwork().isDaoTestNet()) peerGroup.setUseLocalhostPeerWhenPossible(false); return peerGroup; diff --git a/core/src/main/java/bisq/core/dao/governance/param/Param.java b/core/src/main/java/bisq/core/dao/governance/param/Param.java index 102364b2fd5..de020870f1b 100644 --- a/core/src/main/java/bisq/core/dao/governance/param/Param.java +++ b/core/src/main/java/bisq/core/dao/governance/param/Param.java @@ -142,7 +142,9 @@ public enum Param { "4" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "144" : // daoBetaNet; 1 day - "134", // testnet or dao regtest (server side regtest); 0.93 days + BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest() ? + "134" : // dao regtest; 0.93 days + "380", // testnet or dao testnet (server side regtest); 2.6 days ParamType.BLOCK, 2, 2), PHASE_BREAK1(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "149" : // mainnet; 1 day @@ -150,7 +152,9 @@ public enum Param { "1" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "10" : // daoBetaNet - "10", // testnet or dao regtest (server side regtest) + BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest() ? + "10" : // dao regtest + "10", // testnet or dao testnet (server side regtest) ParamType.BLOCK, 2, 2), PHASE_BLIND_VOTE(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "451" : // mainnet; 3 days @@ -158,7 +162,9 @@ public enum Param { "2" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "144" : // daoBetaNet; 1 day - "134", // testnet or dao regtest (server side regtest); 0.93 days + BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest() ? + "134" : // dao regtest; 0.93 days + "300", // testnet or dao testnet (server side regtest); 2 days ParamType.BLOCK, 2, 2), PHASE_BREAK2(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "9" : // mainnet @@ -166,7 +172,9 @@ public enum Param { "1" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "10" : // daoBetaNet - "10", // testnet or dao regtest (server side regtest) + BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest() ? + "10" : // dao regtest + "10", // testnet or dao testnet (server side regtest) ParamType.BLOCK, 2, 2), PHASE_VOTE_REVEAL(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "451" : // mainnet; 3 days @@ -174,7 +182,9 @@ public enum Param { "2" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "144" : // daoBetaNet; 1 day - "132", // testnet or dao regtest (server side regtest); 0.93 days + BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest() ? + "132" : // dao regtest; 0.93 days + "300", // testnet or dao testnet (server side regtest); 2 days ParamType.BLOCK, 2, 2), PHASE_BREAK3(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "9" : // mainnet @@ -182,7 +192,9 @@ public enum Param { "1" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "10" : // daoBetaNet - "10", // testnet or dao regtest (server side regtest) + BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest() ? + "10" : // dao regtest + "10", // testnet or dao testnet (server side regtest) ParamType.BLOCK, 2, 2), PHASE_RESULT(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "10" : // mainnet @@ -190,7 +202,9 @@ public enum Param { "2" : // regtest BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet() ? "10" : // daoBetaNet - "2", // testnet or dao regtest (server side regtest) + BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest() ? + "2" : // dao regtest + "2", // testnet or dao testnet (server side regtest) ParamType.BLOCK, 2, 2); @Getter diff --git a/core/src/main/java/bisq/core/dao/node/full/RpcService.java b/core/src/main/java/bisq/core/dao/node/full/RpcService.java index e3cac5102d9..b956a288167 100644 --- a/core/src/main/java/bisq/core/dao/node/full/RpcService.java +++ b/core/src/main/java/bisq/core/dao/node/full/RpcService.java @@ -99,12 +99,10 @@ public RpcService(Preferences preferences, boolean isPortSet = rpcPort != null && !rpcPort.isEmpty(); boolean isMainnet = BisqEnvironment.getBaseCurrencyNetwork().isMainnet(); boolean isTestnet = BisqEnvironment.getBaseCurrencyNetwork().isTestnet(); - boolean isDaoTestNet = BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest(); boolean isDaoBetaNet = BisqEnvironment.getBaseCurrencyNetwork().isDaoBetaNet(); this.rpcPort = isPortSet ? rpcPort : isMainnet || isDaoBetaNet ? "8332" : isTestnet ? "18332" : - isDaoTestNet ? "18443" : "18443"; // regtest this.rpcBlockPort = rpcBlockPort != null && !rpcBlockPort.isEmpty() ? rpcBlockPort : "5125"; diff --git a/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java b/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java index 10eecd484f4..aced9faa3f1 100644 --- a/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java +++ b/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java @@ -52,14 +52,18 @@ public class GenesisTxInfo { private static final int TESTNET_GENESIS_BLOCK_HEIGHT = 1446300; // 2018-12-02 private static final Coin TESTNET_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5"); // 2.5M BSQ / 2.50000000 BTC - private static final String DAO_REGTEST_GENESIS_TX_ID = "d594ad0c5de53e261b5784e5eb2acec8b807c45b74450401f488d36b8acf2e14"; - private static final int DAO_REGTEST_GENESIS_BLOCK_HEIGHT = 104; // 2019-03-26 - private static final Coin DAO_REGTEST_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5"); // 2.5M BSQ / 2.50000000 BTC + private static final String DAO_TESTNET_GENESIS_TX_ID = "cb316a186b9e88d1b8e1ce8dc79cc6a2080cc7bbc6df94f2be325d8253417af1"; + private static final int DAO_TESTNET_GENESIS_BLOCK_HEIGHT = 104; // 2019-02-19 + private static final Coin DAO_TESTNET_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5"); // 2.5M BSQ / 2.50000000 BTC private static final String DAO_BETANET_GENESIS_TX_ID = "0bd66d8ff26476b55dfaf2a5db0c659a5d8635566488244df25606db63a08bd9"; private static final int DAO_BETANET_GENESIS_BLOCK_HEIGHT = 567405; // 2019-03-16 private static final Coin DAO_BETANET_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("0.49998644"); // 499 986.44 BSQ / 0.49998644 BTC + private static final String DAO_REGTEST_GENESIS_TX_ID = "d594ad0c5de53e261b5784e5eb2acec8b807c45b74450401f488d36b8acf2e14"; + private static final int DAO_REGTEST_GENESIS_BLOCK_HEIGHT = 104; // 2019-03-26 + private static final Coin DAO_REGTEST_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5"); // 2.5M BSQ / 2.50000000 BTC + private static final String REGTEST_GENESIS_TX_ID = "30af0050040befd8af25068cc697e418e09c2d8ebd8d411d2240591b9ec203cf"; private static final int REGTEST_GENESIS_BLOCK_HEIGHT = 111; private static final Coin REGTEST_GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5"); // 2.5M BSQ / 2.50000000 BTC @@ -106,8 +110,9 @@ public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork(); boolean isMainnet = baseCurrencyNetwork.isMainnet(); boolean isTestnet = baseCurrencyNetwork.isTestnet(); - boolean isDaoRegTest = baseCurrencyNetwork.isDaoRegTest(); + boolean isDaoTestNet = baseCurrencyNetwork.isDaoTestNet(); boolean isDaoBetaNet = baseCurrencyNetwork.isDaoBetaNet(); + boolean isDaoRegTest = baseCurrencyNetwork.isDaoRegTest(); boolean isRegtest = baseCurrencyNetwork.isRegtest(); if (!genesisTxId.isEmpty()) { this.genesisTxId = genesisTxId; @@ -115,10 +120,12 @@ public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, this.genesisTxId = MAINNET_GENESIS_TX_ID; } else if (isTestnet) { this.genesisTxId = TESTNET_GENESIS_TX_ID; - } else if (isDaoRegTest) { - this.genesisTxId = DAO_REGTEST_GENESIS_TX_ID; + } else if (isDaoTestNet) { + this.genesisTxId = DAO_TESTNET_GENESIS_TX_ID; } else if (isDaoBetaNet) { this.genesisTxId = DAO_BETANET_GENESIS_TX_ID; + } else if (isDaoRegTest) { + this.genesisTxId = DAO_REGTEST_GENESIS_TX_ID; } else if (isRegtest) { this.genesisTxId = REGTEST_GENESIS_TX_ID; } else { @@ -131,10 +138,12 @@ public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, this.genesisBlockHeight = MAINNET_GENESIS_BLOCK_HEIGHT; } else if (isTestnet) { this.genesisBlockHeight = TESTNET_GENESIS_BLOCK_HEIGHT; - } else if (isDaoRegTest) { - this.genesisBlockHeight = DAO_REGTEST_GENESIS_BLOCK_HEIGHT; + } else if (isDaoTestNet) { + this.genesisBlockHeight = DAO_TESTNET_GENESIS_BLOCK_HEIGHT; } else if (isDaoBetaNet) { this.genesisBlockHeight = DAO_BETANET_GENESIS_BLOCK_HEIGHT; + } else if (isDaoRegTest) { + this.genesisBlockHeight = DAO_REGTEST_GENESIS_BLOCK_HEIGHT; } else if (isRegtest) { this.genesisBlockHeight = REGTEST_GENESIS_BLOCK_HEIGHT; } else { @@ -147,10 +156,12 @@ public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId, this.genesisTotalSupply = MAINNET_GENESIS_TOTAL_SUPPLY.value; } else if (isTestnet) { this.genesisTotalSupply = TESTNET_GENESIS_TOTAL_SUPPLY.value; - } else if (isDaoRegTest) { - this.genesisTotalSupply = DAO_REGTEST_GENESIS_TOTAL_SUPPLY.value; + } else if (isDaoTestNet) { + this.genesisTotalSupply = DAO_TESTNET_GENESIS_TOTAL_SUPPLY.value; } else if (isDaoBetaNet) { this.genesisTotalSupply = DAO_BETANET_GENESIS_TOTAL_SUPPLY.value; + } else if (isDaoRegTest) { + this.genesisTotalSupply = DAO_REGTEST_GENESIS_TOTAL_SUPPLY.value; } else if (isRegtest) { this.genesisTotalSupply = REGTEST_GENESIS_TOTAL_SUPPLY.value; } else { diff --git a/core/src/main/java/bisq/core/user/Preferences.java b/core/src/main/java/bisq/core/user/Preferences.java index 1a2ddfcc195..d28bc95e8a7 100644 --- a/core/src/main/java/bisq/core/user/Preferences.java +++ b/core/src/main/java/bisq/core/user/Preferences.java @@ -651,10 +651,12 @@ public BlockChainExplorer getBlockChainExplorer() { case BTC_TESTNET: case BTC_REGTEST: return prefPayload.getBlockChainExplorerTestNet(); - case BTC_DAO_REGTEST: + case BTC_DAO_TESTNET: return BTC_DAO_TEST_NET_EXPLORERS.get(0); case BTC_DAO_BETANET: return prefPayload.getBlockChainExplorerMainNet(); + case BTC_DAO_REGTEST: + return BTC_DAO_TEST_NET_EXPLORERS.get(0); default: throw new RuntimeException("BaseCurrencyNetwork not defined. BaseCurrencyNetwork=" + baseCurrencyNetwork); } @@ -668,10 +670,12 @@ public ArrayList getBlockChainExplorers() { case BTC_TESTNET: case BTC_REGTEST: return BTC_TEST_NET_EXPLORERS; - case BTC_DAO_REGTEST: + case BTC_DAO_TESTNET: return BTC_DAO_TEST_NET_EXPLORERS; case BTC_DAO_BETANET: return BTC_MAIN_NET_EXPLORERS; + case BTC_DAO_REGTEST: + return BTC_DAO_TEST_NET_EXPLORERS; default: throw new RuntimeException("BaseCurrencyNetwork not defined. BaseCurrencyNetwork=" + baseCurrencyNetwork); } diff --git a/core/src/main/resources/btc_dao_regtest.seednodes b/core/src/main/resources/btc_dao_regtest.seednodes index b4650b93cdb..4c6367383f9 100644 --- a/core/src/main/resources/btc_dao_regtest.seednodes +++ b/core/src/main/resources/btc_dao_regtest.seednodes @@ -1,6 +1,6 @@ # nodeaddress.onion:port [(@owner)] -2bnvhfkdrnx5hrlv.onion:8003 -b3jnw7fyph2jsu6n.onion:8003 +2bnvhfkdrnx5hrlv.onion:8005 +b3jnw7fyph2jsu6n.onion:8005 -# omentgpxrxy5lehq.onion:8003 -# r7cucuwouvhdhdgo.onion:8003 +# omentgpxrxy5lehq.onion:8005 +# r7cucuwouvhdhdgo.onion:8005 diff --git a/core/src/main/resources/btc_dao_testnet.seednodes b/core/src/main/resources/btc_dao_testnet.seednodes new file mode 100644 index 00000000000..fa63d9f5aed --- /dev/null +++ b/core/src/main/resources/btc_dao_testnet.seednodes @@ -0,0 +1,5 @@ +# nodeaddress.onion:port [(@owner)] +fjr5w4eckjghqtnu.onion:8003 +3d56s6acbi3vk52v.onion:8003 +74w2sttlo4qk6go3.onion:8003 +jmc5ajqvtnzqaggm.onion:8003 diff --git a/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java b/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java index 852e67de5d2..9c4982806af 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/preferences/PreferencesView.java @@ -593,7 +593,7 @@ private void activateGeneralOptions() { // We only show mainnet and dao testnet. Testnet is rather un-usable for application testing when asics // create 10000s of blocks per day. baseCurrencyNetworks = baseCurrencyNetworks.stream() - .filter(e -> e.isMainnet() || e.isDaoRegTest() || e.isDaoBetaNet()) + .filter(e -> e.isMainnet() || e.isDaoBetaNet() || e.isDaoRegTest()) .collect(Collectors.toList()); selectBaseCurrencyNetworkComboBox.setItems(FXCollections.observableArrayList(baseCurrencyNetworks)); selectBaseCurrencyNetworkComboBox.setOnAction(e -> onSelectNetwork()); From 431f76e058d5db8a14e41b576c86fae2e7c5371c Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 31 Mar 2019 16:11:00 -0500 Subject: [PATCH 48/55] Add BTC_DAO_TESTNET --- core/src/main/resources/i18n/displayStrings.properties | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index d541fa7cd86..467d7280b88 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2421,9 +2421,11 @@ BTC_TESTNET=Bitcoin Testnet # suppress inspection "UnusedProperty" BTC_REGTEST=Bitcoin Regtest # suppress inspection "UnusedProperty" -BTC_DAO_REGTEST=Bitcoin DAO Regtest +BTC_DAO_TESTNET=Bitcoin DAO Testnet (deprecated) # suppress inspection "UnusedProperty" BTC_DAO_BETANET=Bitcoin DAO Betanet (Bitcoin Mainnet) +# suppress inspection "UnusedProperty" +BTC_DAO_REGTEST=Bitcoin DAO Testnet time.year=Year time.month=Month From e74ce128b35099f6af31bccbe209469683b5c9d9 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 31 Mar 2019 16:28:34 -0500 Subject: [PATCH 49/55] Improve comments, cleanup --- core/src/main/java/bisq/core/btc/BitcoinModule.java | 4 ++-- core/src/main/java/bisq/core/dao/governance/param/Param.java | 2 +- .../governance/proposal/removeAsset/RemoveAssetValidator.java | 4 ++-- .../core/dao/governance/voteresult/VoteResultService.java | 1 - .../main/java/bisq/core/dao/node/explorer/JsonTxOutput.java | 1 - core/src/main/java/bisq/core/dao/node/parser/TxParser.java | 3 ++- .../java/bisq/core/dao/state/model/blockchain/TxType.java | 2 +- 7 files changed, 8 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/BitcoinModule.java b/core/src/main/java/bisq/core/btc/BitcoinModule.java index 6b888dff7d7..29b174539fd 100644 --- a/core/src/main/java/bisq/core/btc/BitcoinModule.java +++ b/core/src/main/java/bisq/core/btc/BitcoinModule.java @@ -54,8 +54,8 @@ public BitcoinModule(Environment environment) { @Override protected void configure() { - // We we have selected BTC_DAO_REGTEST we use our master regtest node, otherwise the specified host or default - // (localhost) + // If we we have selected BTC_DAO_REGTEST or BTC_DAO_TESTNET we use our master regtest node, + // otherwise the specified host or default (localhost) String regTestHost = environment.getProperty(BtcOptionKeys.REG_TEST_HOST, String.class, ""); if (regTestHost.isEmpty()) { regTestHost = BisqEnvironment.getBaseCurrencyNetwork().isDaoTestNet() ? diff --git a/core/src/main/java/bisq/core/dao/governance/param/Param.java b/core/src/main/java/bisq/core/dao/governance/param/Param.java index de020870f1b..81fa09ede95 100644 --- a/core/src/main/java/bisq/core/dao/governance/param/Param.java +++ b/core/src/main/java/bisq/core/dao/governance/param/Param.java @@ -144,7 +144,7 @@ public enum Param { "144" : // daoBetaNet; 1 day BisqEnvironment.getBaseCurrencyNetwork().isDaoRegTest() ? "134" : // dao regtest; 0.93 days - "380", // testnet or dao testnet (server side regtest); 2.6 days + "380", // testnet or dao testnet (server side regtest); 2.6 days ParamType.BLOCK, 2, 2), PHASE_BREAK1(BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? "149" : // mainnet; 1 day diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java index 49b780ee904..a3712482c98 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/removeAsset/RemoveAssetValidator.java @@ -51,8 +51,8 @@ public void validateDataFields(Proposal proposal) throws ProposalValidationExcep RemoveAssetProposal removeAssetProposal = (RemoveAssetProposal) proposal; notEmpty(removeAssetProposal.getTickerSymbol(), "TickerSymbol must not be empty"); - // Usually ticker is very short but our protection intention here is mainly to avoid that someone cause - // damage by inserting a super long string. + // We want to avoid that someone cause damage by inserting a super long string. Real ticker symbols + // are usually very short but we don't want to add additional restrictions here. checkArgument(removeAssetProposal.getTickerSymbol().length() <= 100, "TickerSymbol must not exceed 100 chars"); } catch (ProposalValidationException e) { throw e; diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java index a04e3befc53..03f5f7ffa9e 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java @@ -373,7 +373,6 @@ private BallotList createBallotList(VoteWithProposalTxIdList voteWithProposalTxI // It could be that we missed some proposalPayloads. // If we have votes with proposals which are not found in our ballots we add it to missingBallots. List missingBallots = new ArrayList<>(); - List ballots = voteByTxIdMap.entrySet().stream() .map(entry -> { String txId = entry.getKey(); diff --git a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutput.java b/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutput.java index 8d6dfc4e76a..65ce85374f5 100644 --- a/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutput.java +++ b/core/src/main/java/bisq/core/dao/node/explorer/JsonTxOutput.java @@ -112,7 +112,6 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(super.hashCode(), txVersion, txId, index, bsqAmount, btcAmount, height, isVerified, burntFee, invalidatedBsq, address, scriptPubKey, spentInfo, time, txType.name(), txTypeDisplayString, txOutputType, txOutputTypeDisplayString, opReturn, lockTime, isUnspent); diff --git a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java index ef700aabf03..b19fadfc781 100644 --- a/core/src/main/java/bisq/core/dao/node/parser/TxParser.java +++ b/core/src/main/java/bisq/core/dao/node/parser/TxParser.java @@ -425,7 +425,8 @@ static TxType evaluateTxTypeFromOpReturnType(TempTx tempTx, OpReturnType opRetur return TxType.PROOF_OF_BURN; default: log.warn("We got a BSQ tx with an unknown OP_RETURN. tx={}, opReturnType={}", tempTx, opReturnType); - // We tolerate such an incorrect tx and do not burn the BSQ + // We tolerate such an incorrect tx and do not burn the BSQ. We might need that in case we add new + // opReturn types in future. return TxType.IRREGULAR; } } diff --git a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java b/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java index 390362e7788..5083ae9690a 100644 --- a/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java +++ b/core/src/main/java/bisq/core/dao/state/model/blockchain/TxType.java @@ -46,7 +46,7 @@ public enum TxType implements ImmutableDaoStateModel { UNLOCK(true, false), ASSET_LISTING_FEE(true, true), PROOF_OF_BURN(true, true), - IRREGULAR(true, true); // the params are here irrelevant as we can have any tx which violated the rules set to irregular + IRREGULAR(false, false); // the params are here irrelevant as we can have any tx which violated the rules set to irregular /////////////////////////////////////////////////////////////////////////////////////////// From 30a710f1791c5da924380f0cf5a456ce293e884c Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 31 Mar 2019 16:30:35 -0500 Subject: [PATCH 50/55] Cleanup --- .../dao/governance/votereveal/VoteRevealService.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java index b43848a378b..5c208fe1436 100644 --- a/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java +++ b/core/src/main/java/bisq/core/dao/governance/votereveal/VoteRevealService.java @@ -181,9 +181,10 @@ private void maybeRevealVotes(int chainHeight) { // If we would create the tx in the last block it would be confirmed in the best case in th next // block which would be already the break and would invalidate the vote reveal. boolean isLastBlockInPhase = chainHeight == periodService.getLastBlockOfPhase(chainHeight, DaoPhase.Phase.VOTE_REVEAL); - boolean isBlindVoteTxInCorrectPhaseAndCycle = periodService.isTxInPhaseAndCycle(myVote.getBlindVoteTxId(), DaoPhase.Phase.BLIND_VOTE, chainHeight); + String blindVoteTxId = myVote.getBlindVoteTxId(); + boolean isBlindVoteTxInCorrectPhaseAndCycle = periodService.isTxInPhaseAndCycle(blindVoteTxId, DaoPhase.Phase.BLIND_VOTE, chainHeight); if (isInVoteRevealPhase && !isLastBlockInPhase && isBlindVoteTxInCorrectPhaseAndCycle) { - log.info("We call revealVote at blockHeight {} for blindVoteTxId {}", chainHeight, myVote.getBlindVoteTxId()); + log.info("We call revealVote at blockHeight {} for blindVoteTxId {}", chainHeight, blindVoteTxId); // Standard case that we are in the correct phase and cycle and create the reveal tx. revealVote(myVote, true); } else { @@ -194,7 +195,7 @@ private void maybeRevealVotes(int chainHeight) { boolean missedPhaseSameCycle = isAfterVoteRevealPhase && isBlindVoteTxInCorrectPhaseAndCycle; // If we missed the cycle we don't care about the phase anymore. - boolean isBlindVoteTxInPastCycle = periodService.isTxInPastCycle(myVote.getBlindVoteTxId(), chainHeight); + boolean isBlindVoteTxInPastCycle = periodService.isTxInPastCycle(blindVoteTxId, chainHeight); if (missedPhaseSameCycle || isBlindVoteTxInPastCycle) { // Exceptional case that the user missed the vote reveal phase. We still publish the vote @@ -207,7 +208,7 @@ private void maybeRevealVotes(int chainHeight) { // publish the vote reveal tx but are aware that is is invalid. log.warn("We missed the vote reveal phase but publish now the tx to unlock our locked " + "BSQ from the blind vote tx. BlindVoteTxId={}, blockHeight={}", - myVote.getBlindVoteTxId(), chainHeight); + blindVoteTxId, chainHeight); // We handle the exception here inside the stream iteration as we have not get triggered from an // outside user intent anyway. We keep errors in a observable list so clients can observe that to From dad4b040930eee18b14c2a87779c8dfde4505693 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 31 Mar 2019 17:31:53 -0500 Subject: [PATCH 51/55] Update comment --- .../core/dao/governance/blindvote/MyBlindVoteListService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java b/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java index 6c3fbd76fe1..bf25bac3505 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/MyBlindVoteListService.java @@ -354,6 +354,9 @@ private Transaction getBlindVoteTx(Coin stake, Coin fee, byte[] opReturnData) private void rePublishMyBlindVoteOnceWellConnected() { // We republish at each startup at any block during the cycle. We filter anyway for valid blind votes // of that cycle so it is 1 blind vote getting rebroadcast at each startup to my neighbors. + // Republishing only will have effect if the payload creation date is < 5 hours as other nodes would not + // accept payloads which are too old or are in future. + // Only payloads received from seed nodes would ignore that date check. int minPeers = BisqEnvironment.getBaseCurrencyNetwork().isMainnet() ? 4 : 1; if ((p2PService.getNumConnectedPeers().get() >= minPeers && p2PService.isBootstrapped()) || BisqEnvironment.getBaseCurrencyNetwork().isRegtest()) { From 4d56ce9a1071e4c235bf970f730144304d92a805 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 31 Mar 2019 17:35:14 -0500 Subject: [PATCH 52/55] Apply ExtraDataMapValidator for all extraDataMap fields --- core/src/main/java/bisq/core/alert/Alert.java | 3 ++- core/src/main/java/bisq/core/arbitration/Arbitrator.java | 3 ++- core/src/main/java/bisq/core/arbitration/Mediator.java | 3 ++- .../java/bisq/core/dao/governance/blindvote/BlindVote.java | 3 ++- core/src/main/java/bisq/core/filter/Filter.java | 3 ++- core/src/main/java/bisq/core/offer/OfferPayload.java | 3 ++- .../main/java/bisq/core/trade/statistics/TradeStatistics.java | 3 ++- .../main/java/bisq/core/trade/statistics/TradeStatistics2.java | 3 ++- .../network/p2p/storage/payload/MailboxStoragePayload.java | 3 ++- 9 files changed, 18 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/bisq/core/alert/Alert.java b/core/src/main/java/bisq/core/alert/Alert.java index cb2569f3901..4081b231f26 100644 --- a/core/src/main/java/bisq/core/alert/Alert.java +++ b/core/src/main/java/bisq/core/alert/Alert.java @@ -22,6 +22,7 @@ import bisq.common.app.Version; import bisq.common.crypto.Sig; +import bisq.common.util.ExtraDataMapValidator; import io.bisq.generated.protobuffer.PB; @@ -91,7 +92,7 @@ public Alert(String message, this.version = version; this.ownerPubKeyBytes = ownerPubKeyBytes; this.signatureAsBase64 = signatureAsBase64; - this.extraDataMap = extraDataMap; + this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); ownerPubKey = Sig.getPublicKeyFromBytes(ownerPubKeyBytes); } diff --git a/core/src/main/java/bisq/core/arbitration/Arbitrator.java b/core/src/main/java/bisq/core/arbitration/Arbitrator.java index a9b6d738c45..ffc6a8f4670 100644 --- a/core/src/main/java/bisq/core/arbitration/Arbitrator.java +++ b/core/src/main/java/bisq/core/arbitration/Arbitrator.java @@ -23,6 +23,7 @@ import bisq.common.crypto.PubKeyRing; import bisq.common.proto.ProtoUtil; +import bisq.common.util.ExtraDataMapValidator; import bisq.common.util.Utilities; import io.bisq.generated.protobuffer.PB; @@ -91,7 +92,7 @@ public Arbitrator(NodeAddress nodeAddress, this.registrationSignature = registrationSignature; this.emailAddress = emailAddress; this.info = info; - this.extraDataMap = extraDataMap; + this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/bisq/core/arbitration/Mediator.java b/core/src/main/java/bisq/core/arbitration/Mediator.java index b5d4e3ca643..0673b5465c0 100644 --- a/core/src/main/java/bisq/core/arbitration/Mediator.java +++ b/core/src/main/java/bisq/core/arbitration/Mediator.java @@ -23,6 +23,7 @@ import bisq.common.crypto.PubKeyRing; import bisq.common.proto.ProtoUtil; +import bisq.common.util.ExtraDataMapValidator; import io.bisq.generated.protobuffer.PB; @@ -84,7 +85,7 @@ public Mediator(NodeAddress nodeAddress, this.registrationSignature = registrationSignature; this.emailAddress = emailAddress; this.info = info; - this.extraDataMap = extraDataMap; + this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); } diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVote.java b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVote.java index c5fa1c92cf8..09e961bcf16 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVote.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVote.java @@ -21,6 +21,7 @@ import bisq.common.proto.network.NetworkPayload; import bisq.common.proto.persistable.PersistablePayload; +import bisq.common.util.ExtraDataMapValidator; import bisq.common.util.Utilities; import io.bisq.generated.protobuffer.PB; @@ -65,7 +66,7 @@ public BlindVote(byte[] encryptedVotes, this.txId = txId; this.stake = stake; this.encryptedMeritList = encryptedMeritList; - this.extraDataMap = extraDataMap; + this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); } diff --git a/core/src/main/java/bisq/core/filter/Filter.java b/core/src/main/java/bisq/core/filter/Filter.java index c7b3db9a898..b2fc9f92020 100644 --- a/core/src/main/java/bisq/core/filter/Filter.java +++ b/core/src/main/java/bisq/core/filter/Filter.java @@ -21,6 +21,7 @@ import bisq.network.p2p.storage.payload.ProtectedStoragePayload; import bisq.common.crypto.Sig; +import bisq.common.util.ExtraDataMapValidator; import io.bisq.generated.protobuffer.PB; @@ -146,7 +147,7 @@ public Filter(List bannedOfferIds, disableDao); this.signatureAsBase64 = signatureAsBase64; this.ownerPubKeyBytes = ownerPubKeyBytes; - this.extraDataMap = extraDataMap; + this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); ownerPubKey = Sig.getPublicKeyFromBytes(ownerPubKeyBytes); } diff --git a/core/src/main/java/bisq/core/offer/OfferPayload.java b/core/src/main/java/bisq/core/offer/OfferPayload.java index eb7e37cb030..822f62ce225 100644 --- a/core/src/main/java/bisq/core/offer/OfferPayload.java +++ b/core/src/main/java/bisq/core/offer/OfferPayload.java @@ -24,6 +24,7 @@ import bisq.common.crypto.PubKeyRing; import bisq.common.proto.ProtoUtil; +import bisq.common.util.ExtraDataMapValidator; import bisq.common.util.JsonExclude; import io.bisq.generated.protobuffer.PB; @@ -235,7 +236,7 @@ public OfferPayload(String id, this.upperClosePrice = upperClosePrice; this.isPrivateOffer = isPrivateOffer; this.hashOfChallenge = hashOfChallenge; - this.extraDataMap = extraDataMap; + this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); this.protocolVersion = protocolVersion; } diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics.java index f68b99838ae..47608fed711 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics.java @@ -29,6 +29,7 @@ import bisq.common.crypto.Sig; import bisq.common.proto.persistable.PersistablePayload; +import bisq.common.util.ExtraDataMapValidator; import bisq.common.util.JsonExclude; import io.bisq.generated.protobuffer.PB; @@ -147,7 +148,7 @@ public TradeStatistics(OfferPayload offerPayload, this.tradeDate = tradeDate; this.depositTxId = depositTxId; this.signaturePubKeyBytes = signaturePubKeyBytes; - this.extraDataMap = extraDataMap; + this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); signaturePubKey = Sig.getPublicKeyFromBytes(signaturePubKeyBytes); } diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java index 443b489fe67..ecd3b529371 100644 --- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java +++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java @@ -32,6 +32,7 @@ import bisq.common.app.Capability; import bisq.common.crypto.Hash; import bisq.common.proto.persistable.PersistableEnvelope; +import bisq.common.util.ExtraDataMapValidator; import bisq.common.util.JsonExclude; import bisq.common.util.Utilities; @@ -150,7 +151,7 @@ public TradeStatistics2(OfferPayload.Direction direction, this.tradeAmount = tradeAmount; this.tradeDate = tradeDate; this.depositTxId = depositTxId; - this.extraDataMap = extraDataMap; + this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); if (hash == null) // We create hash from all fields excluding hash itself. We use json as simple data serialisation. diff --git a/p2p/src/main/java/bisq/network/p2p/storage/payload/MailboxStoragePayload.java b/p2p/src/main/java/bisq/network/p2p/storage/payload/MailboxStoragePayload.java index 88dd96cc945..80ce47f5634 100644 --- a/p2p/src/main/java/bisq/network/p2p/storage/payload/MailboxStoragePayload.java +++ b/p2p/src/main/java/bisq/network/p2p/storage/payload/MailboxStoragePayload.java @@ -20,6 +20,7 @@ import bisq.network.p2p.PrefixedSealedAndSignedMessage; import bisq.common.crypto.Sig; +import bisq.common.util.ExtraDataMapValidator; import io.bisq.generated.protobuffer.PB; @@ -85,7 +86,7 @@ private MailboxStoragePayload(PrefixedSealedAndSignedMessage prefixedSealedAndSi this.prefixedSealedAndSignedMessage = prefixedSealedAndSignedMessage; this.senderPubKeyForAddOperationBytes = senderPubKeyForAddOperationBytes; this.ownerPubKeyBytes = ownerPubKeyBytes; - this.extraDataMap = extraDataMap; + this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); senderPubKeyForAddOperation = Sig.getPublicKeyFromBytes(senderPubKeyForAddOperationBytes); ownerPubKey = Sig.getPublicKeyFromBytes(ownerPubKeyBytes); From e9e4b490142260a0830c57b54b5ebad36241f879 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 31 Mar 2019 17:41:59 -0500 Subject: [PATCH 53/55] Add number of irregular txs to UI --- core/src/main/java/bisq/core/dao/DaoFacade.java | 4 ++++ .../bisq/core/dao/state/DaoStateService.java | 4 ++++ .../resources/i18n/displayStrings.properties | 1 + .../transactions/BSQTransactionsView.java | 17 ++++++++++------- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/DaoFacade.java b/core/src/main/java/bisq/core/dao/DaoFacade.java index 26d1415c03f..7c5c90f772a 100644 --- a/core/src/main/java/bisq/core/dao/DaoFacade.java +++ b/core/src/main/java/bisq/core/dao/DaoFacade.java @@ -557,6 +557,10 @@ public List getInvalidTxs() { return daoStateService.getInvalidTxs(); } + public List 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(); diff --git a/core/src/main/java/bisq/core/dao/state/DaoStateService.java b/core/src/main/java/bisq/core/dao/state/DaoStateService.java index d3c3b9f203d..05c6f3d9018 100644 --- a/core/src/main/java/bisq/core/dao/state/DaoStateService.java +++ b/core/src/main/java/bisq/core/dao/state/DaoStateService.java @@ -359,6 +359,10 @@ public List getInvalidTxs() { return getTxStream().filter(tx -> tx.getTxType() == TxType.INVALID).collect(Collectors.toList()); } + public List getIrregularTxs() { + return getTxStream().filter(tx -> tx.getTxType() == TxType.IRREGULAR).collect(Collectors.toList()); + } + public boolean containsTx(String txId) { return getTx(txId).isPresent(); } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 467d7280b88..cae8d4511f2 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1956,6 +1956,7 @@ dao.factsAndFigures.transactions.compensationIssuanceTx=No. of all compensation dao.factsAndFigures.transactions.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions dao.factsAndFigures.transactions.burntTx=No. of all fee payments transactions dao.factsAndFigures.transactions.invalidTx=No. of all invalid transactions +dao.factsAndFigures.transactions.irregularTx=No. of all irregular transactions #################################################################### # Windows diff --git a/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java b/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java index a39dfb1102d..95effae2877 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/economy/transactions/BSQTransactionsView.java @@ -53,8 +53,7 @@ public class BSQTransactionsView extends ActivatableView impleme private int gridRow = 0; private TextField allTxTextField, burntFeeTxsTextField, utxoTextField, compensationIssuanceTxTextField, - reimbursementIssuanceTxTextField, - invalidTxsTextField; + reimbursementIssuanceTxTextField, invalidTxsTextField, irregularTxsTextField; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor, lifecycle @@ -96,21 +95,24 @@ public void initialize() { utxoTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.factsAndFigures.transactions.utxo")).second; compensationIssuanceTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.factsAndFigures.transactions.compensationIssuanceTx")).second; + reimbursementIssuanceTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, + Res.get("dao.factsAndFigures.transactions.reimbursementIssuanceTx")).second; int columnIndex = 1; - gridRow = startRow; titledGroupBg = addTitledGroupBg(root, startRow, columnIndex, 3, "", Layout.GROUP_DISTANCE); titledGroupBg.getStyleClass().add("last"); - reimbursementIssuanceTxTextField = addTopLabelReadOnlyTextField(root, gridRow, columnIndex, - Res.get("dao.factsAndFigures.transactions.reimbursementIssuanceTx"), + burntFeeTxsTextField = addTopLabelReadOnlyTextField(root, gridRow, columnIndex, + Res.get("dao.factsAndFigures.transactions.burntTx"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; - burntFeeTxsTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, - Res.get("dao.factsAndFigures.transactions.burntTx")).second; invalidTxsTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.factsAndFigures.transactions.invalidTx")).second; + irregularTxsTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, + Res.get("dao.factsAndFigures.transactions.irregularTx")).second; + gridRow++; + } @Override @@ -147,6 +149,7 @@ private void updateWithBsqBlockChainData() { reimbursementIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.REIMBURSEMENT))); burntFeeTxsTextField.setText(String.valueOf(daoFacade.getBurntFeeTxs().size())); invalidTxsTextField.setText(String.valueOf(daoFacade.getInvalidTxs().size())); + irregularTxsTextField.setText(String.valueOf(daoFacade.getIrregularTxs().size())); } } From 0e2bb14683734a406d016e62fcdba02065b2bd64 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 31 Mar 2019 17:43:14 -0500 Subject: [PATCH 54/55] Break up lines --- .../common/util/ExtraDataMapValidator.java | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/common/src/main/java/bisq/common/util/ExtraDataMapValidator.java b/common/src/main/java/bisq/common/util/ExtraDataMapValidator.java index d0f10bc5baf..df158557165 100644 --- a/common/src/main/java/bisq/common/util/ExtraDataMapValidator.java +++ b/common/src/main/java/bisq/common/util/ExtraDataMapValidator.java @@ -42,15 +42,19 @@ public static Map getValidatedExtraDataMap(@Nullable Map getValidatedExtraDataMap(@Nullable Map extraDataMap, int maxSize, int maxKeyLength, int maxValueLength) { + public static Map getValidatedExtraDataMap(@Nullable Map extraDataMap, int maxSize, + int maxKeyLength, int maxValueLength) { if (extraDataMap == null) return null; try { - checkArgument(extraDataMap.entrySet().size() <= maxSize, "Size of map must not exceed " + maxSize); + checkArgument(extraDataMap.entrySet().size() <= maxSize, + "Size of map must not exceed " + maxSize); extraDataMap.forEach((key, value) -> { - checkArgument(key.length() <= maxKeyLength, "Length of key must not exceed " + maxKeyLength); - checkArgument(value.length() <= maxValueLength, "Length of value must not exceed " + maxValueLength); + checkArgument(key.length() <= maxKeyLength, + "Length of key must not exceed " + maxKeyLength); + checkArgument(value.length() <= maxValueLength, + "Length of value must not exceed " + maxValueLength); }); return extraDataMap; } catch (Throwable t) { @@ -62,14 +66,18 @@ public static void validate(@Nullable Map extraDataMap) { validate(extraDataMap, MAX_SIZE, MAX_KEY_LENGTH, MAX_VALUE_LENGTH); } - public static void validate(@Nullable Map extraDataMap, int maxSize, int maxKeyLength, int maxValueLength) { + public static void validate(@Nullable Map extraDataMap, int maxSize, int maxKeyLength, + int maxValueLength) { if (extraDataMap == null) return; - checkArgument(extraDataMap.entrySet().size() <= maxSize, "Size of map must not exceed " + maxSize); + checkArgument(extraDataMap.entrySet().size() <= maxSize, + "Size of map must not exceed " + maxSize); extraDataMap.forEach((key, value) -> { - checkArgument(key.length() <= maxKeyLength, "Length of key must not exceed " + maxKeyLength); - checkArgument(value.length() <= maxValueLength, "Length of value must not exceed " + maxValueLength); + checkArgument(key.length() <= maxKeyLength, + "Length of key must not exceed " + maxKeyLength); + checkArgument(value.length() <= maxValueLength, + "Length of value must not exceed " + maxValueLength); }); } } From f95f77021868d161f1ba3d0e7d6de589095f04ac Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 31 Mar 2019 17:54:51 -0500 Subject: [PATCH 55/55] Update witness file --- gradle/witness/gradle-witness.gradle | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle/witness/gradle-witness.gradle b/gradle/witness/gradle-witness.gradle index 42fa8be0381..48e81b11c63 100644 --- a/gradle/witness/gradle-witness.gradle +++ b/gradle/witness/gradle-witness.gradle @@ -38,12 +38,12 @@ dependencyVerification { 'com.googlecode.json-simple:json-simple:4e69696892b88b41c55d49ab2fdcc21eead92bf54acc588c0050596c3b75199c', 'org.springframework:spring-core:c451e8417adb2ffb2445636da5e44a2f59307c4100037a1fe387c3fba4f29b52', 'ch.qos.logback:logback-classic:e66efc674e94837344bc5b748ff510c37a44eeff86cbfdbf9e714ef2eb374013', - 'com.github.bisq-network.bitcoinj:bitcoinj-core:816e976a7efcfb650ff9009059059068b785ead864a6321fe77bba65ebd3d273', 'org.slf4j:slf4j-api:3a4cd4969015f3beb4b5b4d81dbafc01765fb60b8a439955ca64d8476fef553e', 'ch.qos.logback:logback-core:4cd46fa17d77057b39160058df2f21ebbc2aded51d0edcc25d2c1cecc042a005', 'com.google.code.findbugs:jsr305:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.guava:guava:36a666e3b71ae7f0f0dca23654b67e086e6c93d192f60ba5dfd5519db6c288c8', 'com.google.inject:guice:9b9df27a5b8c7864112b4137fd92b36c3f1395bfe57be42fedf2f520ead1a93e', + 'com.github.bisq-network.bitcoinj:bitcoinj-core:816e976a7efcfb650ff9009059059068b785ead864a6321fe77bba65ebd3d273', 'com.github.JesusMcCloud.netlayer:tor:35cf892e6ce3a8d942cfd2b589cfbde5aed31d49777aee873d6614e134df0b42', 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:193ab7813e4d249f2ea4fc1b968fea8c2126bcbeeb5d6127050ce1b93dbaa7c2', 'io.github.microutils:kotlin-logging:4992504fd3c6ecdf9ed10874b9508e758bb908af9e9d7af19a61e9afb6b7e27a', @@ -66,17 +66,17 @@ dependencyVerification { 'commons-logging:commons-logging:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636', 'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'aopalliance:aopalliance:0addec670fedcd3f113c5c8091d783280d23f75e3acb841b61a9cdb079376a08', + 'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f', + 'net.jcip:jcip-annotations:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0', 'com.lambdaworks:scrypt:9a82d218099fb14c10c0e86e7eefeebd8c104de920acdc47b8b4b7a686fb73b4', + 'org.bitcoinj:orchid:f836325cfa0466a011cb755c9b0fee6368487a2352eb45f4306ad9e4c18de080', + 'com.squareup.okhttp:okhttp:b4c943138fcef2bcc9d2006b2250c4aabbedeafc5947ed7c0af7fd103ceb2707', 'com.google.zxing:core:11aae8fd974ab25faa8208be50468eb12349cd239e93e7c797377fa13e381729', 'com.github.JesusMcCloud.tor-binary:tor-binary-geoip:766e4400e5651cf0b11788ea440cc72721be9b92e42f20809c22d0ff129df83c', 'com.github.JesusMcCloud:jtorctl:904f7c53332179a3479c64d63fb303afa6a02b6889aabdab5b235f3efc725ca7', 'org.apache.commons:commons-compress:5f2df1e467825e4cac5996d44890c4201c000b43c0b23cffc0782d28a0beb9b0', 'org.tukaani:xz:a594643d73cc01928cf6ca5ce100e094ea9d73af760a5d4fb6b75fa673ecec96', - 'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f', - 'net.jcip:jcip-annotations:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0', - 'org.bitcoinj:orchid:f836325cfa0466a011cb755c9b0fee6368487a2352eb45f4306ad9e4c18de080', - 'com.squareup.okhttp:okhttp:b4c943138fcef2bcc9d2006b2250c4aabbedeafc5947ed7c0af7fd103ceb2707', - 'org.jetbrains.kotlin:kotlin-stdlib-common:4b161ef619eee0d1a49b1c4f0c4a8e46f4e342573efd8e0106a765f47475fe39', 'com.squareup.okio:okio:114bdc1f47338a68bcbc95abf2f5cdc72beeec91812f2fcd7b521c1937876266', + 'org.jetbrains.kotlin:kotlin-stdlib-common:4b161ef619eee0d1a49b1c4f0c4a8e46f4e342573efd8e0106a765f47475fe39', ] }