evaluatedProposals) {
return evaluatedProposals.stream()
.filter(EvaluatedProposal::isAccepted)
@@ -668,6 +738,14 @@ public class VoteResultService implements BsqStateListener, DaoSetupService {
return periodService.getFirstBlockOfPhase(chainHeight, DaoPhase.Phase.RESULT) == chainHeight;
}
+ private void persistEvaluatedProposals() {
+ evaluatedProposalStorage.queueUpForSave(20);
+ }
+
+ private void persistDecryptedBallotsWithMerits() {
+ decryptedBallotsWithMeritsStorage.queueUpForSave(20);
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////
// Inner classes
diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/issuance/IssuanceService.java b/core/src/main/java/bisq/core/dao/governance/voteresult/issuance/IssuanceService.java
index ae6bd0d8cf..f25d29dd06 100644
--- a/core/src/main/java/bisq/core/dao/governance/voteresult/issuance/IssuanceService.java
+++ b/core/src/main/java/bisq/core/dao/governance/voteresult/issuance/IssuanceService.java
@@ -72,7 +72,7 @@ public class IssuanceService {
StringBuilder sb = new StringBuilder();
sb.append("\n################################################################################\n");
sb.append("We issued new BSQ to tx with ID ").append(txOutput.getTxId())
- .append("\nfor compensationProposal with UID ").append(compensationProposal.getTxId())
+ .append("\nIssued BSQ: ").append(compensationProposal.getRequestedBsq())
.append("\n################################################################################\n");
log.info(sb.toString());
} else {
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 0eecba7c4d..b15956e86a 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
@@ -18,12 +18,12 @@
package bisq.core.dao.governance.votereveal;
import bisq.core.btc.exceptions.TransactionVerificationException;
+import bisq.core.btc.exceptions.TxBroadcastException;
+import bisq.core.btc.exceptions.TxMalleabilityException;
import bisq.core.btc.exceptions.WalletException;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
-import bisq.core.btc.wallet.TxBroadcastException;
import bisq.core.btc.wallet.TxBroadcaster;
-import bisq.core.btc.wallet.TxMalleabilityException;
import bisq.core.btc.wallet.WalletsManager;
import bisq.core.dao.DaoSetupService;
import bisq.core.dao.governance.blindvote.BlindVote;
diff --git a/core/src/main/java/bisq/core/dao/node/BsqNode.java b/core/src/main/java/bisq/core/dao/node/BsqNode.java
index 1ff7b43a08..3cb8d0d7cd 100644
--- a/core/src/main/java/bisq/core/dao/node/BsqNode.java
+++ b/core/src/main/java/bisq/core/dao/node/BsqNode.java
@@ -155,7 +155,11 @@ public abstract class BsqNode implements DaoSetupService {
@SuppressWarnings("WeakerAccess")
protected int getStartBlockHeight() {
- final int startBlockHeight = Math.max(genesisBlockHeight, bsqStateService.getChainHeight());
+ int chainHeight = bsqStateService.getChainHeight();
+ int startBlockHeight = chainHeight;
+ if (chainHeight > genesisBlockHeight)
+ startBlockHeight = chainHeight + 1;
+
log.info("Start parse blocks:\n" +
" Start block height={}\n" +
" Genesis txId={}\n" +
@@ -164,7 +168,7 @@ public abstract class BsqNode implements DaoSetupService {
startBlockHeight,
genesisTxId,
genesisBlockHeight,
- bsqStateService.getChainHeight());
+ chainHeight);
return startBlockHeight;
}
diff --git a/core/src/main/java/bisq/core/dao/node/full/FullNode.java b/core/src/main/java/bisq/core/dao/node/full/FullNode.java
index 05b9ac7db8..945b693043 100644
--- a/core/src/main/java/bisq/core/dao/node/full/FullNode.java
+++ b/core/src/main/java/bisq/core/dao/node/full/FullNode.java
@@ -19,7 +19,7 @@ package bisq.core.dao.node.full;
import bisq.core.dao.node.BsqNode;
import bisq.core.dao.node.full.network.FullNodeNetworkService;
-import bisq.core.dao.node.json.JsonBlockChainExporter;
+import bisq.core.dao.node.json.ExportJsonFilesService;
import bisq.core.dao.node.parser.BlockParser;
import bisq.core.dao.node.parser.exceptions.BlockNotConnectingException;
import bisq.core.dao.state.BsqStateService;
@@ -40,7 +40,7 @@ import lombok.extern.slf4j.Slf4j;
/**
* Main class for a full node which have Bitcoin Core with rpc running and does the blockchain lookup itself.
* It also provides the BSQ transactions to lite nodes on request and broadcasts new BSQ blocks.
- *
+ *
* TODO request p2p network data again after parsing is complete to be sure that in case we missed data during parsing
* we get it added.
*/
@@ -49,7 +49,7 @@ public class FullNode extends BsqNode {
private final RpcService rpcService;
private final FullNodeNetworkService fullNodeNetworkService;
- private final JsonBlockChainExporter jsonBlockChainExporter;
+ private final ExportJsonFilesService exportJsonFilesService;
private boolean addBlockHandlerAdded;
@@ -64,12 +64,12 @@ public class FullNode extends BsqNode {
SnapshotManager snapshotManager,
P2PService p2PService,
RpcService rpcService,
- JsonBlockChainExporter jsonBlockChainExporter,
+ ExportJsonFilesService exportJsonFilesService,
FullNodeNetworkService fullNodeNetworkService) {
super(blockParser, bsqStateService, snapshotManager, p2PService);
this.rpcService = rpcService;
- this.jsonBlockChainExporter = jsonBlockChainExporter;
+ this.exportJsonFilesService = exportJsonFilesService;
this.fullNodeNetworkService = fullNodeNetworkService;
}
@@ -80,6 +80,8 @@ public class FullNode extends BsqNode {
@Override
public void start() {
+ fullNodeNetworkService.start();
+
rpcService.setup(() -> {
super.onInitialized();
startParseBlocks();
@@ -88,7 +90,7 @@ public class FullNode extends BsqNode {
}
public void shutDown() {
- jsonBlockChainExporter.shutDown();
+ exportJsonFilesService.shutDown();
fullNodeNetworkService.shutDown();
}
@@ -147,18 +149,18 @@ public class FullNode extends BsqNode {
}
private void onNewBlock(Block block) {
- jsonBlockChainExporter.maybeExport();
+ exportJsonFilesService.exportToJson();
if (p2pNetworkReady && parseBlockchainComplete)
fullNodeNetworkService.publishNewBlock(block);
}
- private void parseBlocksIfNewBlockAvailable(int chainHeadHeight) {
- rpcService.requestChainHeadHeight(newChainHeadHeight -> {
- if (newChainHeadHeight > chainHeadHeight) {
+ private void parseBlocksIfNewBlockAvailable(int chainHeight) {
+ rpcService.requestChainHeadHeight(newChainHeight -> {
+ if (newChainHeight > chainHeight) {
log.info("During parsing new blocks have arrived. We parse again with those missing blocks." +
- "ChainHeadHeight={}, newChainHeadHeight={}", chainHeadHeight, newChainHeadHeight);
- parseBlocksOnHeadHeight(chainHeadHeight, newChainHeadHeight);
+ "ChainHeadHeight={}, newChainHeadHeight={}", chainHeight, newChainHeight);
+ parseBlocksOnHeadHeight(chainHeight + 1, newChainHeight);
} else {
log.info("parseBlocksIfNewBlockAvailable did not result in a new block, so we complete.");
onParseBlockChainComplete();
@@ -169,27 +171,26 @@ public class FullNode extends BsqNode {
private void requestChainHeadHeightAndParseBlocks(int startBlockHeight) {
log.info("requestChainHeadHeightAndParseBlocks with startBlockHeight={}", startBlockHeight);
- rpcService.requestChainHeadHeight(chainHeadHeight -> parseBlocksOnHeadHeight(startBlockHeight, chainHeadHeight),
+ rpcService.requestChainHeadHeight(chainHeight -> parseBlocksOnHeadHeight(startBlockHeight, chainHeight),
this::handleError);
}
- private void parseBlocksOnHeadHeight(int startBlockHeight, int chainHeadHeight) {
- if (startBlockHeight <= chainHeadHeight) {
- log.info("parseBlocks with startBlockHeight={} and chainHeadHeight={}", startBlockHeight, chainHeadHeight);
+ private void parseBlocksOnHeadHeight(int startBlockHeight, int chainHeight) {
+ if (startBlockHeight <= chainHeight) {
+ log.info("parseBlocks with startBlockHeight={} and chainHeight={}", startBlockHeight, chainHeight);
parseBlocks(startBlockHeight,
- chainHeadHeight,
+ chainHeight,
this::onNewBlock,
() -> {
// We are done but it might be that new blocks have arrived in the meantime,
- // so we try again with startBlockHeight set to current chainHeadHeight
+ // so we try again with startBlockHeight set to current chainHeight
// We also set up the listener in the else main branch where we check
// if we are at chainTip, so do not include here another check as it would
// not trigger the listener registration.
- parseBlocksIfNewBlockAvailable(chainHeadHeight);
+ parseBlocksIfNewBlockAvailable(chainHeight);
}, throwable -> {
if (throwable instanceof BlockNotConnectingException) {
- int blockHeightOfLastBlock = bsqStateService.getBlockHeightOfLastBlock();
- requestChainHeadHeightAndParseBlocks(blockHeightOfLastBlock);
+ startReOrgFromLastSnapshot();
} else {
handleError(throwable);
}
@@ -203,15 +204,15 @@ public class FullNode extends BsqNode {
}
private void parseBlocks(int startBlockHeight,
- int chainHeadHeight,
+ int chainHeight,
Consumer newBlockHandler,
ResultHandler resultHandler,
Consumer errorHandler) {
- parseBlock(startBlockHeight, chainHeadHeight, newBlockHandler, resultHandler, errorHandler);
+ parseBlock(startBlockHeight, chainHeight, newBlockHandler, resultHandler, errorHandler);
}
// Recursively request and parse all blocks
- private void parseBlock(int blockHeight, int chainHeadHeight,
+ private void parseBlock(int blockHeight, int chainHeight,
Consumer newBlockHandler, ResultHandler resultHandler,
Consumer errorHandler) {
rpcService.requestBtcBlock(blockHeight,
@@ -221,10 +222,10 @@ public class FullNode extends BsqNode {
Block block = blockParser.parseBlock(rawBlock);
newBlockHandler.accept(block);
- // Increment blockHeight and recursively call parseBlockAsync until we reach chainHeadHeight
- if (blockHeight < chainHeadHeight) {
+ // Increment blockHeight and recursively call parseBlockAsync until we reach chainHeight
+ if (blockHeight < chainHeight) {
final int newBlockHeight = blockHeight + 1;
- parseBlock(newBlockHeight, chainHeadHeight, newBlockHandler, resultHandler, errorHandler);
+ parseBlock(newBlockHeight, chainHeight, newBlockHandler, resultHandler, errorHandler);
} else {
// We are done
resultHandler.handleResult();
@@ -232,15 +233,20 @@ public class FullNode extends BsqNode {
} catch (BlockNotConnectingException e) {
errorHandler.accept(e);
}
+ } else {
+ log.info("Block was already added height=", rawBlock.getHeight());
}
},
errorHandler);
}
private void handleError(Throwable throwable) {
- final String errorMessage = "Initializing FullNode failed: Error=" + throwable.toString();
+ String errorMessage = "An error occurred: Error=" + throwable.toString();
log.error(errorMessage);
+ if (throwable instanceof BlockNotConnectingException)
+ startReOrgFromLastSnapshot();
+
if (errorMessageHandler != null)
errorMessageHandler.handleErrorMessage(errorMessage);
}
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 67e4361a38..febb7d9956 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
@@ -162,6 +162,11 @@ public class RpcService {
daemon.addBlockListener(new BlockListener() {
@Override
public void blockDetected(com.neemre.btcdcli4j.core.domain.RawBlock rawBtcBlock) {
+ if (rawBtcBlock.getHeight() == null || rawBtcBlock.getHeight() == 0) {
+ log.warn("We received a RawBlock with no data. blockHash={}", rawBtcBlock.getHash());
+ return;
+ }
+
try {
log.info("New block received: height={}, id={}", rawBtcBlock.getHeight(), rawBtcBlock.getHash());
List txList = rawBtcBlock.getTx().stream()
@@ -183,9 +188,9 @@ public class RpcService {
void requestChainHeadHeight(Consumer resultHandler, Consumer errorHandler) {
ListenableFuture future = executor.submit(client::getBlockCount);
- Futures.addCallback(future, new FutureCallback() {
- public void onSuccess(Integer chainHeadHeight) {
- UserThread.execute(() -> resultHandler.accept(chainHeadHeight));
+ Futures.addCallback(future, new FutureCallback<>() {
+ public void onSuccess(Integer chainHeight) {
+ UserThread.execute(() -> resultHandler.accept(chainHeight));
}
public void onFailure(@NotNull Throwable throwable) {
@@ -204,7 +209,7 @@ public class RpcService {
List txList = rawBtcBlock.getTx().stream()
.map(e -> getTxFromRawTransaction(e, rawBtcBlock))
.collect(Collectors.toList());
- log.info("requestBtcBlock with all txs took {} ms at blockHeight {}; txList.size={}",
+ log.debug("requestBtcBlock with all txs took {} ms at blockHeight {}; txList.size={}",
System.currentTimeMillis() - startTs, blockHeight, txList.size());
return new RawBlock(rawBtcBlock.getHeight(),
rawBtcBlock.getTime() * 1000, // rawBtcBlock.getTime() is in sec but we want ms
@@ -213,7 +218,7 @@ public class RpcService {
ImmutableList.copyOf(txList));
});
- Futures.addCallback(future, new FutureCallback() {
+ Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(RawBlock block) {
UserThread.execute(() -> resultHandler.accept(block));
@@ -221,6 +226,7 @@ public class RpcService {
@Override
public void onFailure(@NotNull Throwable throwable) {
+ log.error("Error at requestBtcBlock: blockHeight={}", blockHeight);
UserThread.execute(() -> errorHandler.accept(throwable));
}
});
diff --git a/core/src/main/java/bisq/core/dao/node/full/network/FullNodeNetworkService.java b/core/src/main/java/bisq/core/dao/node/full/network/FullNodeNetworkService.java
index 4f19f5962c..573d5145c9 100644
--- a/core/src/main/java/bisq/core/dao/node/full/network/FullNodeNetworkService.java
+++ b/core/src/main/java/bisq/core/dao/node/full/network/FullNodeNetworkService.java
@@ -17,12 +17,18 @@
package bisq.core.dao.node.full.network;
+import bisq.core.dao.governance.blindvote.BlindVoteListService;
+import bisq.core.dao.governance.blindvote.network.messages.RepublishGovernanceDataRequest;
+import bisq.core.dao.governance.blindvote.storage.BlindVotePayload;
+import bisq.core.dao.governance.proposal.ProposalService;
+import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload;
import bisq.core.dao.node.messages.GetBlocksRequest;
import bisq.core.dao.node.messages.NewBlockBroadcastMessage;
import bisq.core.dao.state.BsqStateService;
import bisq.core.dao.state.blockchain.Block;
import bisq.core.dao.state.blockchain.RawBlock;
+import bisq.network.p2p.P2PService;
import bisq.network.p2p.network.Connection;
import bisq.network.p2p.network.MessageListener;
import bisq.network.p2p.network.NetworkNode;
@@ -35,8 +41,12 @@ import bisq.common.proto.network.NetworkEnvelope;
import javax.inject.Inject;
+import javafx.collections.ObservableList;
+
import java.util.HashMap;
import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
@@ -58,6 +68,9 @@ public class FullNodeNetworkService implements MessageListener, PeerManager.List
private final NetworkNode networkNode;
private final PeerManager peerManager;
private final Broadcaster broadcaster;
+ private final BlindVoteListService blindVoteListService;
+ private final ProposalService proposalService;
+ private final P2PService p2PService;
private final BsqStateService bsqStateService;
// Key is connection UID
@@ -73,20 +86,28 @@ public class FullNodeNetworkService implements MessageListener, PeerManager.List
public FullNodeNetworkService(NetworkNode networkNode,
PeerManager peerManager,
Broadcaster broadcaster,
+ BlindVoteListService blindVoteListService,
+ ProposalService proposalService,
+ P2PService p2PService,
BsqStateService bsqStateService) {
this.networkNode = networkNode;
this.peerManager = peerManager;
this.broadcaster = broadcaster;
+ this.blindVoteListService = blindVoteListService;
+ this.proposalService = proposalService;
+ this.p2PService = p2PService;
this.bsqStateService = bsqStateService;
-
- networkNode.addMessageListener(this);
- peerManager.addListener(this);
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
+ public void start() {
+ networkNode.addMessageListener(this);
+ peerManager.addListener(this);
+ }
+
@SuppressWarnings("Duplicates")
public void shutDown() {
Log.traceCall();
@@ -173,6 +194,39 @@ public class FullNodeNetworkService implements MessageListener, PeerManager.List
} else {
log.warn("We have stopped already. We ignore that onMessage call.");
}
+ } else if (networkEnvelope instanceof RepublishGovernanceDataRequest) {
+ 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.info("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);
+ });
+
+ 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.info("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);
+ });
}
}
}
diff --git a/core/src/main/java/bisq/core/dao/node/full/network/GetBlocksRequestHandler.java b/core/src/main/java/bisq/core/dao/node/full/network/GetBlocksRequestHandler.java
index 5714d87f51..9791030f32 100644
--- a/core/src/main/java/bisq/core/dao/node/full/network/GetBlocksRequestHandler.java
+++ b/core/src/main/java/bisq/core/dao/node/full/network/GetBlocksRequestHandler.java
@@ -95,10 +95,12 @@ class GetBlocksRequestHandler {
List rawBlocks = blocks.stream().map(RawBlock::fromBlock).collect(Collectors.toList());
final GetBlocksResponse getBlocksResponse = new GetBlocksResponse(rawBlocks, getBlocksRequest.getNonce());
log.debug("getBlocksResponse " + getBlocksResponse.getRequestNonce());
-
+ log.info("Received getBlocksResponse from {} for blocks from height {}",
+ connection.getPeersNodeAddressOptional(), getBlocksRequest.getFromBlockHeight());
if (timeoutTimer == null) {
timeoutTimer = UserThread.runAfter(() -> { // setup before sending to avoid race conditions
- String errorMessage = "A timeout occurred for getBlocksResponse:" + getBlocksResponse +
+ String errorMessage = "A timeout occurred for getBlocksResponse.requestNonce:" +
+ getBlocksResponse.getRequestNonce() +
" on connection:" + connection;
handleFault(errorMessage, CloseConnectionReason.SEND_MSG_TIMEOUT, connection);
},
@@ -110,8 +112,8 @@ class GetBlocksRequestHandler {
@Override
public void onSuccess(Connection connection) {
if (!stopped) {
- log.trace("Send DataResponse to {} succeeded. getBlocksResponse={}",
- connection.getPeersNodeAddressOptional(), getBlocksResponse);
+ log.info("Send DataResponse to {} succeeded. getBlocksResponse.getBlocks().size()={}",
+ connection.getPeersNodeAddressOptional(), getBlocksResponse.getBlocks().size());
cleanup();
listener.onComplete();
} else {
diff --git a/core/src/main/java/bisq/core/dao/node/json/ExportJsonFilesService.java b/core/src/main/java/bisq/core/dao/node/json/ExportJsonFilesService.java
new file mode 100644
index 0000000000..019a1cd32e
--- /dev/null
+++ b/core/src/main/java/bisq/core/dao/node/json/ExportJsonFilesService.java
@@ -0,0 +1,268 @@
+/*
+ * 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.node.json;
+
+import bisq.core.dao.DaoOptionKeys;
+import bisq.core.dao.DaoSetupService;
+import bisq.core.dao.state.BsqState;
+import bisq.core.dao.state.BsqStateService;
+import bisq.core.dao.state.blockchain.Block;
+import bisq.core.dao.state.blockchain.PubKeyScript;
+import bisq.core.dao.state.blockchain.Tx;
+import bisq.core.dao.state.blockchain.TxOutput;
+import bisq.core.dao.state.blockchain.TxType;
+
+import bisq.common.storage.FileUtil;
+import bisq.common.storage.JsonFileManager;
+import bisq.common.storage.Storage;
+import bisq.common.util.Utilities;
+
+import org.bitcoinj.core.Utils;
+
+import com.google.inject.Inject;
+
+import javax.inject.Named;
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+import java.nio.file.Paths;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.jetbrains.annotations.NotNull;
+
+@Slf4j
+public class ExportJsonFilesService implements DaoSetupService {
+ private final BsqStateService bsqStateService;
+ private final File storageDir;
+ private final boolean dumpBlockchainData;
+
+ private final ListeningExecutorService executor = Utilities.getListeningExecutorService("JsonExporter",
+ 1, 1, 1200);
+ private JsonFileManager txFileManager, txOutputFileManager, bsqStateFileManager;
+
+ @Inject
+ public ExportJsonFilesService(BsqStateService bsqStateService,
+ @Named(Storage.STORAGE_DIR) File storageDir,
+ @Named(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) {
+ this.bsqStateService = bsqStateService;
+ this.storageDir = storageDir;
+ this.dumpBlockchainData = dumpBlockchainData;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // DaoSetupService
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void addListeners() {
+ }
+
+ @Override
+ public void start() {
+ if (dumpBlockchainData) {
+ File jsonDir = new File(Paths.get(storageDir.getAbsolutePath(), "json").toString());
+ File txDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "tx").toString());
+ File txOutputDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "txo").toString());
+ File bsqStateDir = new File(Paths.get(storageDir.getAbsolutePath(), "json", "all").toString());
+ try {
+ if (txDir.exists())
+ FileUtil.deleteDirectory(txDir);
+ if (txOutputDir.exists())
+ FileUtil.deleteDirectory(txOutputDir);
+ if (bsqStateDir.exists())
+ FileUtil.deleteDirectory(bsqStateDir);
+ if (jsonDir.exists())
+ FileUtil.deleteDirectory(jsonDir);
+ } catch (IOException e) {
+ log.error(e.toString());
+ e.printStackTrace();
+ }
+
+ if (!jsonDir.mkdir())
+ log.warn("make jsonDir failed.\njsonDir=" + jsonDir.getAbsolutePath());
+
+ if (!txDir.mkdir())
+ log.warn("make txDir failed.\ntxDir=" + txDir.getAbsolutePath());
+
+ if (!txOutputDir.mkdir())
+ log.warn("make txOutputDir failed.\ntxOutputDir=" + txOutputDir.getAbsolutePath());
+
+ if (!bsqStateDir.mkdir())
+ log.warn("make bsqStateDir failed.\nbsqStateDir=" + bsqStateDir.getAbsolutePath());
+
+ txFileManager = new JsonFileManager(txDir);
+ txOutputFileManager = new JsonFileManager(txOutputDir);
+ bsqStateFileManager = new JsonFileManager(bsqStateDir);
+ }
+ }
+
+ public void shutDown() {
+ if (dumpBlockchainData) {
+ txFileManager.shutDown();
+ txOutputFileManager.shutDown();
+ bsqStateFileManager.shutDown();
+ }
+ }
+
+ public void exportToJson() {
+ if (dumpBlockchainData) {
+ // We store the data we need once we write the data to disk (in the thread) locally.
+ // Access to bsqStateService is single threaded, we must not access bsqStateService from the thread.
+ List allJsonTxOutputs = new ArrayList<>();
+
+ List jsonTxs = bsqStateService.getTxStream()
+ .map(tx -> {
+ JsonTx jsonTx = getJsonTx(tx);
+ allJsonTxOutputs.addAll(jsonTx.getOutputs());
+ return jsonTx;
+ }).collect(Collectors.toList());
+
+ BsqState bsqState = bsqStateService.getClone();
+ List jsonBlockList = bsqState.getBlocks().stream()
+ .map(this::getJsonBlock)
+ .collect(Collectors.toList());
+ JsonBlocks jsonBlocks = new JsonBlocks(bsqState.getChainHeight(), jsonBlockList);
+
+ ListenableFuture future = executor.submit(() -> {
+ bsqStateFileManager.writeToDisc(Utilities.objectToJson(jsonBlocks), "blocks");
+ allJsonTxOutputs.forEach(jsonTxOutput -> txOutputFileManager.writeToDisc(Utilities.objectToJson(jsonTxOutput), jsonTxOutput.getId()));
+ jsonTxs.forEach(jsonTx -> txFileManager.writeToDisc(Utilities.objectToJson(jsonTx), jsonTx.getId()));
+ return null;
+ });
+
+ Futures.addCallback(future, new FutureCallback<>() {
+ public void onSuccess(Void ignore) {
+ log.trace("onSuccess");
+ }
+
+ public void onFailure(@NotNull Throwable throwable) {
+ log.error(throwable.toString());
+ throwable.printStackTrace();
+ }
+ });
+ }
+ }
+
+ private JsonBlock getJsonBlock(Block block) {
+ List jsonTxs = block.getTxs().stream()
+ .map(this::getJsonTx)
+ .collect(Collectors.toList());
+ return new JsonBlock(block.getHeight(),
+ block.getTime(),
+ block.getHash(),
+ block.getPreviousBlockHash(),
+ jsonTxs);
+ }
+
+ private JsonTx getJsonTx(Tx tx) {
+ JsonTxType jsonTxType = getJsonTxType(tx);
+ String jsonTxTypeDisplayString = getJsonTxTypeDisplayString(jsonTxType);
+ return new JsonTx(tx.getId(),
+ tx.getBlockHeight(),
+ tx.getBlockHash(),
+ tx.getTime(),
+ getJsonTxInputs(tx),
+ getJsonTxOutputs(tx),
+ jsonTxType,
+ jsonTxTypeDisplayString,
+ bsqStateService.getBurntFee(tx.getId()),
+ tx.getUnlockBlockHeight());
+ }
+
+ private List getJsonTxInputs(Tx tx) {
+ return tx.getTxInputs().stream()
+ .map(txInput -> {
+ Optional optionalTxOutput = bsqStateService.getConnectedTxOutput(txInput);
+ if (optionalTxOutput.isPresent()) {
+ TxOutput connectedTxOutput = optionalTxOutput.get();
+ boolean isBsqTxOutputType = bsqStateService.isBsqTxOutputType(connectedTxOutput);
+ return new JsonTxInput(txInput.getConnectedTxOutputIndex(),
+ txInput.getConnectedTxOutputTxId(),
+ connectedTxOutput.getValue(),
+ isBsqTxOutputType,
+ connectedTxOutput.getAddress(),
+ tx.getTime());
+ } else {
+ return null;
+ }
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ private List getJsonTxOutputs(Tx tx) {
+ JsonTxType jsonTxType = getJsonTxType(tx);
+ String jsonTxTypeDisplayString = getJsonTxTypeDisplayString(jsonTxType);
+ return tx.getTxOutputs().stream()
+ .map(txOutput -> {
+ boolean isBsqTxOutputType = bsqStateService.isBsqTxOutputType(txOutput);
+ long bsqAmount = isBsqTxOutputType ? txOutput.getValue() : 0;
+ long btcAmount = !isBsqTxOutputType ? txOutput.getValue() : 0;
+ PubKeyScript pubKeyScript = txOutput.getPubKeyScript();
+ JsonScriptPubKey scriptPubKey = pubKeyScript != null ? new JsonScriptPubKey(pubKeyScript) : null;
+ JsonSpentInfo spentInfo = bsqStateService.getSpentInfo(txOutput).map(JsonSpentInfo::new).orElse(null);
+ JsonTxOutputType txOutputType = JsonTxOutputType.valueOf(txOutput.getTxOutputType().name());
+ int lockTime = txOutput.getLockTime();
+ String opReturn = txOutput.getOpReturnData() != null ? Utils.HEX.encode(txOutput.getOpReturnData()) : null;
+ boolean isUnspent = bsqStateService.isUnspent(txOutput.getKey());
+ return new JsonTxOutput(tx.getId(),
+ txOutput.getIndex(),
+ bsqAmount,
+ btcAmount,
+ tx.getBlockHeight(),
+ isBsqTxOutputType,
+ bsqStateService.getBurntFee(tx.getId()),
+ txOutput.getAddress(),
+ scriptPubKey,
+ spentInfo,
+ tx.getTime(),
+ jsonTxType,
+ jsonTxTypeDisplayString,
+ txOutputType,
+ txOutputType.getDisplayString(),
+ opReturn,
+ lockTime,
+ isUnspent
+ );
+ })
+ .collect(Collectors.toList());
+ }
+
+ private String getJsonTxTypeDisplayString(JsonTxType jsonTxType) {
+ return jsonTxType != null ? jsonTxType.getDisplayString() : "";
+ }
+
+ private JsonTxType getJsonTxType(Tx tx) {
+ TxType txType = tx.getTxType();
+ return txType != null ? JsonTxType.valueOf(txType.name()) : null;
+ }
+}
diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/MissingBallotException.java b/core/src/main/java/bisq/core/dao/node/json/JsonBlock.java
similarity index 56%
rename from core/src/main/java/bisq/core/dao/governance/voteresult/MissingBallotException.java
rename to core/src/main/java/bisq/core/dao/node/json/JsonBlock.java
index a86d0c1ea4..99227b404d 100644
--- a/core/src/main/java/bisq/core/dao/governance/voteresult/MissingBallotException.java
+++ b/core/src/main/java/bisq/core/dao/node/json/JsonBlock.java
@@ -15,23 +15,17 @@
* along with Bisq. If not, see .
*/
-package bisq.core.dao.governance.voteresult;
-
-import bisq.core.dao.governance.ballot.Ballot;
+package bisq.core.dao.node.json;
import java.util.List;
-import lombok.EqualsAndHashCode;
import lombok.Value;
-@EqualsAndHashCode(callSuper = true)
@Value
-public class MissingBallotException extends Exception {
- private List existingBallots;
- private List proposalTxIdsOfMissingBallots;
-
- public MissingBallotException(List existingBallots, List proposalTxIdsOfMissingBallots) {
- this.existingBallots = existingBallots;
- this.proposalTxIdsOfMissingBallots = proposalTxIdsOfMissingBallots;
- }
+class JsonBlock {
+ protected final int height;
+ protected final long time; // in ms
+ protected final String hash;
+ protected final String previousBlockHash;
+ private final List txs;
}
diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonBlockChainExporter.java b/core/src/main/java/bisq/core/dao/node/json/JsonBlockChainExporter.java
deleted file mode 100644
index 25745e3556..0000000000
--- a/core/src/main/java/bisq/core/dao/node/json/JsonBlockChainExporter.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * 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.node.json;
-
-import bisq.core.dao.DaoOptionKeys;
-import bisq.core.dao.state.BsqState;
-import bisq.core.dao.state.BsqStateService;
-import bisq.core.dao.state.blockchain.PubKeyScript;
-import bisq.core.dao.state.blockchain.SpentInfo;
-import bisq.core.dao.state.blockchain.Tx;
-import bisq.core.dao.state.blockchain.TxOutput;
-import bisq.core.dao.state.blockchain.TxType;
-
-import bisq.common.storage.FileUtil;
-import bisq.common.storage.JsonFileManager;
-import bisq.common.storage.Storage;
-import bisq.common.util.Utilities;
-
-import org.bitcoinj.core.Utils;
-
-import com.google.inject.Inject;
-
-import javax.inject.Named;
-
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-
-import java.nio.file.Paths;
-
-import java.io.File;
-import java.io.IOException;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-import lombok.extern.slf4j.Slf4j;
-
-import org.jetbrains.annotations.NotNull;
-
-@Slf4j
-public class JsonBlockChainExporter {
- private final BsqStateService bsqStateService;
- private final boolean dumpBlockchainData;
-
- private final ListeningExecutorService executor = Utilities.getListeningExecutorService("JsonExporter", 1, 1, 1200);
- private JsonFileManager txFileManager, txOutputFileManager, jsonFileManager;
-
- @Inject
- public JsonBlockChainExporter(BsqStateService bsqStateService,
- @Named(Storage.STORAGE_DIR) File storageDir,
- @Named(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) {
- this.bsqStateService = bsqStateService;
- this.dumpBlockchainData = dumpBlockchainData;
-
- init(storageDir, dumpBlockchainData);
- }
-
- private void init(@Named(Storage.STORAGE_DIR) File storageDir, @Named(DaoOptionKeys.DUMP_BLOCKCHAIN_DATA) boolean dumpBlockchainData) {
- if (dumpBlockchainData) {
- File txDir = new File(Paths.get(storageDir.getAbsolutePath(), "tx").toString());
- File txOutputDir = new File(Paths.get(storageDir.getAbsolutePath(), "txo").toString());
- File blockchainDir = new File(Paths.get(storageDir.getAbsolutePath(), "all").toString());
- try {
- if (txDir.exists())
- FileUtil.deleteDirectory(txDir);
- if (txOutputDir.exists())
- FileUtil.deleteDirectory(txOutputDir);
- if (blockchainDir.exists())
- FileUtil.deleteDirectory(blockchainDir);
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- if (!txDir.mkdir())
- log.warn("make txDir failed.\ntxDir=" + txDir.getAbsolutePath());
-
- if (!txOutputDir.mkdir())
- log.warn("make txOutputDir failed.\ntxOutputDir=" + txOutputDir.getAbsolutePath());
-
- if (!blockchainDir.mkdir())
- log.warn("make blockchainDir failed.\nblockchainDir=" + blockchainDir.getAbsolutePath());
-
- txFileManager = new JsonFileManager(txDir);
- txOutputFileManager = new JsonFileManager(txOutputDir);
- jsonFileManager = new JsonFileManager(blockchainDir);
- }
- }
-
- public void shutDown() {
- if (dumpBlockchainData) {
- txFileManager.shutDown();
- txOutputFileManager.shutDown();
- jsonFileManager.shutDown();
- }
- }
-
- public void maybeExport() {
- if (dumpBlockchainData) {
- ListenableFuture future = executor.submit(() -> {
- final BsqState bsqStateClone = bsqStateService.getClone();
- Map txMap = bsqStateService.getBlocksFromState(bsqStateClone).stream()
- .filter(Objects::nonNull)
- .flatMap(block -> block.getTxs().stream())
- .collect(Collectors.toMap(Tx::getId, tx -> tx));
- for (Tx tx : txMap.values()) {
- String txId = tx.getId();
- final Optional optionalTxType = bsqStateService.getOptionalTxType(txId);
- optionalTxType.ifPresent(txType1 -> {
- JsonTxType txType = txType1 != TxType.UNDEFINED_TX_TYPE ?
- JsonTxType.valueOf(txType1.name()) : null;
- List outputs = new ArrayList<>();
- tx.getTxOutputs().forEach(txOutput -> {
- final Optional optionalSpentInfo = bsqStateService.getSpentInfo(txOutput);
- final boolean isBsqOutput = bsqStateService.isBsqTxOutputType(txOutput);
- final PubKeyScript pubKeyScript = txOutput.getPubKeyScript();
- final JsonTxOutput outputForJson = new JsonTxOutput(txId,
- txOutput.getIndex(),
- isBsqOutput ? txOutput.getValue() : 0,
- !isBsqOutput ? txOutput.getValue() : 0,
- txOutput.getBlockHeight(),
- isBsqOutput,
- bsqStateService.getBurntFee(tx.getId()),
- txOutput.getAddress(),
- pubKeyScript != null ? new JsonScriptPubKey(pubKeyScript) : null,
- optionalSpentInfo.map(JsonSpentInfo::new).orElse(null),
- tx.getTime(),
- txType,
- txType != null ? txType.getDisplayString() : "",
- txOutput.getOpReturnData() != null ? Utils.HEX.encode(txOutput.getOpReturnData()) : null
- );
- outputs.add(outputForJson);
- txOutputFileManager.writeToDisc(Utilities.objectToJson(outputForJson), outputForJson.getId());
- });
-
-
- List inputs = tx.getTxInputs().stream()
- .map(txInput -> {
- Optional optionalTxOutput = bsqStateService.getConnectedTxOutput(txInput);
- if (optionalTxOutput.isPresent()) {
- final TxOutput connectedTxOutput = optionalTxOutput.get();
- final boolean isBsqOutput = bsqStateService.isBsqTxOutputType(connectedTxOutput);
- return new JsonTxInput(txInput.getConnectedTxOutputIndex(),
- txInput.getConnectedTxOutputTxId(),
- connectedTxOutput.getValue(),
- isBsqOutput,
- connectedTxOutput.getAddress(),
- tx.getTime());
- } else {
- return null;
- }
- })
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
-
- final JsonTx jsonTx = new JsonTx(txId,
- tx.getBlockHeight(),
- tx.getBlockHash(),
- tx.getTime(),
- inputs,
- outputs,
- txType,
- txType != null ? txType.getDisplayString() : "",
- bsqStateService.getBurntFee(tx.getId()));
-
- txFileManager.writeToDisc(Utilities.objectToJson(jsonTx), txId);
- });
- }
-
- jsonFileManager.writeToDisc(Utilities.objectToJson(bsqStateClone), "BsqStateService");
- return null;
- });
-
- Futures.addCallback(future, new FutureCallback() {
- public void onSuccess(Void ignore) {
- log.trace("onSuccess");
- }
-
- public void onFailure(@NotNull Throwable throwable) {
- log.error(throwable.toString());
- throwable.printStackTrace();
- }
- });
- }
- }
-}
diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonBlocks.java b/core/src/main/java/bisq/core/dao/node/json/JsonBlocks.java
new file mode 100644
index 0000000000..a87a7e5b04
--- /dev/null
+++ b/core/src/main/java/bisq/core/dao/node/json/JsonBlocks.java
@@ -0,0 +1,28 @@
+/*
+ * 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.node.json;
+
+import java.util.List;
+
+import lombok.Value;
+
+@Value
+class JsonBlocks {
+ private int chainHeight;
+ private final List blocks;
+}
diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonScriptPubKey.java b/core/src/main/java/bisq/core/dao/node/json/JsonScriptPubKey.java
index c585b5dc87..a69ae8c397 100644
--- a/core/src/main/java/bisq/core/dao/node/json/JsonScriptPubKey.java
+++ b/core/src/main/java/bisq/core/dao/node/json/JsonScriptPubKey.java
@@ -26,14 +26,14 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@Value
-public class JsonScriptPubKey {
+class JsonScriptPubKey {
private final List addresses;
private final String asm;
private final String hex;
private final int reqSigs;
private final String type;
- public JsonScriptPubKey(PubKeyScript pubKeyScript) {
+ JsonScriptPubKey(PubKeyScript pubKeyScript) {
addresses = pubKeyScript.getAddresses();
asm = pubKeyScript.getAsm();
hex = pubKeyScript.getHex();
diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonSpentInfo.java b/core/src/main/java/bisq/core/dao/node/json/JsonSpentInfo.java
index 14f82f5505..119579c761 100644
--- a/core/src/main/java/bisq/core/dao/node/json/JsonSpentInfo.java
+++ b/core/src/main/java/bisq/core/dao/node/json/JsonSpentInfo.java
@@ -22,12 +22,12 @@ import bisq.core.dao.state.blockchain.SpentInfo;
import lombok.Value;
@Value
-public class JsonSpentInfo {
+class JsonSpentInfo {
private final long height;
private final int inputIndex;
private final String txId;
- public JsonSpentInfo(SpentInfo spentInfo) {
+ JsonSpentInfo(SpentInfo spentInfo) {
height = spentInfo.getBlockHeight();
inputIndex = spentInfo.getInputIndex();
txId = spentInfo.getTxId();
diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonTx.java b/core/src/main/java/bisq/core/dao/node/json/JsonTx.java
index 5ea7d38e4b..e68e74859e 100644
--- a/core/src/main/java/bisq/core/dao/node/json/JsonTx.java
+++ b/core/src/main/java/bisq/core/dao/node/json/JsonTx.java
@@ -23,9 +23,8 @@ import java.util.List;
import lombok.Value;
-//TODO sync up with data model
@Value
-public class JsonTx {
+class JsonTx {
private final String txVersion = Version.BSQ_TX_VERSION;
private final String id;
private final int blockHeight;
@@ -36,4 +35,6 @@ public class JsonTx {
private final JsonTxType txType;
private final String txTypeDisplayString;
private final long burntFee;
+ // If not set it is -1. LockTime of 0 is a valid value.
+ private final int unlockBlockHeight;
}
diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonTxInput.java b/core/src/main/java/bisq/core/dao/node/json/JsonTxInput.java
index 329e75a1e0..684d6031d8 100644
--- a/core/src/main/java/bisq/core/dao/node/json/JsonTxInput.java
+++ b/core/src/main/java/bisq/core/dao/node/json/JsonTxInput.java
@@ -21,14 +21,13 @@ import lombok.Value;
import javax.annotation.concurrent.Immutable;
-//TODO sync up with data model
@Value
@Immutable
-public class JsonTxInput {
- private final int spendingTxOutputIndex;
- private final String spendingTxId;
+class JsonTxInput {
+ private final int spendingTxOutputIndex; // connectedTxOutputIndex
+ private final String spendingTxId; // connectedTxOutputTxId
private final long bsqAmount;
- private final boolean isVerified;
+ private final boolean isVerified; // isBsqTxOutputType
private final String address;
private final long time;
}
diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonTxOutput.java b/core/src/main/java/bisq/core/dao/node/json/JsonTxOutput.java
index 069066a645..03e56d0d79 100644
--- a/core/src/main/java/bisq/core/dao/node/json/JsonTxOutput.java
+++ b/core/src/main/java/bisq/core/dao/node/json/JsonTxOutput.java
@@ -21,26 +21,34 @@ import bisq.common.app.Version;
import lombok.Value;
-//TODO sync up with data model
+import javax.annotation.Nullable;
+
@Value
-public class JsonTxOutput {
+class JsonTxOutput {
private final String txVersion = Version.BSQ_TX_VERSION;
private final String txId;
- private final int outputIndex;
+ private final int index;
private final long bsqAmount;
private final long btcAmount;
private final int height;
- private final boolean isVerified;
+ private final boolean isVerified; // isBsqTxOutputType
private final long burntFee;
private final String address;
+ @Nullable
private final JsonScriptPubKey scriptPubKey;
+ @Nullable
private final JsonSpentInfo spentInfo;
private final long time;
private final JsonTxType txType;
private final String txTypeDisplayString;
+ private final JsonTxOutputType txOutputType;
+ private final String txOutputTypeDisplayString;
+ @Nullable
private final String opReturn;
+ private final int lockTime;
+ private final boolean isUnspent;
- public String getId() {
- return txId + ":" + outputIndex;
+ String getId() {
+ return txId + ":" + index;
}
}
diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonTxOutputType.java b/core/src/main/java/bisq/core/dao/node/json/JsonTxOutputType.java
new file mode 100644
index 0000000000..f8187e065a
--- /dev/null
+++ b/core/src/main/java/bisq/core/dao/node/json/JsonTxOutputType.java
@@ -0,0 +1,47 @@
+/*
+ * 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.node.json;
+
+import lombok.Getter;
+
+// Need to be in sync with TxOutputType
+enum JsonTxOutputType {
+ UNDEFINED_OUTPUT("Undefined"),
+ GENESIS_OUTPUT("Genesis"),
+ BSQ_OUTPUT("BSQ"),
+ BTC_OUTPUT("BTC"),
+ PROPOSAL_OP_RETURN_OUTPUT("Proposal opReturn"),
+ COMP_REQ_OP_RETURN_OUTPUT("Compensation request opReturn"),
+ CONFISCATE_BOND_OP_RETURN_OUTPUT("Confiscate bond opReturn"),
+ ISSUANCE_CANDIDATE_OUTPUT("Issuance candidate"),
+ BLIND_VOTE_LOCK_STAKE_OUTPUT("Blind vote lock stake"),
+ BLIND_VOTE_OP_RETURN_OUTPUT("Blind vote opReturn"),
+ VOTE_REVEAL_UNLOCK_STAKE_OUTPUT("Vote reveal unlock stake"),
+ VOTE_REVEAL_OP_RETURN_OUTPUT("Vote reveal opReturn"),
+ LOCKUP_OUTPUT("Lockup"),
+ LOCKUP_OP_RETURN_OUTPUT("Lockup opReturn"),
+ UNLOCK_OUTPUT("Unlock"),
+ INVALID_OUTPUT("Invalid");
+
+ @Getter
+ private String displayString;
+
+ JsonTxOutputType(String displayString) {
+ this.displayString = displayString;
+ }
+}
diff --git a/core/src/main/java/bisq/core/dao/node/json/JsonTxType.java b/core/src/main/java/bisq/core/dao/node/json/JsonTxType.java
index e624247377..5d44c504c6 100644
--- a/core/src/main/java/bisq/core/dao/node/json/JsonTxType.java
+++ b/core/src/main/java/bisq/core/dao/node/json/JsonTxType.java
@@ -19,21 +19,20 @@ package bisq.core.dao.node.json;
import lombok.Getter;
-//TODO sync up with data model
-public enum JsonTxType {
+// Need to be in sync with TxOutputType
+enum JsonTxType {
UNDEFINED_TX_TYPE("Undefined"),
UNVERIFIED("Unverified"),
INVALID("Invalid"),
GENESIS("Genesis"),
TRANSFER_BSQ("Transfer BSQ"),
PAY_TRADE_FEE("Pay trade fee"),
- PROPOSAL("Ballot"),
+ PROPOSAL("Proposal"),
COMPENSATION_REQUEST("Compensation request"),
- VOTE("Vote"),
BLIND_VOTE("Blind vote"),
VOTE_REVEAL("Vote reveal"),
- LOCK_UP("Lockup"),
- UN_LOCK("Unlock");
+ LOCKUP("Lockup"),
+ UNLOCK("Unlock");
@Getter
private String displayString;
diff --git a/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java b/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java
index 35cb2eb6ff..96e264c4ee 100644
--- a/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java
+++ b/core/src/main/java/bisq/core/dao/node/lite/LiteNode.java
@@ -73,7 +73,7 @@ public class LiteNode extends BsqNode {
public void start() {
super.onInitialized();
- liteNodeNetworkService.init();
+ liteNodeNetworkService.start();
}
@Override
diff --git a/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java b/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java
index 32a24cc898..d9a20d6634 100644
--- a/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java
+++ b/core/src/main/java/bisq/core/dao/node/lite/network/LiteNodeNetworkService.java
@@ -117,7 +117,7 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
// API
///////////////////////////////////////////////////////////////////////////////////////////
- public void init() {
+ public void start() {
networkNode.addMessageListener(this);
networkNode.addConnectionListener(this);
peerManager.addListener(this);
@@ -139,7 +139,6 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
}
public void requestBlocks(int startBlockHeight) {
- Log.traceCall();
lastRequestedBlockHeight = startBlockHeight;
Optional connectionToSeedNodeOptional = networkNode.getConfirmedConnections().stream()
.filter(peerManager::isSeedNode)
@@ -219,6 +218,7 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
@Override
public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) {
if (networkEnvelope instanceof NewBlockBroadcastMessage) {
+ log.info("We received blocks from peer {}", connection.getPeersNodeAddressOptional());
listeners.forEach(listener -> listener.onNewBlockReceived((NewBlockBroadcastMessage) networkEnvelope));
}
}
@@ -239,7 +239,7 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
new RequestBlocksHandler.Listener() {
@Override
public void onComplete(GetBlocksResponse getBlocksResponse) {
- log.trace("requestBlocksHandler of outbound connection complete. nodeAddress={}",
+ log.info("requestBlocksHandler of outbound connection complete. nodeAddress={}",
peersNodeAddress);
stopRetryTimer();
@@ -270,6 +270,7 @@ public class LiteNodeNetworkService implements MessageListener, ConnectionListen
}
});
requestBlocksHandlerMap.put(key, requestBlocksHandler);
+ log.info("requestBlocks with startBlockHeight={} from peer {}", startBlockHeight, peersNodeAddress);
requestBlocksHandler.requestBlocks();
} else {
//TODO check with re-orgs
diff --git a/core/src/main/java/bisq/core/dao/node/lite/network/RequestBlocksHandler.java b/core/src/main/java/bisq/core/dao/node/lite/network/RequestBlocksHandler.java
index 371a43f78f..4b3d6051e6 100644
--- a/core/src/main/java/bisq/core/dao/node/lite/network/RequestBlocksHandler.java
+++ b/core/src/main/java/bisq/core/dao/node/lite/network/RequestBlocksHandler.java
@@ -126,14 +126,14 @@ public class RequestBlocksHandler implements MessageListener {
TIMEOUT);
}
- log.debug("We send a {} to peer {}. ", getBlocksRequest.getClass().getSimpleName(), nodeAddress);
+ log.info("We send to peer {} a {}.", nodeAddress, getBlocksRequest);
networkNode.addMessageListener(this);
SettableFuture future = networkNode.sendMessage(nodeAddress, getBlocksRequest);
Futures.addCallback(future, new FutureCallback() {
@Override
public void onSuccess(Connection connection) {
if (!stopped) {
- log.trace("Send " + getBlocksRequest + " to " + nodeAddress + " succeeded.");
+ log.info("Sending of GetBlocksRequest message to peer {} succeeded.", nodeAddress.getHostName());
} else {
log.trace("We have stopped already. We ignore that networkNode.sendMessage.onSuccess call." +
"Might be caused by an previous timeout.");
@@ -178,6 +178,8 @@ public class RequestBlocksHandler implements MessageListener {
"RequestDataHandler.onMessage: connection.getPeersNodeAddressOptional() must be present " +
"at that moment");
cleanup();
+ log.info("We received from peer {} a BlocksResponse with {} blocks",
+ nodeAddress.getFullAddress(), getBlocksResponse.getBlocks().size());
listener.onComplete(getBlocksResponse);
} else {
log.warn("Nonce not matching. That can happen rarely if we get a response after a canceled " +
diff --git a/core/src/main/java/bisq/core/dao/node/messages/GetBlocksRequest.java b/core/src/main/java/bisq/core/dao/node/messages/GetBlocksRequest.java
index 3750f55ccf..ee66b69d1a 100644
--- a/core/src/main/java/bisq/core/dao/node/messages/GetBlocksRequest.java
+++ b/core/src/main/java/bisq/core/dao/node/messages/GetBlocksRequest.java
@@ -32,11 +32,9 @@ import java.util.List;
import lombok.EqualsAndHashCode;
import lombok.Getter;
-import lombok.ToString;
@EqualsAndHashCode(callSuper = true)
@Getter
-@ToString
public final class GetBlocksRequest extends NetworkEnvelope implements DirectMessage, CapabilityRequiringPayload {
private final int fromBlockHeight;
private final int nonce;
@@ -75,4 +73,13 @@ public final class GetBlocksRequest extends NetworkEnvelope implements DirectMes
Capabilities.Capability.DAO_FULL_NODE.ordinal()
));
}
+
+
+ @Override
+ public String toString() {
+ return "GetBlocksRequest{" +
+ "\n fromBlockHeight=" + fromBlockHeight +
+ ",\n nonce=" + nonce +
+ "\n} " + super.toString();
+ }
}
diff --git a/core/src/main/java/bisq/core/dao/node/messages/GetBlocksResponse.java b/core/src/main/java/bisq/core/dao/node/messages/GetBlocksResponse.java
index ad8c6493e4..c8b244d2ef 100644
--- a/core/src/main/java/bisq/core/dao/node/messages/GetBlocksResponse.java
+++ b/core/src/main/java/bisq/core/dao/node/messages/GetBlocksResponse.java
@@ -75,4 +75,13 @@ public final class GetBlocksResponse extends NetworkEnvelope implements DirectMe
proto.getRequestNonce(),
messageVersion);
}
+
+
+ @Override
+ public String toString() {
+ return "GetBlocksResponse{" +
+ "\n blocks=" + blocks +
+ ",\n requestNonce=" + requestNonce +
+ "\n} " + super.toString();
+ }
}
diff --git a/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java b/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java
index 2e0f5b433b..ccba248673 100644
--- a/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java
+++ b/core/src/main/java/bisq/core/dao/node/parser/BlockParser.java
@@ -122,7 +122,7 @@ public class BlockParser {
private void validateIfBlockIsConnecting(RawBlock rawBlock) throws BlockNotConnectingException {
LinkedList blocks = bsqStateService.getBlocks();
if (!isBlockConnecting(rawBlock, blocks)) {
- final Block last = blocks.getLast();
+ Block last = blocks.getLast();
log.warn("addBlock called with a not connecting block. New block:\n" +
"height()={}, hash()={}, lastBlock.height()={}, lastBlock.hash()={}",
rawBlock.getHeight(),
diff --git a/core/src/main/java/bisq/core/dao/node/parser/GenesisTxParser.java b/core/src/main/java/bisq/core/dao/node/parser/GenesisTxParser.java
new file mode 100644
index 0000000000..83f22913c3
--- /dev/null
+++ b/core/src/main/java/bisq/core/dao/node/parser/GenesisTxParser.java
@@ -0,0 +1,88 @@
+/*
+ * 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.node.parser;
+
+import bisq.core.dao.node.parser.exceptions.InvalidGenesisTxException;
+import bisq.core.dao.state.BsqStateService;
+import bisq.core.dao.state.blockchain.RawTx;
+import bisq.core.dao.state.blockchain.TempTx;
+import bisq.core.dao.state.blockchain.TempTxOutput;
+import bisq.core.dao.state.blockchain.Tx;
+import bisq.core.dao.state.blockchain.TxOutput;
+import bisq.core.dao.state.blockchain.TxOutputType;
+import bisq.core.dao.state.blockchain.TxType;
+
+import org.bitcoinj.core.Coin;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+public class GenesisTxParser {
+ public static boolean isGenesis(RawTx rawTx, String genesisTxId, int genesisBlockHeight) {
+ return rawTx.getBlockHeight() == genesisBlockHeight && rawTx.getId().equals(genesisTxId);
+ }
+
+ public static Tx getGenesisTx(RawTx rawTx, Coin genesisTotalSupply, BsqStateService bsqStateService) {
+ TempTx genesisTx = getGenesisTempTx(rawTx, genesisTotalSupply);
+ commitUTXOs(bsqStateService, genesisTx);
+ return Tx.fromTempTx(genesisTx);
+ }
+
+ private static void commitUTXOs(BsqStateService bsqStateService, TempTx genesisTx) {
+ ImmutableList outputs = genesisTx.getTempTxOutputs();
+ for (int i = 0; i < outputs.size(); ++i) {
+ TempTxOutput tempTxOutput = outputs.get(i);
+ bsqStateService.addUnspentTxOutput(TxOutput.fromTempOutput(tempTxOutput));
+ }
+ }
+
+ /**
+ * Parse and return the genesis transaction for bisq, if applicable.
+ *
+ * @param rawTx The candidate transaction.
+ * @param genesisTotalSupply The total supply of the genesis issuance for bisq.
+ * @return The genesis transaction.
+ */
+ @VisibleForTesting
+ static TempTx getGenesisTempTx(RawTx rawTx, Coin genesisTotalSupply) {
+ TempTx tempTx = TempTx.fromRawTx(rawTx);
+ tempTx.setTxType(TxType.GENESIS);
+ long remainingInputValue = genesisTotalSupply.getValue();
+ List tempTxOutputs = tempTx.getTempTxOutputs();
+ for (int i = 0; i < tempTxOutputs.size(); ++i) {
+ TempTxOutput txOutput = tempTxOutputs.get(i);
+ long value = txOutput.getValue();
+ boolean isValid = value <= remainingInputValue;
+ if (!isValid)
+ throw new InvalidGenesisTxException("Genesis tx is invalid; using more than available inputs. " +
+ "Remaining input value is " + remainingInputValue + " sat; tx info: " + tempTx.toString());
+
+ remainingInputValue -= value;
+ txOutput.setTxOutputType(TxOutputType.GENESIS_OUTPUT);
+ }
+
+ if (remainingInputValue > 0) {
+ throw new InvalidGenesisTxException("Genesis tx is invalid; not using all available inputs. " +
+ "Remaining input value is " + remainingInputValue + " sat, tx info: " + tempTx.toString());
+ }
+
+ return tempTx;
+ }
+}
diff --git a/core/src/main/java/bisq/core/dao/node/parser/OpReturnParser.java b/core/src/main/java/bisq/core/dao/node/parser/OpReturnParser.java
index f4a11b1974..832d151a50 100644
--- a/core/src/main/java/bisq/core/dao/node/parser/OpReturnParser.java
+++ b/core/src/main/java/bisq/core/dao/node/parser/OpReturnParser.java
@@ -58,17 +58,16 @@ class OpReturnParser {
* Parse the type of OP_RETURN data and validate it.
*
* @param tempTxOutput The temporary transaction output to parse.
- * @param isLastOutput If true, the output being parsed has a non-zero value.
* @return The type of the transaction output, which will be either one of the
* {@code *_OP_RETURN_OUTPUT} values, or {@code UNDEFINED} in case of
* unexpected state.
*/
- static TxOutputType getTxOutputType(TempTxOutput tempTxOutput, boolean isLastOutput) {
+ static TxOutputType getTxOutputType(TempTxOutput tempTxOutput) {
boolean nonZeroOutput = tempTxOutput.getValue() != 0;
byte[] opReturnData = tempTxOutput.getOpReturnData();
checkNotNull(opReturnData, "opReturnData must not be null");
- if (nonZeroOutput || !isLastOutput || opReturnData.length < 22) {
+ if (nonZeroOutput || opReturnData.length < 22) {
log.warn("OP_RETURN data does not match our rules. opReturnData={}",
Utils.HEX.encode(opReturnData));
return TxOutputType.INVALID_OUTPUT;
diff --git a/core/src/main/java/bisq/core/dao/node/parser/TxInputParser.java b/core/src/main/java/bisq/core/dao/node/parser/TxInputParser.java
index e2546099a7..01eb37c64f 100644
--- a/core/src/main/java/bisq/core/dao/node/parser/TxInputParser.java
+++ b/core/src/main/java/bisq/core/dao/node/parser/TxInputParser.java
@@ -25,9 +25,7 @@ import bisq.core.dao.state.blockchain.TxOutputType;
import javax.inject.Inject;
-import java.util.HashSet;
import java.util.Optional;
-import java.util.Set;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@@ -37,10 +35,9 @@ import lombok.extern.slf4j.Slf4j;
*/
@Slf4j
public class TxInputParser {
- enum VoteRevealInputState {
- UNKNOWN, VALID, INVALID
- }
+ private final BsqStateService bsqStateService;
+ // Getters
@Getter
private long accumulatedInputValue = 0;
@Getter
@@ -49,21 +46,27 @@ public class TxInputParser {
private int unlockBlockHeight;
@Getter
private Optional optionalSpentLockupTxOutput = Optional.empty();
-
- private final BsqStateService bsqStateService;
@Getter
- private TxInputParser.VoteRevealInputState voteRevealInputState = TxInputParser.VoteRevealInputState.UNKNOWN;
+ private boolean isUnLockInputValid = true;
- //TODO never read from... remove?
- // We use here TxOutput as we do not alter it but take it from the BsqState
- private Set spentUnlockConnectedTxOutputs = new HashSet<>();
+ // Private
+ private int numVoteRevealInputs = 0;
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor
+ ///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public TxInputParser(BsqStateService bsqStateService) {
this.bsqStateService = bsqStateService;
}
- @SuppressWarnings("IfCanBeSwitch")
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // API
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
void process(TxOutputKey txOutputKey, int blockHeight, String txId, int inputIndex) {
bsqStateService.getUnspentTxOutput(txOutputKey)
.ifPresent(connectedTxOutput -> {
@@ -74,7 +77,7 @@ public class TxInputParser {
// for later verification at the outputs of a reveal tx.
TxOutputType connectedTxOutputType = connectedTxOutput.getTxOutputType();
switch (connectedTxOutputType) {
- case UNDEFINED:
+ case UNDEFINED_OUTPUT:
case GENESIS_OUTPUT:
case BSQ_OUTPUT:
case BTC_OUTPUT:
@@ -84,43 +87,44 @@ public class TxInputParser {
case ISSUANCE_CANDIDATE_OUTPUT:
break;
case BLIND_VOTE_LOCK_STAKE_OUTPUT:
- if (voteRevealInputState == TxInputParser.VoteRevealInputState.UNKNOWN) {
- // The connected tx output of the blind vote tx is our input for the reveal tx.
- // We allow only one input from any blind vote tx otherwise the vote reveal tx is invalid.
- voteRevealInputState = TxInputParser.VoteRevealInputState.VALID;
- } else {
+ numVoteRevealInputs++;
+ // The connected tx output of the blind vote tx is our input for the reveal tx.
+ // We allow only one input from any blind vote tx otherwise the vote reveal tx is invalid.
+ if (!isVoteRevealInputValid()) {
log.warn("We have a tx which has >1 connected txOutputs marked as BLIND_VOTE_LOCK_STAKE_OUTPUT. " +
"This is not a valid BSQ tx.");
- voteRevealInputState = TxInputParser.VoteRevealInputState.INVALID;
}
break;
case BLIND_VOTE_OP_RETURN_OUTPUT:
case VOTE_REVEAL_UNLOCK_STAKE_OUTPUT:
case VOTE_REVEAL_OP_RETURN_OUTPUT:
break;
- case LOCKUP:
+ case LOCKUP_OUTPUT:
// A LOCKUP BSQ txOutput is spent to a corresponding UNLOCK
- // txOutput. The UNLOCK can only be spent after lockTime blocks has passed.
- if (!optionalSpentLockupTxOutput.isPresent()) {
+ // txInput. The UNLOCK can only be spent after lockTime blocks has passed.
+ isUnLockInputValid = !optionalSpentLockupTxOutput.isPresent();
+ if (isUnLockInputValid) {
optionalSpentLockupTxOutput = Optional.of(connectedTxOutput);
unlockBlockHeight = blockHeight + connectedTxOutput.getLockTime();
+ } else {
+ log.warn("We have a tx which has >1 connected txOutputs marked as LOCKUP_OUTPUT. " +
+ "This is not a valid BSQ tx.");
}
break;
case LOCKUP_OP_RETURN_OUTPUT:
+ // Cannot happen
break;
- case UNLOCK:
+ case UNLOCK_OUTPUT:
// This txInput is Spending an UNLOCK txOutput
- spentUnlockConnectedTxOutputs.add(connectedTxOutput);
+ int unlockBlockHeight = connectedTxOutput.getUnlockBlockHeight();
+ if (blockHeight < unlockBlockHeight) {
+ accumulatedInputValue -= inputValue;
+ burntBondValue += inputValue;
- //TODO We should add unlockBlockHeight to TempTxOutput and remove unlockBlockHeight from tempTx
- // then we can use connectedTxOutput to access the unlockBlockHeight instead of the tx
- bsqStateService.getTx(connectedTxOutput.getTxId()).ifPresent(unlockTx -> {
- // Only count the input as BSQ input if spent after unlock time
- if (blockHeight < unlockTx.getUnlockBlockHeight()) {
- accumulatedInputValue -= inputValue;
- burntBondValue += inputValue;
- }
- });
+ log.warn("We got a tx which spends the output from an unlock tx but before the " +
+ "unlockTime has passed. That leads to burned BSQ! " +
+ "blockHeight={}, unLockHeight={}", blockHeight, unlockBlockHeight);
+ }
break;
case INVALID_OUTPUT:
default:
@@ -131,4 +135,8 @@ public class TxInputParser {
bsqStateService.removeUnspentTxOutput(connectedTxOutput);
});
}
+
+ boolean isVoteRevealInputValid() {
+ return numVoteRevealInputs == 1;
+ }
}
diff --git a/core/src/main/java/bisq/core/dao/node/parser/TxOutputParser.java b/core/src/main/java/bisq/core/dao/node/parser/TxOutputParser.java
index 418b0d1062..8a082f4b29 100644
--- a/core/src/main/java/bisq/core/dao/node/parser/TxOutputParser.java
+++ b/core/src/main/java/bisq/core/dao/node/parser/TxOutputParser.java
@@ -20,13 +20,14 @@ package bisq.core.dao.node.parser;
import bisq.core.dao.bonding.BondingConsensus;
import bisq.core.dao.state.BsqStateService;
import bisq.core.dao.state.blockchain.OpReturnType;
-import bisq.core.dao.state.blockchain.TempTx;
import bisq.core.dao.state.blockchain.TempTxOutput;
import bisq.core.dao.state.blockchain.TxOutput;
import bisq.core.dao.state.blockchain.TxOutputType;
import com.google.common.annotations.VisibleForTesting;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Optional;
import lombok.Getter;
@@ -50,17 +51,14 @@ public class TxOutputParser {
@Setter
private int unlockBlockHeight;
@Setter
- private TempTx tempTx; //TODO remove
- @Setter
@Getter
private Optional optionalSpentLockupTxOutput = Optional.empty();
+ // Getters
@Getter
private boolean bsqOutputFound;
@Getter
- private Optional optionalOpReturnTypeCandidate = Optional.empty();
- @Getter
- private Optional optionalVerifiedOpReturnType = Optional.empty();
+ private Optional optionalOpReturnType = Optional.empty();
@Getter
private Optional optionalIssuanceCandidate = Optional.empty();
@Getter
@@ -70,51 +68,75 @@ public class TxOutputParser {
@Getter
private Optional optionalLockupOutput = Optional.empty();
+ // Private
+ private int lockTime;
+ private final List utxoCandidates = new ArrayList<>();
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
TxOutputParser(BsqStateService bsqStateService) {
this.bsqStateService = bsqStateService;
}
- public void processGenesisTxOutput(TempTx genesisTx) {
- for (int i = 0; i < genesisTx.getTempTxOutputs().size(); ++i) {
- TempTxOutput tempTxOutput = genesisTx.getTempTxOutputs().get(i);
- bsqStateService.addUnspentTxOutput(TxOutput.fromTempOutput(tempTxOutput));
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // API
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ void processOpReturnOutput(TempTxOutput tempTxOutput) {
+ byte[] opReturnData = tempTxOutput.getOpReturnData();
+ checkNotNull(opReturnData, "opReturnData must not be null");
+ TxOutputType txOutputType = OpReturnParser.getTxOutputType(tempTxOutput);
+ tempTxOutput.setTxOutputType(txOutputType);
+
+ optionalOpReturnType = getMappedOpReturnType(txOutputType);
+
+ // If we have a LOCKUP opReturn output we save the lockTime to apply it later to the LOCKUP output.
+ // We keep that data in that other output as it makes parsing of the UNLOCK tx easier.
+ optionalOpReturnType.filter(opReturnType -> opReturnType == OpReturnType.LOCKUP)
+ .ifPresent(opReturnType -> lockTime = BondingConsensus.getLockTime(opReturnData));
+ }
+
+ void processTxOutput(TempTxOutput tempTxOutput) {
+ // We don not expect here an opReturn output as we do not get called on the last output. Any opReturn at
+ // another output index is invalid.
+ if (tempTxOutput.isOpReturnOutput()) {
+ tempTxOutput.setTxOutputType(TxOutputType.INVALID_OUTPUT);
+ return;
+ }
+
+ long txOutputValue = tempTxOutput.getValue();
+ int index = tempTxOutput.getIndex();
+ if (isUnlockBondTx(tempTxOutput.getValue(), index)) {
+ // We need to handle UNLOCK transactions separately as they don't follow the pattern on spending BSQ
+ // The LOCKUP BSQ is burnt unless the output exactly matches the input, that would cause the
+ // output to not be BSQ output at all
+ handleUnlockBondTx(tempTxOutput);
+ } else if (availableInputValue > 0 && availableInputValue >= txOutputValue) {
+ handleBsqOutput(tempTxOutput, index, txOutputValue);
+ } else {
+ handleBtcOutput(tempTxOutput, index);
}
}
- void processOpReturnCandidate(TempTxOutput txOutput) {
- optionalOpReturnTypeCandidate = OpReturnParser.getOptionalOpReturnTypeCandidate(txOutput);
+ void commitUTXOCandidates() {
+ utxoCandidates.forEach(output -> bsqStateService.addUnspentTxOutput(TxOutput.fromTempOutput(output)));
}
/**
- * Process a transaction output.
- *
- * @param isLastOutput If it is the last output
- * @param tempTxOutput The TempTxOutput we are parsing
- * @param index The index in the outputs
+ * This sets all outputs to BTC_OUTPUT and doesn't add any txOutputs to the unspentTxOutput map in bsqStateService
*/
- void processTxOutput(boolean isLastOutput, TempTxOutput tempTxOutput, int index) {
- // We do not check for pubKeyScript.scriptType.NULL_DATA because that is only set if dumpBlockchainData is true
- byte[] opReturnData = tempTxOutput.getOpReturnData();
- if (opReturnData == null) {
- long txOutputValue = tempTxOutput.getValue();
- if (isUnlockBondTx(tempTxOutput.getValue(), index)) {
- // We need to handle UNLOCK transactions separately as they don't follow the pattern on spending BSQ
- // The LOCKUP BSQ is burnt unless the output exactly matches the input, that would cause the
- // output to not be BSQ output at all
- handleUnlockBondTx(tempTxOutput);
- } else if (availableInputValue > 0 && availableInputValue >= txOutputValue) {
- handleBsqOutput(tempTxOutput, index, txOutputValue);
- } else {
- handleBtcOutput(tempTxOutput, index);
- }
- } else {
- handleOpReturnOutput(tempTxOutput, isLastOutput);
- }
+ void invalidateUTXOCandidates() {
+ utxoCandidates.forEach(output -> output.setTxOutputType(TxOutputType.BTC_OUTPUT));
}
- boolean isOpReturnOutput(TempTxOutput txOutput) {
- return txOutput.getOpReturnData() != null;
- }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Private
+ ///////////////////////////////////////////////////////////////////////////////////////////
/**
* Whether a transaction is a valid unlock bond transaction or not.
@@ -135,12 +157,9 @@ public class TxOutputParser {
checkArgument(optionalSpentLockupTxOutput.isPresent(), "optionalSpentLockupTxOutput must be present");
availableInputValue -= optionalSpentLockupTxOutput.get().getValue();
- txOutput.setTxOutputType(TxOutputType.UNLOCK);
- bsqStateService.addUnspentTxOutput(TxOutput.fromTempOutput(txOutput));
-
- //TODO move up to TxParser
- // We should add unlockBlockHeight to TempTxOutput and remove unlockBlockHeight from tempTx
- tempTx.setUnlockBlockHeight(unlockBlockHeight);
+ txOutput.setTxOutputType(TxOutputType.UNLOCK_OUTPUT);
+ txOutput.setUnlockBlockHeight(unlockBlockHeight);
+ utxoCandidates.add(txOutput);
bsqOutputFound = true;
}
@@ -152,8 +171,8 @@ public class TxOutputParser {
boolean isFirstOutput = index == 0;
OpReturnType opReturnTypeCandidate = null;
- if (optionalOpReturnTypeCandidate.isPresent())
- opReturnTypeCandidate = optionalOpReturnTypeCandidate.get();
+ if (optionalOpReturnType.isPresent())
+ opReturnTypeCandidate = optionalOpReturnType.get();
TxOutputType bsqOutput;
if (isFirstOutput && opReturnTypeCandidate == OpReturnType.BLIND_VOTE) {
@@ -163,46 +182,40 @@ public class TxOutputParser {
bsqOutput = TxOutputType.VOTE_REVEAL_UNLOCK_STAKE_OUTPUT;
optionalVoteRevealUnlockStakeOutput = Optional.of(txOutput);
} else if (isFirstOutput && opReturnTypeCandidate == OpReturnType.LOCKUP) {
- bsqOutput = TxOutputType.LOCKUP;
+ bsqOutput = TxOutputType.LOCKUP_OUTPUT;
+
+ // We store the lockTime in the output which will be used as input for a unlock tx.
+ // That makes parsing of that data easier as if we would need to access it from the opReturn output of
+ // that tx.
+ txOutput.setLockTime(lockTime);
optionalLockupOutput = Optional.of(txOutput);
} else {
bsqOutput = TxOutputType.BSQ_OUTPUT;
}
txOutput.setTxOutputType(bsqOutput);
- bsqStateService.addUnspentTxOutput(TxOutput.fromTempOutput(txOutput));
+ utxoCandidates.add(txOutput);
bsqOutputFound = true;
}
private void handleBtcOutput(TempTxOutput txOutput, int index) {
- // If we have BSQ left for burning and at the second output a compensation request output we set the
- // candidate to the parsingModel and we don't apply the TxOutputType as we do that later as the OpReturn check.
+ // If we have BSQ left as fee and we are at the second output it might be a compensation request output.
+ // We store the candidate but we don't apply the TxOutputType yet as we need to verify the fee after all
+ // outputs are parsed and check the phase. The TxParser will do that....
if (availableInputValue > 0 &&
index == 1 &&
- optionalOpReturnTypeCandidate.isPresent() &&
- optionalOpReturnTypeCandidate.get() == OpReturnType.COMPENSATION_REQUEST) {
- // We don't set the txOutputType yet as we have not fully validated the tx but put the candidate
- // into our local optionalIssuanceCandidate.
-
+ optionalOpReturnType.isPresent() &&
+ optionalOpReturnType.get() == OpReturnType.COMPENSATION_REQUEST) {
optionalIssuanceCandidate = Optional.of(txOutput);
} else {
txOutput.setTxOutputType(TxOutputType.BTC_OUTPUT);
}
}
- private void handleOpReturnOutput(TempTxOutput tempTxOutput, boolean isLastOutput) {
- TxOutputType txOutputType = OpReturnParser.getTxOutputType(tempTxOutput, isLastOutput);
- tempTxOutput.setTxOutputType(txOutputType);
- optionalVerifiedOpReturnType = getMappedOpReturnType(txOutputType);
- optionalVerifiedOpReturnType.filter(verifiedOpReturnType -> verifiedOpReturnType == OpReturnType.LOCKUP)
- .ifPresent(verifiedOpReturnType -> {
- byte[] opReturnData = tempTxOutput.getOpReturnData();
- checkNotNull(opReturnData, "opReturnData must not be null");
- int lockTime = BondingConsensus.getLockTime(opReturnData);
- tempTxOutput.setLockTime(lockTime);
- });
- }
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Static
+ ///////////////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("WeakerAccess")
@VisibleForTesting
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 d1c3fb1bc6..69899218d5 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
@@ -17,7 +17,6 @@
package bisq.core.dao.node.parser;
-import bisq.core.dao.node.parser.exceptions.InvalidGenesisTxException;
import bisq.core.dao.state.BsqStateService;
import bisq.core.dao.state.blockchain.OpReturnType;
import bisq.core.dao.state.blockchain.RawTx;
@@ -25,6 +24,7 @@ import bisq.core.dao.state.blockchain.TempTx;
import bisq.core.dao.state.blockchain.TempTxOutput;
import bisq.core.dao.state.blockchain.Tx;
import bisq.core.dao.state.blockchain.TxInput;
+import bisq.core.dao.state.blockchain.TxOutput;
import bisq.core.dao.state.blockchain.TxOutputKey;
import bisq.core.dao.state.blockchain.TxOutputType;
import bisq.core.dao.state.blockchain.TxType;
@@ -32,8 +32,6 @@ import bisq.core.dao.state.governance.Param;
import bisq.core.dao.state.period.DaoPhase;
import bisq.core.dao.state.period.PeriodService;
-import bisq.common.app.DevEnv;
-
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
@@ -45,8 +43,6 @@ import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
-import static com.google.common.base.Preconditions.checkArgument;
-
/**
* Verifies if a given transaction is a BSQ transaction.
*/
@@ -54,10 +50,14 @@ import static com.google.common.base.Preconditions.checkArgument;
public class TxParser {
private final PeriodService periodService;
private final BsqStateService bsqStateService;
- private long remainingInputValue;
private TxOutputParser txOutputParser;
private TxInputParser txInputParser;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
@Inject
public TxParser(PeriodService periodService,
BsqStateService bsqStateService) {
@@ -65,176 +65,143 @@ public class TxParser {
this.bsqStateService = bsqStateService;
}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // API
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public Optional findTx(RawTx rawTx, String genesisTxId, int genesisBlockHeight, Coin genesisTotalSupply) {
+ if (GenesisTxParser.isGenesis(rawTx, genesisTxId, genesisBlockHeight))
+ return Optional.of(GenesisTxParser.getGenesisTx(rawTx, genesisTotalSupply, bsqStateService));
+ else
+ return findTx(rawTx);
+ }
+
// Apply state changes to tx, inputs and outputs
- // return true if any input contained BSQ
- // Any tx with BSQ input is a BSQ tx (except genesis tx but that is not handled in
- // that class).
+ // return Tx if any input contained BSQ
+ // Any tx with BSQ input is a BSQ tx.
// There might be txs without any valid BSQ txOutput but we still keep track of it,
// for instance to calculate the total burned BSQ.
- public Optional findTx(RawTx rawTx, String genesisTxId, int genesisBlockHeight, Coin genesisTotalSupply) {
- txInputParser = new TxInputParser(bsqStateService);
- txOutputParser = new TxOutputParser(bsqStateService);
-
- // Let's see if we have a genesis tx
- Optional optionalGenesisTx = TxParser.findGenesisTx(
- genesisTxId,
- genesisBlockHeight,
- genesisTotalSupply,
- rawTx);
- if (optionalGenesisTx.isPresent()) {
- TempTx genesisTx = optionalGenesisTx.get();
- txOutputParser.processGenesisTxOutput(genesisTx);
- return Optional.of(Tx.fromTempTx(genesisTx));
- }
-
- // If it is not a genesis tx we continue to parse to see if it is a valid BSQ tx.
+ private Optional findTx(RawTx rawTx) {
int blockHeight = rawTx.getBlockHeight();
- // We could pass tx also to the sub validators but as long we have not refactored the validators to pure
- // functions lets use the parsingModel.
TempTx tempTx = TempTx.fromRawTx(rawTx);
+ //****************************************************************************************
+ // Parse Inputs
+ //****************************************************************************************
+
+ txInputParser = new TxInputParser(bsqStateService);
for (int inputIndex = 0; inputIndex < tempTx.getTxInputs().size(); inputIndex++) {
TxInput input = tempTx.getTxInputs().get(inputIndex);
TxOutputKey outputKey = input.getConnectedTxOutputKey();
txInputParser.process(outputKey, blockHeight, rawTx.getId(), inputIndex);
}
+ // Results from txInputParser
long accumulatedInputValue = txInputParser.getAccumulatedInputValue();
- txOutputParser.setAvailableInputValue(accumulatedInputValue);
- txOutputParser.setUnlockBlockHeight(txInputParser.getUnlockBlockHeight());
- txOutputParser.setOptionalSpentLockupTxOutput(txInputParser.getOptionalSpentLockupTxOutput());
- txOutputParser.setTempTx(tempTx); //TODO remove
+ long burntBondValue = txInputParser.getBurntBondValue();
+ boolean unLockInputValid = txInputParser.isUnLockInputValid();
+ int unlockBlockHeight = txInputParser.getUnlockBlockHeight();
+ Optional optionalSpentLockupTxOutput = txInputParser.getOptionalSpentLockupTxOutput();
boolean hasBsqInputs = accumulatedInputValue > 0;
- if (hasBsqInputs) {
- final List outputs = tempTx.getTempTxOutputs();
- // We start with last output as that might be an OP_RETURN output and gives us the specific tx type, so it is
- // easier and cleaner at parsing the other outputs to detect which kind of tx we deal with.
- // Setting the opReturn type here does not mean it will be a valid BSQ tx as the checks are only partial and
- // BSQ inputs are not verified yet.
- // We keep the temporary opReturn type in the parsingModel object.
- checkArgument(!outputs.isEmpty(), "outputs must not be empty");
- int lastIndex = outputs.size() - 1;
- txOutputParser.processOpReturnCandidate(outputs.get(lastIndex));
+ boolean hasBurntBond = burntBondValue > 0;
- // We use order of output index. An output is a BSQ utxo as long there is enough input value
- // We iterate all outputs including the opReturn to do a full validation including the BSQ fee
- for (int index = 0; index < outputs.size(); index++) {
- boolean isLastOutput = index == lastIndex;
- txOutputParser.processTxOutput(isLastOutput,
- outputs.get(index),
- index
- );
- }
+ // If we don't have any BSQ in our input and we don't have burnt bonds we do not consider the tx as a BSQ tx.
+ if (!hasBsqInputs && !hasBurntBond)
+ return Optional.empty();
- remainingInputValue = txOutputParser.getAvailableInputValue();
- processOpReturnType(blockHeight, tempTx);
+ //****************************************************************************************
+ // Parse Outputs
+ //****************************************************************************************
- // We don't allow multiple opReturn outputs (they are non-standard but to be safe lets check it)
- long numOpReturnOutputs = tempTx.getTempTxOutputs().stream().filter(txOutputParser::isOpReturnOutput).count();
- if (numOpReturnOutputs <= 1) {
- boolean isAnyTxOutputTypeUndefined = tempTx.getTempTxOutputs().stream()
- .anyMatch(txOutput -> TxOutputType.UNDEFINED == txOutput.getTxOutputType());
- if (!isAnyTxOutputTypeUndefined) {
- // TODO(chirhonul): we don't modify the tempTx within the call below, so maybe we should
- // use RawTx?
- TxType txType = TxParser.getBisqTxType(
- tempTx,
- txOutputParser.getOptionalOpReturnTypeCandidate().isPresent(),
- remainingInputValue,
- getOptionalOpReturnType()
- );
- tempTx.setTxType(txType);
- if (remainingInputValue > 0)
- tempTx.setBurntFee(remainingInputValue);
- } else {
- tempTx.setTxType(TxType.INVALID);
- String msg = "We have undefined txOutput types which must not happen. tx=" + tempTx;
- DevEnv.logErrorAndThrowIfDevMode(msg);
- }
- } else {
- // We don't consider a tx with multiple OpReturn outputs valid.
- tempTx.setTxType(TxType.INVALID);
- String msg = "Invalid tx. We have multiple opReturn outputs. tx=" + tempTx;
- log.warn(msg);
- }
+ txOutputParser = new TxOutputParser(bsqStateService);
+ txOutputParser.setAvailableInputValue(accumulatedInputValue);
+ txOutputParser.setUnlockBlockHeight(unlockBlockHeight);
+ txOutputParser.setOptionalSpentLockupTxOutput(optionalSpentLockupTxOutput);
+
+ List outputs = tempTx.getTempTxOutputs();
+ // We start with last output as that might be an OP_RETURN output and gives us the specific tx type, so it is
+ // easier and cleaner at parsing the other outputs to detect which kind of tx we deal with.
+ int lastIndex = outputs.size() - 1;
+ int lastNonOpReturnIndex = lastIndex;
+ if (outputs.get(lastIndex).isOpReturnOutput()) {
+ txOutputParser.processOpReturnOutput(outputs.get(lastIndex));
+ lastNonOpReturnIndex -= 1;
}
- // TODO || parsingModel.getBurntBondValue() > 0; should not be necessary
- // How should we consider the burnt BSQ from spending a LOCKUP tx with the wrong format.
- // Example: LOCKUP txOutput is 1000 satoshi but first txOutput in spending tx is 900
- // satoshi, this burns the 1000 satoshi and is currently not considered in the
- // bsqInputBalancePositive, hence the need to check for parsingModel.getBurntBondValue
- // Perhaps adding boolean parsingModel.isBSQTx and checking for that would be better?
+ // We need to consider the order of the outputs. An output is a BSQ utxo as long there is enough input value
+ // We iterate all outputs (excluding an optional opReturn).
+ for (int index = 0; index <= lastNonOpReturnIndex; index++) {
+ txOutputParser.processTxOutput(outputs.get(index));
+ }
- if (hasBsqInputs || txInputParser.getBurntBondValue() > 0)
- return Optional.of(Tx.fromTempTx(tempTx));
- else
- return Optional.empty();
+ // Results from txOutputParser
+ long remainingInputValue = txOutputParser.getAvailableInputValue();
+ Optional optionalOpReturnType = txOutputParser.getOptionalOpReturnType();
+ boolean bsqOutputFound = txOutputParser.isBsqOutputFound();
+
+ long burntBsq = remainingInputValue + burntBondValue;
+ boolean hasBurntBSQ = burntBsq > 0;
+ if (hasBurntBSQ)
+ tempTx.setBurntFee(burntBsq);
+
+
+ //****************************************************************************************
+ // Verify and apply txType and txOutputTypes after we have all outputs parsed
+ //****************************************************************************************
+
+ applyTxTypeAndTxOutputType(blockHeight, tempTx, remainingInputValue);
+
+ TxType txType = evaluateTxType(tempTx, optionalOpReturnType, hasBurntBSQ, unLockInputValid);
+ tempTx.setTxType(txType);
+
+ if (isTxInvalid(tempTx, bsqOutputFound, hasBurntBond)) {
+ tempTx.setTxType(TxType.INVALID);
+ txOutputParser.invalidateUTXOCandidates();
+
+ if (hasBurntBSQ) {
+ log.warn("We have destroyed BSQ because of an invalid tx. Burned BSQ={}. tx={}",
+ burntBsq / 100D, tempTx);
+ }
+ } else {
+ txOutputParser.commitUTXOCandidates();
+ }
+
+ return Optional.of(Tx.fromTempTx(tempTx));
}
- private void processOpReturnType(int blockHeight, TempTx tempTx) {
- // We might have a opReturn output
- OpReturnType verifiedOpReturnType = null;
- Optional optionalVerifiedOpReturnType = txOutputParser.getOptionalVerifiedOpReturnType();
- if (optionalVerifiedOpReturnType.isPresent()) {
- verifiedOpReturnType = optionalVerifiedOpReturnType.get();
- long bsqFee = remainingInputValue;
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Private
+ ///////////////////////////////////////////////////////////////////////////////////////////
- boolean isFeeAndPhaseValid;
- switch (verifiedOpReturnType) {
+ /**
+ * This method verifies after all outputs are parsed if the opReturn type and the optional txOutputs required for
+ * certain use cases are valid.
+ * It verifies also if the fee is correct (if required) and if the phase is correct (if relevant).
+ * We set the txType as well as the txOutputType of the relevant outputs.
+ */
+ // TODO That method is not testable and still too complex.
+ private void applyTxTypeAndTxOutputType(int blockHeight, TempTx tempTx, long bsqFee) {
+ OpReturnType opReturnType = null;
+ Optional optionalOpReturnType = txOutputParser.getOptionalOpReturnType();
+ if (optionalOpReturnType.isPresent()) {
+ opReturnType = optionalOpReturnType.get();
+
+ switch (opReturnType) {
case PROPOSAL:
- isFeeAndPhaseValid = isFeeAndPhaseValid(blockHeight, bsqFee, DaoPhase.Phase.PROPOSAL, Param.PROPOSAL_FEE);
- if (!isFeeAndPhaseValid) {
- tempTx.setTxType(TxType.INVALID);
- }
+ processProposal(blockHeight, tempTx, bsqFee);
break;
case COMPENSATION_REQUEST:
- isFeeAndPhaseValid = isFeeAndPhaseValid(blockHeight, bsqFee, DaoPhase.Phase.PROPOSAL, Param.PROPOSAL_FEE);
- Optional optionalIssuanceCandidate = txOutputParser.getOptionalIssuanceCandidate();
- if (isFeeAndPhaseValid) {
- if (optionalIssuanceCandidate.isPresent()) {
- // Now after we have validated the opReturn data we will apply the TxOutputType
- optionalIssuanceCandidate.get().setTxOutputType(TxOutputType.ISSUANCE_CANDIDATE_OUTPUT);
- } else {
- log.warn("It can be that we have a opReturn which is correct from its structure but the whole tx " +
- "in not valid as the issuanceCandidate in not there. " +
- "As the BSQ fee is set it must be either a buggy tx or an manually crafted invalid tx.");
- }
- } else {
- tempTx.setTxType(TxType.INVALID);
- 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.
- }
+ processCompensationRequest(blockHeight, tempTx, bsqFee);
break;
case BLIND_VOTE:
- isFeeAndPhaseValid = isFeeAndPhaseValid(blockHeight, bsqFee, DaoPhase.Phase.BLIND_VOTE, Param.BLIND_VOTE_FEE);
- if (!isFeeAndPhaseValid) {
- tempTx.setTxType(TxType.INVALID);
- Optional optionalBlindVoteLockStakeOutput = txOutputParser.getOptionalBlindVoteLockStakeOutput();
- optionalBlindVoteLockStakeOutput.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.
- }
+ processBlindVote(blockHeight, tempTx, bsqFee);
break;
case VOTE_REVEAL:
- boolean isPhaseValid = isPhaseValid(blockHeight, DaoPhase.Phase.VOTE_REVEAL);
- boolean isVoteRevealInputInValid = txInputParser.getVoteRevealInputState() != TxInputParser.VoteRevealInputState.VALID;
- if (!isPhaseValid) {
- tempTx.setTxType(TxType.INVALID);
- }
- if (!isPhaseValid || isVoteRevealInputInValid) {
- Optional optionalVoteRevealUnlockStakeOutput = txOutputParser
- .getOptionalVoteRevealUnlockStakeOutput();
- optionalVoteRevealUnlockStakeOutput.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.
- }
+ processVoteReveal(blockHeight, tempTx);
break;
case LOCKUP:
// do nothing
@@ -242,32 +209,86 @@ public class TxParser {
}
}
- // We need to check if any temp txOutput is available and if so and the OpRetrun data is invalid we
- // set the output to a BTC output. We must not use if else cases here!
- if (verifiedOpReturnType != OpReturnType.COMPENSATION_REQUEST) {
+ // We need to check if any tempTxOutput is available and if so and the OpReturn data is invalid we
+ // set the output to a BTC output. We must not use `if else` cases here!
+ if (opReturnType != OpReturnType.COMPENSATION_REQUEST) {
txOutputParser.getOptionalIssuanceCandidate().ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT));
}
- if (verifiedOpReturnType != OpReturnType.BLIND_VOTE) {
+ if (opReturnType != OpReturnType.BLIND_VOTE) {
txOutputParser.getOptionalBlindVoteLockStakeOutput().ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT));
}
- if (verifiedOpReturnType != OpReturnType.VOTE_REVEAL) {
+ if (opReturnType != OpReturnType.VOTE_REVEAL) {
txOutputParser.getOptionalVoteRevealUnlockStakeOutput().ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT));
}
- if (verifiedOpReturnType != OpReturnType.LOCKUP) {
+ if (opReturnType != OpReturnType.LOCKUP) {
txOutputParser.getOptionalLockupOutput().ifPresent(tempTxOutput -> tempTxOutput.setTxOutputType(TxOutputType.BTC_OUTPUT));
}
}
+ private void processProposal(int blockHeight, TempTx tempTx, long bsqFee) {
+ boolean isFeeAndPhaseValid = isFeeAndPhaseValid(blockHeight, bsqFee, DaoPhase.Phase.PROPOSAL, Param.PROPOSAL_FEE);
+ if (!isFeeAndPhaseValid) {
+ tempTx.setTxType(TxType.INVALID);
+ }
+ }
+
+ private void processCompensationRequest(int blockHeight, TempTx tempTx, long bsqFee) {
+ boolean isFeeAndPhaseValid = isFeeAndPhaseValid(blockHeight, bsqFee, DaoPhase.Phase.PROPOSAL, Param.PROPOSAL_FEE);
+ Optional optionalIssuanceCandidate = txOutputParser.getOptionalIssuanceCandidate();
+ if (isFeeAndPhaseValid) {
+ if (optionalIssuanceCandidate.isPresent()) {
+ // Now after we have validated the fee and phase we will apply the TxOutputType
+ optionalIssuanceCandidate.get().setTxOutputType(TxOutputType.ISSUANCE_CANDIDATE_OUTPUT);
+ } else {
+ log.warn("It can be that we have a opReturn which is correct from its structure but the whole tx " +
+ "in not valid as the issuanceCandidate in not there. " +
+ "As the BSQ fee is set it must be either a buggy tx or an manually crafted invalid tx.");
+ tempTx.setTxType(TxType.INVALID);
+ }
+ } else {
+ tempTx.setTxType(TxType.INVALID);
+ 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.
+ }
+ }
+
+ private void processBlindVote(int blockHeight, TempTx tempTx, long bsqFee) {
+ boolean isFeeAndPhaseValid = isFeeAndPhaseValid(blockHeight, bsqFee, DaoPhase.Phase.BLIND_VOTE, Param.BLIND_VOTE_FEE);
+ if (!isFeeAndPhaseValid) {
+ tempTx.setTxType(TxType.INVALID);
+ txOutputParser.getOptionalBlindVoteLockStakeOutput().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.
+ }
+ }
+
+ private void processVoteReveal(int blockHeight, TempTx tempTx) {
+ boolean isPhaseValid = isPhaseValid(blockHeight, DaoPhase.Phase.VOTE_REVEAL);
+ if (!isPhaseValid) {
+ tempTx.setTxType(TxType.INVALID);
+ }
+
+ // We must not use an `if else` here!
+ if (!isPhaseValid || !txInputParser.isVoteRevealInputValid()) {
+ txOutputParser.getOptionalVoteRevealUnlockStakeOutput().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.
+ }
+ }
+
+
/**
* Whether the BSQ fee and phase is valid for a transaction.
*
- * @param blockHeight The height of the block that the transaction is in.
- * @param bsqFee The fee in BSQ, in satoshi.
- * @param phase The current phase of the DAO, e.g {@code DaoPhase.Phase.PROPOSAL}.
- * @param param The parameter for the fee, e.g {@code Param.PROPOSAL_FEE}.
+ * @param blockHeight The height of the block that the transaction is in.
+ * @param bsqFee The fee in BSQ, in satoshi.
+ * @param phase The current phase of the DAO, e.g {@code DaoPhase.Phase.PROPOSAL}.
+ * @param param The parameter for the fee, e.g {@code Param.PROPOSAL_FEE}.
* @return True if the fee and phase was valid, false otherwise.
*/
private boolean isFeeAndPhaseValid(int blockHeight, long bsqFee, DaoPhase.Phase phase, Param param) {
@@ -293,53 +314,99 @@ public class TxParser {
return isInPhase;
}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Static methods
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ @VisibleForTesting
+ // Performs various checks for an invalid tx
+ static boolean isTxInvalid(TempTx tempTx, boolean bsqOutputFound, boolean burntBondValue) {
+ if (tempTx.getTxType() == TxType.INVALID) {
+ // We got already set the invalid type in earlier checks and return early.
+ return true;
+ }
+
+ // We don't allow multiple opReturn outputs (they are non-standard but to be safe lets check it)
+ long numOpReturnOutputs = tempTx.getTempTxOutputs().stream()
+ .filter(TempTxOutput::isOpReturnOutput)
+ .count();
+ if (numOpReturnOutputs > 1) {
+ log.warn("Invalid tx. We have multiple opReturn outputs. tx=" + tempTx);
+ return true;
+ }
+
+ if (!bsqOutputFound) {
+ log.warn("Invalid Tx: No BSQ output found. tx=" + tempTx);
+ return true;
+ }
+
+ if (burntBondValue) {
+ log.warn("Invalid Tx: Bond value was burnt. tx=" + tempTx);
+ return true;
+ }
+
+ if (tempTx.getTempTxOutputs().stream()
+ .anyMatch(txOutput -> TxOutputType.UNDEFINED_OUTPUT == txOutput.getTxOutputType() ||
+ TxOutputType.INVALID_OUTPUT == txOutput.getTxOutputType())) {
+ log.warn("Invalid Tx: We have undefined or invalid txOutput types. tx=" + tempTx);
+ return true;
+ }
+
+ return false;
+ }
+
/**
* Retrieve the type of the transaction, assuming it is relevant to bisq.
*
- * @param tx The temporary transaction.
- * @param hasOpReturnCandidate True if we have a candidate for an OP_RETURN.
- * @param remainingInputValue The remaining value of inputs not yet accounted for, in satoshi.
- * @param optionalOpReturnType If present, the OP_RETURN type of the transaction.
+ * @param tempTx The temporary transaction.
+ * @param optionalOpReturnType The optional OP_RETURN type of the transaction.
+ * @param hasBurntBSQ If the have been remaining value from the inputs which got not spent in outputs.
+ * Might be valid BSQ fees or burned BSQ from an invalid tx.
* @return The type of the transaction, if it is relevant to bisq.
*/
@VisibleForTesting
- static TxType getBisqTxType(TempTx tx, boolean hasOpReturnCandidate, long remainingInputValue, Optional optionalOpReturnType) {
- TxType txType;
- // We need to have at least one BSQ output
+ static TxType evaluateTxType(TempTx tempTx, Optional optionalOpReturnType,
+ boolean hasBurntBSQ, boolean isUnLockInputValid) {
if (optionalOpReturnType.isPresent()) {
- log.debug("Optional OP_RETURN type is present for tx.");
- txType = TxParser.getTxTypeForOpReturn(tx, optionalOpReturnType.get());
- } else if (!hasOpReturnCandidate) {
- log.debug("No optional OP_RETURN type and no OP_RETURN candidate is present for tx.");
-
- boolean bsqFeesBurnt = remainingInputValue > 0;
- if (bsqFeesBurnt) {
- // Burned fee but no opReturn
- txType = TxType.PAY_TRADE_FEE;
- } else if (tx.getTempTxOutputs().get(0).getTxOutputType() == TxOutputType.UNLOCK) {
- txType = TxType.UNLOCK;
- } else {
- log.debug("No burned fee and no OP_RETURN, so this is a TRANSFER_BSQ tx.");
- txType = TxType.TRANSFER_BSQ;
- }
- } else {
- log.debug("No optional OP_RETURN type is present for tx but we do have an OP_RETURN candidate, so it failed validation.");
- txType = TxType.INVALID;
+ // We use the opReturnType to find the txType
+ return evaluateTxTypeFromOpReturnType(tempTx, optionalOpReturnType.get());
}
- return txType;
+ // No opReturnType, so we check for the remaining possible cases
+ if (hasBurntBSQ) {
+ // PAY_TRADE_FEE tx has a fee and no opReturn
+ return TxType.PAY_TRADE_FEE;
+ }
+
+ // UNLOCK tx has no fee, no opReturn but an UNLOCK_OUTPUT at first output.
+ if (tempTx.getTempTxOutputs().get(0).getTxOutputType() == TxOutputType.UNLOCK_OUTPUT) {
+ // We check if there have been invalid inputs
+ if (!isUnLockInputValid)
+ return TxType.INVALID;
+
+ // UNLOCK tx has no fee, no OpReturn
+ return TxType.UNLOCK;
+ }
+
+ // TRANSFER_BSQ has no fee, no opReturn and no UNLOCK_OUTPUT at first output
+ log.debug("No burned fee and no OP_RETURN, so this is a TRANSFER_BSQ tx.");
+ return TxType.TRANSFER_BSQ;
}
- private static TxType getTxTypeForOpReturn(TempTx tx, OpReturnType opReturnType) {
+ @VisibleForTesting
+ static TxType evaluateTxTypeFromOpReturnType(TempTx tempTx, OpReturnType opReturnType) {
switch (opReturnType) {
+ case PROPOSAL:
+ return TxType.PROPOSAL;
case COMPENSATION_REQUEST:
- boolean hasCorrectNumOutputs = tx.getTempTxOutputs().size() >= 3;
+ boolean hasCorrectNumOutputs = tempTx.getTempTxOutputs().size() >= 3;
if (!hasCorrectNumOutputs) {
log.warn("Compensation request tx need to have at least 3 outputs");
return TxType.INVALID;
}
- TempTxOutput issuanceTxOutput = tx.getTempTxOutputs().get(1);
+ TempTxOutput issuanceTxOutput = tempTx.getTempTxOutputs().get(1);
boolean hasIssuanceOutput = issuanceTxOutput.getTxOutputType() == TxOutputType.ISSUANCE_CANDIDATE_OUTPUT;
if (!hasIssuanceOutput) {
log.warn("Compensation request txOutput type of output at index 1 need to be ISSUANCE_CANDIDATE_OUTPUT. " +
@@ -348,8 +415,6 @@ public class TxParser {
}
return TxType.COMPENSATION_REQUEST;
- case PROPOSAL:
- return TxType.PROPOSAL;
case BLIND_VOTE:
return TxType.BLIND_VOTE;
case VOTE_REVEAL:
@@ -357,79 +422,8 @@ public class TxParser {
case LOCKUP:
return TxType.LOCKUP;
default:
- log.warn("We got a BSQ tx with fee and unknown OP_RETURN. tx={}", tx);
+ log.warn("We got a BSQ tx with an unknown OP_RETURN. tx={}, opReturnType={}", tempTx, opReturnType);
return TxType.INVALID;
}
}
-
- /**
- * The type of the OP_RETURN value of the transaction, if it has such a BSQ output.
- *
- * @return The OP_RETURN type if applicable, otherwise Optional.empty().
- */
- private Optional getOptionalOpReturnType() {
- if (txOutputParser.isBsqOutputFound()) {
- // We want to be sure that the initial assumption of the opReturn type was matching the result after full
- // validation.
- Optional optionalOpReturnTypeCandidate = txOutputParser.getOptionalOpReturnTypeCandidate();
- if (optionalOpReturnTypeCandidate.isPresent()) {
- OpReturnType opReturnTypeCandidate = optionalOpReturnTypeCandidate.get();
- Optional optionalVerifiedOpReturnType = txOutputParser.getOptionalVerifiedOpReturnType();
- if (optionalVerifiedOpReturnType.isPresent()) {
- OpReturnType verifiedOpReturnType = optionalVerifiedOpReturnType.get();
- if (opReturnTypeCandidate == verifiedOpReturnType) {
- return optionalVerifiedOpReturnType;
- }
- }
- }
-
- String msg = "We got a different opReturn type after validation as we expected initially. " +
- "optionalOpReturnTypeCandidate=" + optionalOpReturnTypeCandidate +
- ", optionalVerifiedOpReturnType=" + txOutputParser.getOptionalVerifiedOpReturnType();
- log.error(msg);
-
- } else {
- String msg = "We got a tx without any valid BSQ output but with burned BSQ. " +
- "Burned fee=" + remainingInputValue / 100D + " BSQ.";
- log.warn(msg);
- }
- return Optional.empty();
- }
-
- /**
- * Parse and return the genesis transaction for bisq, if applicable.
- *
- * @param genesisTxId The transaction id of the bisq genesis transaction.
- * @param genesisBlockHeight The block height of the bisq genesis transaction.
- * @param genesisTotalSupply The total supply of the genesis issuance for bisq.
- * @param rawTx The candidate transaction.
- * @return The genesis transaction if applicable, or Optional.empty() otherwise.
- */
- public static Optional findGenesisTx(String genesisTxId, int genesisBlockHeight, Coin genesisTotalSupply,
- RawTx rawTx) {
- boolean isGenesis = rawTx.getBlockHeight() == genesisBlockHeight &&
- rawTx.getId().equals(genesisTxId);
- if (!isGenesis)
- return Optional.empty();
-
- TempTx tempTx = TempTx.fromRawTx(rawTx);
- tempTx.setTxType(TxType.GENESIS);
- long remainingInputValue = genesisTotalSupply.getValue();
- for (int i = 0; i < tempTx.getTempTxOutputs().size(); ++i) {
- TempTxOutput txOutput = tempTx.getTempTxOutputs().get(i);
- long value = txOutput.getValue();
- boolean isValid = value <= remainingInputValue;
- if (!isValid)
- throw new InvalidGenesisTxException("Genesis tx is invalid; using more than available inputs. " +
- "Remaining input value is " + remainingInputValue + " sat; tx info: " + tempTx.toString());
-
- remainingInputValue -= value;
- txOutput.setTxOutputType(TxOutputType.GENESIS_OUTPUT);
- }
- if (remainingInputValue > 0) {
- throw new InvalidGenesisTxException("Genesis tx is invalid; not using all available inputs. " +
- "Remaining input value is " + remainingInputValue + " sat, tx info: " + tempTx.toString());
- }
- return Optional.of(tempTx);
- }
}
diff --git a/core/src/main/java/bisq/core/dao/state/BsqState.java b/core/src/main/java/bisq/core/dao/state/BsqState.java
index b46c3b2386..62237ff8da 100644
--- a/core/src/main/java/bisq/core/dao/state/BsqState.java
+++ b/core/src/main/java/bisq/core/dao/state/BsqState.java
@@ -50,12 +50,6 @@ import lombok.extern.slf4j.Slf4j;
*/
@Slf4j
public class BsqState implements PersistableEnvelope {
- //TODO not sure if we will use that
- /* private static final int ISSUANCE_MATURITY = 144 * 30; // 30 days
-
- static int getIssuanceMaturity() {
- return ISSUANCE_MATURITY;
- }*/
///////////////////////////////////////////////////////////////////////////////////////////
// Fields
@@ -132,10 +126,10 @@ public class BsqState implements PersistableEnvelope {
@Override
public Message toProtoMessage() {
- return PB.PersistableEnvelope.newBuilder().setBsqState(getStateBuilder()).build();
+ return PB.PersistableEnvelope.newBuilder().setBsqState(getBsqStateBuilder()).build();
}
- private PB.BsqState.Builder getStateBuilder() {
+ private PB.BsqState.Builder getBsqStateBuilder() {
final PB.BsqState.Builder builder = PB.BsqState.newBuilder();
builder.setChainHeight(chainHeight)
.addAllBlocks(blocks.stream().map(Block::toProtoMessage).collect(Collectors.toList()))
@@ -193,10 +187,10 @@ public class BsqState implements PersistableEnvelope {
}
BsqState getClone() {
- return (BsqState) BsqState.fromProto(getStateBuilder().build());
+ return (BsqState) BsqState.fromProto(getBsqStateBuilder().build());
}
BsqState getClone(BsqState snapshotCandidate) {
- return (BsqState) BsqState.fromProto(snapshotCandidate.getStateBuilder().build());
+ return (BsqState) BsqState.fromProto(snapshotCandidate.getBsqStateBuilder().build());
}
}
diff --git a/core/src/main/java/bisq/core/dao/state/BsqStateService.java b/core/src/main/java/bisq/core/dao/state/BsqStateService.java
index 97eabda13d..3932930e5d 100644
--- a/core/src/main/java/bisq/core/dao/state/BsqStateService.java
+++ b/core/src/main/java/bisq/core/dao/state/BsqStateService.java
@@ -120,10 +120,6 @@ public class BsqStateService implements DaoSetupService {
return bsqState.getClone();
}
- public LinkedList getBlocksFromState(BsqState bsqState) {
- return new LinkedList<>(bsqState.getBlocks());
- }
-
///////////////////////////////////////////////////////////////////////////////////////////
// ChainHeight
@@ -387,7 +383,7 @@ public class BsqStateService implements DaoSetupService {
TxOutput txOutput = optionalTxOutput.get();
switch (txOutput.getTxOutputType()) {
- case UNDEFINED:
+ case UNDEFINED_OUTPUT:
return false;
case GENESIS_OUTPUT:
case BSQ_OUTPUT:
@@ -404,11 +400,11 @@ public class BsqStateService implements DaoSetupService {
case VOTE_REVEAL_UNLOCK_STAKE_OUTPUT:
case VOTE_REVEAL_OP_RETURN_OUTPUT:
return true;
- case LOCKUP:
+ case LOCKUP_OUTPUT:
return false;
case LOCKUP_OP_RETURN_OUTPUT:
return true;
- case UNLOCK:
+ case UNLOCK_OUTPUT:
return isLockTimeOverForUnlockTxOutput(txOutput);
case INVALID_OUTPUT:
return false;
@@ -431,7 +427,7 @@ public class BsqStateService implements DaoSetupService {
public boolean isBsqTxOutputType(TxOutput txOutput) {
final TxOutputType txOutputType = txOutput.getTxOutputType();
switch (txOutputType) {
- case UNDEFINED:
+ case UNDEFINED_OUTPUT:
return false;
case GENESIS_OUTPUT:
case BSQ_OUTPUT:
@@ -447,9 +443,9 @@ public class BsqStateService implements DaoSetupService {
case BLIND_VOTE_OP_RETURN_OUTPUT:
case VOTE_REVEAL_UNLOCK_STAKE_OUTPUT:
case VOTE_REVEAL_OP_RETURN_OUTPUT:
- case LOCKUP:
+ case LOCKUP_OUTPUT:
case LOCKUP_OP_RETURN_OUTPUT:
- case UNLOCK:
+ case UNLOCK_OUTPUT:
return true;
case INVALID_OUTPUT:
return false;
@@ -564,7 +560,7 @@ public class BsqStateService implements DaoSetupService {
public Optional getLockupHash(TxOutput txOutput) {
Optional lockupTx = Optional.empty();
String txId = txOutput.getTxId();
- if (txOutput.getTxOutputType() == TxOutputType.LOCKUP) {
+ if (txOutput.getTxOutputType() == TxOutputType.LOCKUP_OUTPUT) {
lockupTx = getTx(txId);
} else if (isUnlockTxOutputAndLockTimeNotOver(txOutput)) {
if (getTx(txId).isPresent()) {
@@ -591,7 +587,7 @@ public class BsqStateService implements DaoSetupService {
}*/
public boolean isUnlockTxOutputAndLockTimeNotOver(TxOutput txOutput) {
- return txOutput.getTxOutputType() == TxOutputType.UNLOCK && !isLockTimeOverForUnlockTxOutput(txOutput);
+ return txOutput.getTxOutputType() == TxOutputType.UNLOCK_OUTPUT && !isLockTimeOverForUnlockTxOutput(txOutput);
}
// Lockup
@@ -601,15 +597,15 @@ public class BsqStateService implements DaoSetupService {
}
public boolean isLockupOutput(TxOutput txOutput) {
- return txOutput.getTxOutputType() == TxOutputType.LOCKUP;
+ return txOutput.getTxOutputType() == TxOutputType.LOCKUP_OUTPUT;
}
public Set getLockupTxOutputs() {
- return getTxOutputsByTxOutputType(TxOutputType.LOCKUP);
+ return getTxOutputsByTxOutputType(TxOutputType.LOCKUP_OUTPUT);
}
public Set getUnlockTxOutputs() {
- return getTxOutputsByTxOutputType(TxOutputType.UNLOCK);
+ return getTxOutputsByTxOutputType(TxOutputType.UNLOCK_OUTPUT);
}
public Optional getLockupTxOutput(String txId) {
@@ -633,13 +629,13 @@ public class BsqStateService implements DaoSetupService {
// Unlock
public boolean isUnlockOutput(TxOutput txOutput) {
- return txOutput.getTxOutputType() == TxOutputType.UNLOCK;
+ return txOutput.getTxOutputType() == TxOutputType.UNLOCK_OUTPUT;
}
// Unlocking
// Return UNLOCK TxOutputs that are not yet spendable as lockTime is not over
public Stream getUnspentUnlockingTxOutputsStream() {
- return getTxOutputsByTxOutputType(TxOutputType.UNLOCK).stream()
+ return getTxOutputsByTxOutputType(TxOutputType.UNLOCK_OUTPUT).stream()
.filter(txOutput -> isUnspent(txOutput.getKey()))
.filter(txOutput -> !isLockTimeOverForUnlockTxOutput(txOutput));
}
@@ -657,7 +653,7 @@ public class BsqStateService implements DaoSetupService {
// TODO SQ i changed the code here. i think it was wrong before
public boolean isUnlockingOutput(TxOutput unlockTxOutput) {
- return unlockTxOutput.getTxOutputType() == TxOutputType.UNLOCK &&
+ return unlockTxOutput.getTxOutputType() == TxOutputType.UNLOCK_OUTPUT &&
!isLockTimeOverForUnlockTxOutput(unlockTxOutput);
}
@@ -675,7 +671,7 @@ public class BsqStateService implements DaoSetupService {
// We don't care here about the unspent state
public Stream getUnlockedTxOutputsStream() {
- return getTxOutputsByTxOutputType(TxOutputType.UNLOCK).stream()
+ return getTxOutputsByTxOutputType(TxOutputType.UNLOCK_OUTPUT).stream()
.filter(this::isLockTimeOverForUnlockTxOutput);
}
@@ -707,7 +703,7 @@ public class BsqStateService implements DaoSetupService {
}
getTxOutputStream()
.filter(txOutput -> isUnspent(txOutput.getKey()))
- .filter(txOutput -> txOutput.getTxOutputType() == TxOutputType.LOCKUP ||
+ .filter(txOutput -> txOutput.getTxOutputType() == TxOutputType.LOCKUP_OUTPUT ||
(isUnlockTxOutputAndLockTimeNotOver(txOutput)))
.filter(txOutput -> {
Optional hash = getLockupHash(txOutput);
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 30c24a6eb8..db90bc859d 100644
--- a/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java
+++ b/core/src/main/java/bisq/core/dao/state/GenesisTxInfo.java
@@ -17,6 +17,8 @@
package bisq.core.dao.state;
+import bisq.core.app.BisqEnvironment;
+import bisq.core.btc.BaseCurrencyNetwork;
import bisq.core.dao.DaoOptionKeys;
import org.bitcoinj.core.Coin;
@@ -27,8 +29,6 @@ import javax.inject.Named;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
-import javax.annotation.Nullable;
-
/**
* Encapsulate the genesis txId and height.
@@ -45,8 +45,11 @@ public class GenesisTxInfo {
public static final Coin GENESIS_TOTAL_SUPPLY = Coin.parseCoin("2.5");
- private static final String DEFAULT_GENESIS_TX_ID = "81855816eca165f17f0668898faa8724a105196e90ffc4993f4cac980176674e";
- private static final int DEFAULT_GENESIS_BLOCK_HEIGHT = 524717; // 2018-05-27
+ private static final String MAINNET_GENESIS_TX_ID = "81855816eca165f17f0668898faa8724a105196e90ffc4993f4cac980176674e";
+ private static final int MAINNET_GENESIS_BLOCK_HEIGHT = 524717; // 2018-05-27
+
+ private static final String TESTNET_GENESIS_TX_ID = "7085539068b4fc27dfc6c39b0feae2adc7fe20f925e79ca0ba064725fe6c9991";
+ private static final int TESTNET_GENESIS_BLOCK_HEIGHT = 1414332; // 2018-09-25
///////////////////////////////////////////////////////////////////////////////////////////
@@ -60,14 +63,6 @@ public class GenesisTxInfo {
@Getter
private final int genesisBlockHeight;
-
- //TODO not sure if we will use that
- /* private static final int ISSUANCE_MATURITY = 144 * 30; // 30 days
-
- static int getIssuanceMaturity() {
- return ISSUANCE_MATURITY;
- }*/
-
// mainnet
// this tx has a lot of outputs
// https://blockchain.info/de/tx/ee921650ab3f978881b8fe291e0c025e0da2b7dc684003d7a03d9649dfee2e15
@@ -85,15 +80,34 @@ public class GenesisTxInfo {
// private static final String DEFAULT_GENESIS_TX_ID = "--";
// private static final int DEFAULT_GENESIS_BLOCK_HEIGHT = 499000; // recursive test 137298, 499000 dec 2017
-
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
- public GenesisTxInfo(@Nullable @Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId,
- @Named(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) int genesisBlockHeight) {
- this.genesisTxId = genesisTxId != null ? genesisTxId : DEFAULT_GENESIS_TX_ID;
- this.genesisBlockHeight = genesisBlockHeight != 0 ? genesisBlockHeight : DEFAULT_GENESIS_BLOCK_HEIGHT;
+ public GenesisTxInfo(@Named(DaoOptionKeys.GENESIS_TX_ID) String genesisTxId,
+ @Named(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) Integer genesisBlockHeight) {
+ BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork();
+ boolean isMainnet = baseCurrencyNetwork.isMainnet();
+ boolean isTestnet = baseCurrencyNetwork.isTestnet();
+ if (!genesisTxId.isEmpty()) {
+ this.genesisTxId = genesisTxId;
+ } else if (isMainnet) {
+ this.genesisTxId = MAINNET_GENESIS_TX_ID;
+ } else if (isTestnet) {
+ this.genesisTxId = TESTNET_GENESIS_TX_ID;
+ } else {
+ this.genesisTxId = "genesisTxId is undefined";
+ }
+
+ if (genesisBlockHeight > -1) {
+ this.genesisBlockHeight = genesisBlockHeight;
+ } else if (isMainnet) {
+ this.genesisBlockHeight = MAINNET_GENESIS_BLOCK_HEIGHT;
+ } else if (isTestnet) {
+ this.genesisBlockHeight = TESTNET_GENESIS_BLOCK_HEIGHT;
+ } else {
+ this.genesisBlockHeight = 0;
+ }
}
}
diff --git a/core/src/main/java/bisq/core/dao/state/SnapshotManager.java b/core/src/main/java/bisq/core/dao/state/SnapshotManager.java
index d86e98db34..fee52cd4c2 100644
--- a/core/src/main/java/bisq/core/dao/state/SnapshotManager.java
+++ b/core/src/main/java/bisq/core/dao/state/SnapshotManager.java
@@ -29,20 +29,24 @@ import com.google.common.annotations.VisibleForTesting;
import java.io.File;
+import java.util.LinkedList;
+
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Manages snapshots of BsqState.
- * // FIXME not working correctly anymore
+ *
+ * One BSQ block with empty txs adds 152 bytes which results in about 8 MB/year
*/
@Slf4j
public class SnapshotManager implements BsqStateListener {
- private static final int SNAPSHOT_GRID = 11000;
+ private static final int SNAPSHOT_GRID = 100;
private final BsqState bsqState;
private final BsqStateService bsqStateService;
+ private final GenesisTxInfo genesisTxInfo;
private final Storage storage;
private BsqState snapshotCandidate;
@@ -51,9 +55,11 @@ public class SnapshotManager implements BsqStateListener {
public SnapshotManager(BsqState bsqState,
BsqStateService bsqStateService,
PersistenceProtoResolver persistenceProtoResolver,
+ GenesisTxInfo genesisTxInfo,
@Named(Storage.STORAGE_DIR) File storageDir) {
this.bsqState = bsqState;
this.bsqStateService = bsqStateService;
+ this.genesisTxInfo = genesisTxInfo;
storage = new Storage<>(storageDir, persistenceProtoResolver);
this.bsqStateService.addBsqStateListener(this);
@@ -70,21 +76,22 @@ public class SnapshotManager implements BsqStateListener {
@Override
public void onParseTxsComplete(Block block) {
- final int chainHeadHeight = block.getHeight();
- if (isSnapshotHeight(chainHeadHeight) &&
+ int chainHeight = block.getHeight();
+ if (isSnapshotHeight(chainHeight) &&
(snapshotCandidate == null ||
- snapshotCandidate.getChainHeight() != chainHeadHeight)) {
+ snapshotCandidate.getChainHeight() != chainHeight)) {
// At trigger event we store the latest snapshotCandidate to disc
if (snapshotCandidate != null) {
// We clone because storage is in a threaded context
final BsqState cloned = bsqState.getClone(snapshotCandidate);
- storage.queueUpForSave(cloned);
- log.info("Saved snapshotCandidate to Disc at height " + chainHeadHeight);
+ if (cloned.getBlocks().getLast().getHeight() >= genesisTxInfo.getGenesisBlockHeight())
+ storage.queueUpForSave(cloned);
+ log.info("Saved snapshotCandidate to Disc at height " + chainHeight);
}
// Now we clone and keep it in memory for the next trigger
snapshotCandidate = bsqState.getClone();
// don't access cloned anymore with methods as locks are transient!
- log.debug("Cloned new snapshotCandidate at height " + chainHeadHeight);
+ log.info("Cloned new snapshotCandidate at height " + chainHeight);
}
}
@@ -101,8 +108,9 @@ public class SnapshotManager implements BsqStateListener {
checkNotNull(storage, "storage must not be null");
BsqState persisted = storage.initAndGetPersisted(bsqState, 100);
if (persisted != null) {
- log.info("applySnapshot persisted.chainHeadHeight=" + bsqStateService.getBlocksFromState(persisted).getLast().getHeight());
- bsqStateService.applySnapshot(persisted);
+ log.info("applySnapshot persisted.chainHeight=" + new LinkedList<>(persisted.getBlocks()).getLast().getHeight());
+ if (persisted.getBlocks().getLast().getHeight() >= genesisTxInfo.getGenesisBlockHeight())
+ bsqStateService.applySnapshot(persisted);
} else {
log.info("Try to apply snapshot but no stored snapshot available");
}
diff --git a/core/src/main/java/bisq/core/dao/state/blockchain/BaseBlock.java b/core/src/main/java/bisq/core/dao/state/blockchain/BaseBlock.java
index 3ea8349c9f..ebc6c4b4f7 100644
--- a/core/src/main/java/bisq/core/dao/state/blockchain/BaseBlock.java
+++ b/core/src/main/java/bisq/core/dao/state/blockchain/BaseBlock.java
@@ -19,8 +19,11 @@ package bisq.core.dao.state.blockchain;
import io.bisq.generated.protobuffer.PB;
+import java.util.Optional;
+
import lombok.Data;
+import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
@@ -33,6 +36,7 @@ public abstract class BaseBlock {
protected final int height;
protected final long time; // in ms
protected final String hash;
+ @Nullable // in case of first block in the blockchain
protected final String previousBlockHash;
BaseBlock(int height, long time, String hash, String previousBlockHash) {
@@ -48,11 +52,13 @@ public abstract class BaseBlock {
///////////////////////////////////////////////////////////////////////////////////////////
PB.BaseBlock.Builder getBaseBlockBuilder() {
- return PB.BaseBlock.newBuilder()
+ PB.BaseBlock.Builder builder = PB.BaseBlock.newBuilder()
.setHeight(height)
.setTime(time)
- .setHash(hash)
- .setPreviousBlockHash(previousBlockHash);
+ .setHash(hash);
+ Optional.ofNullable(previousBlockHash).ifPresent(builder::setPreviousBlockHash);
+ return builder;
+
}
@Override
diff --git a/core/src/main/java/bisq/core/dao/state/blockchain/ScriptType.java b/core/src/main/java/bisq/core/dao/state/blockchain/ScriptType.java
index e89ae2bc13..523f9928f5 100644
--- a/core/src/main/java/bisq/core/dao/state/blockchain/ScriptType.java
+++ b/core/src/main/java/bisq/core/dao/state/blockchain/ScriptType.java
@@ -35,7 +35,7 @@ import lombok.ToString;
@JsonIgnoreProperties(ignoreUnknown = true)
public enum ScriptType {
- // https://github.com/bitcoin/bitcoin/blob/8152d3fe57a991e9088d0b9d261d2b10936f45a9/src/script/standard.cpp
+ // https://github.com/bitcoin/bitcoin/blob/master/src/script/standard.cpp
PUB_KEY("pubkey"),
PUB_KEY_HASH("pubkeyhash"),
SCRIPT_HASH("scripthash"),
@@ -43,6 +43,7 @@ public enum ScriptType {
NULL_DATA("nulldata"),
WITNESS_V0_KEYHASH("witness_v0_keyhash"),
WITNESS_V0_SCRIPTHASH("witness_v0_scripthash"),
+ WITNESS_UNKNOWN("witness_unknown"),
NONSTANDARD("nonstandard");
private final String name;
diff --git a/core/src/main/java/bisq/core/dao/state/blockchain/TempTx.java b/core/src/main/java/bisq/core/dao/state/blockchain/TempTx.java
index 73da786329..5d2622c998 100644
--- a/core/src/main/java/bisq/core/dao/state/blockchain/TempTx.java
+++ b/core/src/main/java/bisq/core/dao/state/blockchain/TempTx.java
@@ -43,7 +43,6 @@ public class TempTx extends BaseTx {
rawTx.getTxInputs(),
ImmutableList.copyOf(rawTx.getRawTxOutputs().stream().map(TempTxOutput::fromRawTxOutput).collect(Collectors.toList())),
null,
- 0,
0);
}
@@ -53,8 +52,6 @@ public class TempTx extends BaseTx {
@Nullable
private TxType txType;
private long burntFee;
- // If not set it is -1. LockTime of 0 is a valid value.
- private int unlockBlockHeight;
///////////////////////////////////////////////////////////////////////////////////////////
@@ -69,8 +66,7 @@ public class TempTx extends BaseTx {
ImmutableList txInputs,
ImmutableList tempTxOutputs,
@Nullable TxType txType,
- long burntFee,
- int unlockBlockHeight) {
+ long burntFee) {
super(txVersion,
id,
blockHeight,
@@ -80,17 +76,14 @@ public class TempTx extends BaseTx {
this.tempTxOutputs = tempTxOutputs;
this.txType = txType;
this.burntFee = burntFee;
- this.unlockBlockHeight = unlockBlockHeight;
}
-
@Override
public String toString() {
return "TempTx{" +
"\n txOutputs=" + tempTxOutputs +
",\n txType=" + txType +
",\n burntFee=" + burntFee +
- ",\n unlockBlockHeight=" + unlockBlockHeight +
"\n} " + super.toString();
}
}
diff --git a/core/src/main/java/bisq/core/dao/state/blockchain/TempTxOutput.java b/core/src/main/java/bisq/core/dao/state/blockchain/TempTxOutput.java
index b68c790540..2f8e8c7bc7 100644
--- a/core/src/main/java/bisq/core/dao/state/blockchain/TempTxOutput.java
+++ b/core/src/main/java/bisq/core/dao/state/blockchain/TempTxOutput.java
@@ -37,12 +37,18 @@ public class TempTxOutput extends BaseTxOutput {
txOutput.getAddress(),
txOutput.getOpReturnData(),
txOutput.getBlockHeight(),
- TxOutputType.UNDEFINED,
+ TxOutputType.UNDEFINED_OUTPUT,
+ -1,
0);
}
private TxOutputType txOutputType;
+
+ // The lockTime is stored in the first output of the LOCKUP tx.
+ // If not set it is -1, 0 is a valid value.
private int lockTime;
+ // The unlockBlockHeight is stored in the first output of the UNLOCK tx.
+ private int unlockBlockHeight;
private TempTxOutput(int index,
long value,
@@ -52,7 +58,8 @@ public class TempTxOutput extends BaseTxOutput {
@Nullable byte[] opReturnData,
int blockHeight,
TxOutputType txOutputType,
- int lockTime) {
+ int lockTime,
+ int unlockBlockHeight) {
super(index,
value,
txId,
@@ -63,6 +70,7 @@ public class TempTxOutput extends BaseTxOutput {
this.txOutputType = txOutputType;
this.lockTime = lockTime;
+ this.unlockBlockHeight = unlockBlockHeight;
}
@@ -71,6 +79,12 @@ public class TempTxOutput extends BaseTxOutput {
return "TempTxOutput{" +
"\n txOutputType=" + txOutputType +
"\n lockTime=" + lockTime +
+ "\n unlockBlockHeight=" + unlockBlockHeight +
"\n} " + super.toString();
}
+
+ public boolean isOpReturnOutput() {
+ // We do not check for pubKeyScript.scriptType.NULL_DATA because that is only set if dumpBlockchainData is true
+ return getOpReturnData() != null;
+ }
}
diff --git a/core/src/main/java/bisq/core/dao/state/blockchain/Tx.java b/core/src/main/java/bisq/core/dao/state/blockchain/Tx.java
index 75c369c573..d7917e0427 100644
--- a/core/src/main/java/bisq/core/dao/state/blockchain/Tx.java
+++ b/core/src/main/java/bisq/core/dao/state/blockchain/Tx.java
@@ -53,16 +53,13 @@ public final class Tx extends BaseTx implements PersistablePayload {
tempTx.getTxInputs(),
txOutputs,
tempTx.getTxType(),
- tempTx.getBurntFee(),
- tempTx.getUnlockBlockHeight());
+ tempTx.getBurntFee());
}
private final ImmutableList txOutputs;
@Nullable
private final TxType txType;
private final long burntFee;
- // If not set it is -1. LockTime of 0 is a valid value.
- private final int unlockBlockHeight;
///////////////////////////////////////////////////////////////////////////////////////////
@@ -77,8 +74,7 @@ public final class Tx extends BaseTx implements PersistablePayload {
ImmutableList txInputs,
ImmutableList txOutputs,
@Nullable TxType txType,
- long burntFee,
- int unlockBlockHeight) {
+ long burntFee) {
super(txVersion,
id,
blockHeight,
@@ -88,7 +84,7 @@ public final class Tx extends BaseTx implements PersistablePayload {
this.txOutputs = txOutputs;
this.txType = txType;
this.burntFee = burntFee;
- this.unlockBlockHeight = unlockBlockHeight;
+
}
@Override
@@ -97,8 +93,7 @@ public final class Tx extends BaseTx implements PersistablePayload {
.addAllTxOutputs(txOutputs.stream()
.map(TxOutput::toProtoMessage)
.collect(Collectors.toList()))
- .setBurntFee(burntFee)
- .setUnlockBlockHeight(unlockBlockHeight);
+ .setBurntFee(burntFee);
Optional.ofNullable(txType).ifPresent(txType -> builder.setTxType(txType.toProtoMessage()));
return getBaseTxBuilder().setTx(builder).build();
}
@@ -123,8 +118,7 @@ public final class Tx extends BaseTx implements PersistablePayload {
txInputs,
outputs,
TxType.fromProto(protoTx.getTxType()),
- protoTx.getBurntFee(),
- protoTx.getUnlockBlockHeight());
+ protoTx.getBurntFee());
}
@@ -137,15 +131,14 @@ public final class Tx extends BaseTx implements PersistablePayload {
}
- /**
- * OpReturn output might contain the lockTime in case of a LockTx. It has to be the last output.
- * We store technically the lockTime there as is is stored in the OpReturn data but conceptually we want to provide
- * it from the transaction.
- *
- * @return
- */
+ // The lockTime is stored in the first output of the LOCKUP tx.
public int getLockTime() {
- return getLastTxOutput().getLockTime();
+ return txOutputs.get(0).getLockTime();
+ }
+
+ // The unlockBlockHeight is stored in the first output of the UNLOCK tx.
+ public int getUnlockBlockHeight() {
+ return txOutputs.get(0).getUnlockBlockHeight();
}
@Override
@@ -154,7 +147,6 @@ public final class Tx extends BaseTx implements PersistablePayload {
"\n txOutputs=" + txOutputs +
",\n txType=" + txType +
",\n burntFee=" + burntFee +
- ",\n unlockBlockHeight=" + unlockBlockHeight +
"\n} " + super.toString();
}
diff --git a/core/src/main/java/bisq/core/dao/state/blockchain/TxOutput.java b/core/src/main/java/bisq/core/dao/state/blockchain/TxOutput.java
index e0ff51a618..a38b66cfe1 100644
--- a/core/src/main/java/bisq/core/dao/state/blockchain/TxOutput.java
+++ b/core/src/main/java/bisq/core/dao/state/blockchain/TxOutput.java
@@ -44,11 +44,17 @@ public class TxOutput extends BaseTxOutput implements PersistablePayload {
tempTxOutput.getOpReturnData(),
tempTxOutput.getBlockHeight(),
tempTxOutput.getTxOutputType(),
- tempTxOutput.getLockTime());
+ tempTxOutput.getLockTime(),
+ tempTxOutput.getUnlockBlockHeight());
}
private final TxOutputType txOutputType;
+
+ // The lockTime is stored in the first output of the LOCKUP tx.
+ // If not set it is -1, 0 is a valid value.
private final int lockTime;
+ // The unlockBlockHeight is stored in the first output of the UNLOCK tx.
+ private final int unlockBlockHeight;
public TxOutput(int index,
long value,
@@ -58,7 +64,8 @@ public class TxOutput extends BaseTxOutput implements PersistablePayload {
@Nullable byte[] opReturnData,
int blockHeight,
TxOutputType txOutputType,
- int lockTime) {
+ int lockTime,
+ int unlockBlockHeight) {
super(index,
value,
txId,
@@ -69,6 +76,7 @@ public class TxOutput extends BaseTxOutput implements PersistablePayload {
this.txOutputType = txOutputType;
this.lockTime = lockTime;
+ this.unlockBlockHeight = unlockBlockHeight;
}
@@ -80,11 +88,13 @@ public class TxOutput extends BaseTxOutput implements PersistablePayload {
public PB.BaseTxOutput toProtoMessage() {
PB.TxOutput.Builder builder = PB.TxOutput.newBuilder()
.setTxOutputType(txOutputType.toProtoMessage())
- .setLockTime(lockTime);
+ .setLockTime(lockTime)
+ .setUnlockBlockHeight(unlockBlockHeight);
return getRawTxOutputBuilder().setTxOutput(builder).build();
}
public static TxOutput fromProto(PB.BaseTxOutput proto) {
+ PB.TxOutput protoTxOutput = proto.getTxOutput();
return new TxOutput(proto.getIndex(),
proto.getValue(),
proto.getTxId(),
@@ -92,8 +102,9 @@ public class TxOutput extends BaseTxOutput implements PersistablePayload {
proto.getAddress().isEmpty() ? null : proto.getAddress(),
proto.getOpReturnData().isEmpty() ? null : proto.getOpReturnData().toByteArray(),
proto.getBlockHeight(),
- TxOutputType.fromProto(proto.getTxOutput().getTxOutputType()),
- proto.getTxOutput().getLockTime());
+ TxOutputType.fromProto(protoTxOutput.getTxOutputType()),
+ protoTxOutput.getLockTime(),
+ protoTxOutput.getUnlockBlockHeight());
}
@@ -102,6 +113,7 @@ public class TxOutput extends BaseTxOutput implements PersistablePayload {
return "TxOutput{" +
"\n txOutputType=" + txOutputType +
"\n lockTime=" + lockTime +
+ ",\n unlockBlockHeight=" + unlockBlockHeight +
"\n} " + super.toString();
}
}
diff --git a/core/src/main/java/bisq/core/dao/state/blockchain/TxOutputType.java b/core/src/main/java/bisq/core/dao/state/blockchain/TxOutputType.java
index 17b7c1ad82..a4ed27bbb2 100644
--- a/core/src/main/java/bisq/core/dao/state/blockchain/TxOutputType.java
+++ b/core/src/main/java/bisq/core/dao/state/blockchain/TxOutputType.java
@@ -22,7 +22,7 @@ import bisq.common.proto.ProtoUtil;
import io.bisq.generated.protobuffer.PB;
public enum TxOutputType {
- UNDEFINED,
+ UNDEFINED_OUTPUT,
GENESIS_OUTPUT,
BSQ_OUTPUT,
BTC_OUTPUT,
@@ -34,9 +34,9 @@ public enum TxOutputType {
BLIND_VOTE_OP_RETURN_OUTPUT,
VOTE_REVEAL_UNLOCK_STAKE_OUTPUT,
VOTE_REVEAL_OP_RETURN_OUTPUT,
- LOCKUP,
+ LOCKUP_OUTPUT,
LOCKUP_OP_RETURN_OUTPUT,
- UNLOCK,
+ UNLOCK_OUTPUT,
INVALID_OUTPUT;
diff --git a/core/src/main/java/bisq/core/dao/state/governance/Issuance.java b/core/src/main/java/bisq/core/dao/state/governance/Issuance.java
index 1334b1a976..daea557313 100644
--- a/core/src/main/java/bisq/core/dao/state/governance/Issuance.java
+++ b/core/src/main/java/bisq/core/dao/state/governance/Issuance.java
@@ -75,4 +75,14 @@ public class Issuance implements PersistablePayload, NetworkPayload {
proto.getAmount(),
proto.getPubKey().isEmpty() ? null : proto.getPubKey());
}
+
+ @Override
+ public String toString() {
+ return "Issuance{" +
+ "\n txId='" + txId + '\'' +
+ ",\n chainHeight=" + chainHeight +
+ ",\n amount=" + amount +
+ ",\n pubKey='" + pubKey + '\'' +
+ "\n}";
+ }
}
diff --git a/core/src/main/java/bisq/core/dao/state/governance/Param.java b/core/src/main/java/bisq/core/dao/state/governance/Param.java
index a65ffed2d7..10c1e31665 100644
--- a/core/src/main/java/bisq/core/dao/state/governance/Param.java
+++ b/core/src/main/java/bisq/core/dao/state/governance/Param.java
@@ -42,52 +42,79 @@ import static com.google.common.base.Preconditions.checkNotNull;
public enum Param {
UNDEFINED(0),
- // TODO trade fee is not implemented yet to be actually used.
- // FeeService is setting the fee atm....
- // 0.2% 100 = 1%, 1 is 0.01%
- BSQ_MAKER_FEE_IN_PERCENT(20),
- BSQ_TAKER_FEE_IN_PERCENT(20),
- BTC_MAKER_FEE_IN_PERCENT(20),
- BTC_TAKER_FEE_IN_PERCENT(20),
+ // Fee in BSQ satoshi for a 1 BTC trade. 200 Satoshi = 2 BSQ = 0.02%.
+ // About 2 USD if 1 BSQ = 1 USD for a 1 BTC trade which is about 10% of the BTC fee.,
+ // Might need adjustment if BSQ/BTC rate changes.
+ DEFAULT_MAKER_FEE_BSQ(200), // 0.02%
+ DEFAULT_TAKER_FEE_BSQ(200),
+ // 0.05 BSQ (5 satoshi) for a 1 BTC trade. 0.05 USD if 1 BSQ = 1 USD, 10 % of the BTC fee
+ MIN_MAKER_FEE_BSQ(5), // 0.0005%.
+ MIN_TAKER_FEE_BSQ(5),
+
+
+ // Fee in BTC satoshi for a 1 BTC trade. 200_000 Satoshi = 0.00200000 BTC = 0.2%.
+ // 20 USD at BTC price 10_000 USD for a 1 BTC trade;
+ DEFAULT_MAKER_FEE_BTC(200_000),
+ DEFAULT_TAKER_FEE_BTC(200_000), // 0.2%
+ MIN_MAKER_FEE_BTC(5_000), // 0.005%.
+ MIN_TAKER_FEE_BTC(5_000),
// Fees proposal/voting. Atm we don't use diff. fees for diff. proposal types
- PROPOSAL_FEE(100), // 5 BSQ TODO change low dev
- BLIND_VOTE_FEE(200), // 10 BSQ TODO change low dev
+ // See: https://github.com/bisq-network/proposals/issues/46
+ PROPOSAL_FEE(200), // 2 BSQ
+ BLIND_VOTE_FEE(200), // 2 BSQ
- // Quorum for voting in BSQ stake
- QUORUM_PROPOSAL(100), // 10 000 BSQ TODO change low dev value
- QUORUM_COMP_REQUEST(100), // 10 000 BSQ TODO change low dev value
- QUORUM_CHANGE_PARAM(300), // 100 000 BSQ TODO change low dev value
- QUORUM_REMOVE_ASSET(400), // 10 000 BSQ TODO change low dev value
- QUORUM_CONFISCATION(500), // 10 000 BSQ TODO change low dev value
+ // As BSQ based validation values can change over time if BSQ value rise we need to support that in the Params as well
+ COMPENSATION_REQUEST_MIN_AMOUNT(1_000), // 10 BSQ
+ COMPENSATION_REQUEST_MAX_AMOUNT(10_000_000), // 100 000 BSQ
+
+ // Quorum required for voting result to be valid.
+ // Quorum is the min. amount of total BSQ (earned+stake) which was used for voting on a request.
+ // E.g. If only 2000 BSQ was used on a vote but 10 000 is required the result is invalid even if the voters voted
+ // 100% for acceptance. This should prevent that changes can be done with low stakeholder participation.
+ QUORUM_COMP_REQUEST(2_000_000), // 20 000 BSQ
+ QUORUM_CHANGE_PARAM(10_000_000), // 100 000 BSQ
+ QUORUM_ROLE(5_000_000), // 50 000 BSQ
+ QUORUM_CONFISCATION(20_000_000), // 200 000 BSQ
+ QUORUM_GENERIC(500_000), // 5 000 BSQ
+ QUORUM_REMOVE_ASSET(1_000_000), // 10 000 BSQ
// Threshold for voting in % with precision of 2 (e.g. 5000 -> 50.00%)
- THRESHOLD_PROPOSAL(5_000), // 50%
+ // This is the required amount of weighted vote result needed for acceptance of the result.
+ // E.g. If the result ends up in 65% weighted vote for acceptance and threshold was 50% it is accepted.
+ // The result must be larger than the threshold. A 50% vote result for a threshold with 50% is not sufficient,
+ // it requires min. 50.01%.
THRESHOLD_COMP_REQUEST(5_000), // 50%
- THRESHOLD_CHANGE_PARAM(7_500), // 75% -> that might change the THRESHOLD_CHANGE_PARAM and QUORUM_CHANGE_PARAM!
+ THRESHOLD_CHANGE_PARAM(7_500), // 75% That might change the THRESHOLD_CHANGE_PARAM and QUORUM_CHANGE_PARAM as well. So we have to be careful here!
+ THRESHOLD_ROLE(5_000), // 50%
+ THRESHOLD_CONFISCATION(8_500), // 85% Confiscation is considered an exceptional case and need very high consensus among the stakeholders.
+ THRESHOLD_GENERIC(5_000), // 50%
THRESHOLD_REMOVE_ASSET(5_000), // 50%
- THRESHOLD_CONFISCATION(8_500), // 85%
- // Period phase (16 blocks atm)
+ //TODO add asset listing params (nr. of trades, volume, time, fee which defines listing state)
+
+ // TODO for dev testing we use short periods...
+ // Period phase (11 blocks atm)
PHASE_UNDEFINED(0),
- PHASE_PROPOSAL(2), // 24 days
- PHASE_BREAK1(1), // 10 blocks
- PHASE_BLIND_VOTE(2), // 4 days
- PHASE_BREAK2(1), // 10 blocks
- PHASE_VOTE_REVEAL(1), // 2 days
- PHASE_BREAK3(1), // 10 blocks
- PHASE_RESULT(1), // 1 block
- PHASE_BREAK4(1); // 10 blocks
+ PHASE_PROPOSAL(2),
+ PHASE_BREAK1(1),
+ PHASE_BLIND_VOTE(2),
+ PHASE_BREAK2(1),
+ PHASE_VOTE_REVEAL(2),
+ PHASE_BREAK3(1),
+ PHASE_RESULT(2);
- /*PHASE_UNDEFINED(0),
- PHASE_PROPOSAL(3456), // 24 days
- PHASE_BREAK1(10), // 10 blocks
- PHASE_BLIND_VOTE(576), // 4 days
+ // See: https://github.com/bisq-network/proposals/issues/46
+ /*
+ PHASE_UNDEFINED(0),
+ PHASE_PROPOSAL(3600), // 24 days
+ PHASE_BREAK1(150), // 1 day
+ PHASE_BLIND_VOTE(600), // 4 days
PHASE_BREAK2(10), // 10 blocks
- PHASE_VOTE_REVEAL(432), // 2 days
+ PHASE_VOTE_REVEAL(300), // 2 days
PHASE_BREAK3(10), // 10 blocks
- PHASE_RESULT(1), // 1 block
- PHASE_BREAK4(10); // 10 blocks*/
+ PHASE_RESULT(10); // 10 block
+ */
@Getter
private long defaultValue;
diff --git a/core/src/main/java/bisq/core/dao/state/period/CycleService.java b/core/src/main/java/bisq/core/dao/state/period/CycleService.java
index c08067b558..de1fe2e554 100644
--- a/core/src/main/java/bisq/core/dao/state/period/CycleService.java
+++ b/core/src/main/java/bisq/core/dao/state/period/CycleService.java
@@ -17,17 +17,15 @@
package bisq.core.dao.state.period;
-import bisq.core.dao.DaoOptionKeys;
import bisq.core.dao.DaoSetupService;
import bisq.core.dao.state.BsqStateListener;
import bisq.core.dao.state.BsqStateService;
+import bisq.core.dao.state.GenesisTxInfo;
import bisq.core.dao.state.blockchain.Block;
import bisq.core.dao.state.governance.Param;
import com.google.inject.Inject;
-import javax.inject.Named;
-
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
@@ -51,9 +49,9 @@ public class CycleService implements BsqStateListener, DaoSetupService {
@Inject
public CycleService(BsqStateService bsqStateService,
- @Named(DaoOptionKeys.GENESIS_BLOCK_HEIGHT) int genesisBlockHeight) {
+ GenesisTxInfo genesisTxInfo) {
this.bsqStateService = bsqStateService;
- this.genesisBlockHeight = genesisBlockHeight;
+ this.genesisBlockHeight = genesisTxInfo.getGenesisBlockHeight();
}
diff --git a/core/src/main/java/bisq/core/dao/state/period/PeriodService.java b/core/src/main/java/bisq/core/dao/state/period/PeriodService.java
index 780241993f..2ac09506e9 100644
--- a/core/src/main/java/bisq/core/dao/state/period/PeriodService.java
+++ b/core/src/main/java/bisq/core/dao/state/period/PeriodService.java
@@ -129,9 +129,9 @@ public final class PeriodService {
.orElse(0);
}
- public boolean isTxInPastCycle(String txId, int chainHeadHeight) {
+ public boolean isTxInPastCycle(String txId, int chainHeight) {
return getOptionalTx(txId)
- .filter(tx -> isTxInPastCycle(tx.getBlockHeight(), chainHeadHeight))
+ .filter(tx -> isTxInPastCycle(tx.getBlockHeight(), chainHeight))
.isPresent();
}
diff --git a/core/src/main/java/bisq/core/filter/FilterManager.java b/core/src/main/java/bisq/core/filter/FilterManager.java
index 4969615a1f..5aeaa55661 100644
--- a/core/src/main/java/bisq/core/filter/FilterManager.java
+++ b/core/src/main/java/bisq/core/filter/FilterManager.java
@@ -19,7 +19,7 @@ package bisq.core.filter;
import bisq.core.app.AppOptionKeys;
import bisq.core.app.BisqEnvironment;
-import bisq.core.btc.BitcoinNodes;
+import bisq.core.btc.nodes.BtcNodes;
import bisq.core.payment.payload.PaymentAccountPayload;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.provider.ProvidersRepository;
@@ -222,8 +222,8 @@ public class FilterManager {
listeners.forEach(e -> e.onFilterAdded(filter));
if (filter.isPreventPublicBtcNetwork() &&
- preferences.getBitcoinNodesOptionOrdinal() == BitcoinNodes.BitcoinNodesOption.PUBLIC.ordinal())
- preferences.setBitcoinNodesOptionOrdinal(BitcoinNodes.BitcoinNodesOption.PROVIDED.ordinal());
+ preferences.getBitcoinNodesOptionOrdinal() == BtcNodes.BitcoinNodesOption.PUBLIC.ordinal())
+ preferences.setBitcoinNodesOptionOrdinal(BtcNodes.BitcoinNodesOption.PROVIDED.ordinal());
}
}
diff --git a/core/src/main/java/bisq/core/locale/CurrencyUtil.java b/core/src/main/java/bisq/core/locale/CurrencyUtil.java
index 3cac7dcfb6..d9468d634f 100644
--- a/core/src/main/java/bisq/core/locale/CurrencyUtil.java
+++ b/core/src/main/java/bisq/core/locale/CurrencyUtil.java
@@ -18,12 +18,8 @@
package bisq.core.locale;
import bisq.core.app.BisqEnvironment;
-
-import bisq.asset.Asset;
-import bisq.asset.AssetRegistry;
-import bisq.asset.Coin;
-import bisq.asset.Token;
-import bisq.asset.coins.BSQ;
+import bisq.core.btc.BaseCurrencyNetwork;
+import bisq.core.dao.governance.asset.AssetService;
import bisq.common.app.DevEnv;
@@ -38,8 +34,19 @@ import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
+import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
+import static com.google.common.base.Preconditions.checkArgument;
+
+
+
+import bisq.asset.Asset;
+import bisq.asset.AssetRegistry;
+import bisq.asset.Coin;
+import bisq.asset.Token;
+import bisq.asset.coins.BSQ;
+
@Slf4j
public class CurrencyUtil {
@@ -47,6 +54,7 @@ public class CurrencyUtil {
setBaseCurrencyCode(BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode());
}
+ @Getter
private static final AssetRegistry assetRegistry = new AssetRegistry();
private static String baseCurrencyCode = "BTC";
@@ -106,14 +114,13 @@ public class CurrencyUtil {
private static List createAllSortedCryptoCurrenciesList() {
List result = assetRegistry.stream()
.filter(CurrencyUtil::assetIsNotBaseCurrency)
- .filter(CurrencyUtil::excludeBsqUnlessDaoTradingIsActive)
- .filter(CurrencyUtil::assetMatchesNetwork)
+ .filter(asset -> isNotBsqOrBsqTradingActivated(asset, BisqEnvironment.getBaseCurrencyNetwork(), DevEnv.isDaoTradingActivated()))
+ .filter(asset -> assetMatchesNetworkIfMainnet(asset, BisqEnvironment.getBaseCurrencyNetwork()))
.map(CurrencyUtil::assetToCryptoCurrency)
.sorted(TradeCurrency::compareTo)
.collect(Collectors.toList());
// Util for printing all altcoins for adding to FAQ page
-
/* StringBuilder sb = new StringBuilder();
result.stream().forEach(e -> sb.append("“")
.append(e.getCode())
@@ -344,7 +351,6 @@ public class CurrencyUtil {
return new FiatCurrency(currency.getCurrencyCode());
}
-
public static String getNameByCode(String currencyCode) {
if (isCryptoCurrency(currencyCode))
return getCryptoCurrency(currencyCode).get().getName();
@@ -357,6 +363,12 @@ public class CurrencyUtil {
}
}
+ public static Optional findCryptoCurrencyByName(String currencyName) {
+ return getAllSortedCryptoCurrencies().stream()
+ .filter(e -> e.getName().equals(currencyName))
+ .findAny();
+ }
+
public static String getNameAndCode(String currencyCode) {
return getNameByCode(currencyCode) + " (" + currencyCode + ")";
}
@@ -366,20 +378,84 @@ public class CurrencyUtil {
}
private static boolean assetIsNotBaseCurrency(Asset asset) {
- return !asset.getTickerSymbol().equals(baseCurrencyCode);
+ return !assetMatchesCurrencyCode(asset, baseCurrencyCode);
}
- private static boolean assetMatchesNetwork(Asset asset) {
+ // TODO We handle assets of other types (Token, ERC20) as matching the network which is not correct.
+ // We should add support for network property in those tokens as well.
+ public static boolean assetMatchesNetwork(Asset asset, BaseCurrencyNetwork baseCurrencyNetwork) {
return !(asset instanceof Coin) ||
- ((Coin) asset).getNetwork().name().equals(BisqEnvironment.getDefaultBaseCurrencyNetwork().getNetwork());
+ ((Coin) asset).getNetwork().name().equals(baseCurrencyNetwork.getNetwork());
+ }
+
+ // We only check for coins not other types of assets (TODO network check should be supported for all assets)
+ public static boolean assetMatchesNetworkIfMainnet(Asset asset, BaseCurrencyNetwork baseCurrencyNetwork) {
+ return !(asset instanceof Coin) ||
+ coinMatchesNetworkIfMainnet((Coin) asset, baseCurrencyNetwork);
+ }
+
+ // We want all coins available also in testnet or regtest for testing purpose
+ public static boolean coinMatchesNetworkIfMainnet(Coin coin, BaseCurrencyNetwork baseCurrencyNetwork) {
+ boolean matchesNetwork = assetMatchesNetwork(coin, baseCurrencyNetwork);
+ return !baseCurrencyNetwork.isMainnet() ||
+ matchesNetwork;
}
private static CryptoCurrency assetToCryptoCurrency(Asset asset) {
return new CryptoCurrency(asset.getTickerSymbol(), asset.getName(), asset instanceof Token);
}
- private static boolean excludeBsqUnlessDaoTradingIsActive(Asset asset) {
- return (!(asset instanceof BSQ) || (DevEnv.isDaoTradingActivated()
- && ((BSQ) asset).getNetwork().name().equals(BisqEnvironment.getBaseCurrencyNetwork().getNetwork())));
+ private static boolean isNotBsqOrBsqTradingActivated(Asset asset, BaseCurrencyNetwork baseCurrencyNetwork, boolean daoTradingActivated) {
+ return !(asset instanceof BSQ) ||
+ daoTradingActivated && assetMatchesNetwork(asset, baseCurrencyNetwork);
+ }
+
+ public static boolean assetMatchesCurrencyCode(Asset asset, String currencyCode) {
+ return currencyCode.equals(asset.getTickerSymbol());
+ }
+
+ public static Optional findAsset(AssetRegistry assetRegistry, String currencyCode,
+ BaseCurrencyNetwork baseCurrencyNetwork, boolean daoTradingActivated) {
+ List assets = assetRegistry.stream()
+ .filter(asset -> assetMatchesCurrencyCode(asset, currencyCode)).collect(Collectors.toList());
+
+ // If we don't have the ticker symbol we throw an exception
+ if (!assets.stream().findFirst().isPresent())
+ return Optional.empty();
+
+ if (currencyCode.equals("BSQ") && baseCurrencyNetwork.isMainnet() && !daoTradingActivated)
+ return Optional.empty();
+
+ // We check for exact match with network, e.g. BTC$TESTNET
+ Optional optionalAssetMatchesNetwork = assets.stream()
+ .filter(asset -> assetMatchesNetwork(asset, baseCurrencyNetwork))
+ .findFirst();
+ if (optionalAssetMatchesNetwork.isPresent())
+ return optionalAssetMatchesNetwork;
+
+ // In testnet or regtest we want to show all coins as well. Most coins have only Mainnet defined so we deliver
+ // that if no exact match was found in previous step
+ if (!baseCurrencyNetwork.isMainnet()) {
+ Optional optionalAsset = assets.stream().findFirst();
+ checkArgument(optionalAsset.isPresent(), "optionalAsset must be present as we checked for " +
+ "not matching ticker symbols already above");
+ return optionalAsset;
+ }
+
+ // If we are in mainnet we need have a mainet asset defined.
+ throw new IllegalArgumentException("We are on mainnet and we could not find an asset with network type mainnet");
+ }
+
+ public static Optional findAsset(String tickerSymbol, BaseCurrencyNetwork baseCurrencyNetwork) {
+ return assetRegistry.stream()
+ .filter(asset -> asset.getTickerSymbol().equals(tickerSymbol))
+ .filter(asset -> assetMatchesNetwork(asset, baseCurrencyNetwork))
+ .findAny();
+ }
+
+ public static List getWhiteListedSortedCryptoCurrencies(AssetService assetService) {
+ return getAllSortedCryptoCurrencies().stream()
+ .filter(e -> !assetService.isAssetRemoved(e.getCode()))
+ .collect(Collectors.toList());
}
}
diff --git a/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeAddresses.java b/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeAddresses.java
index 79c516fb15..34c6d6dd18 100644
--- a/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeAddresses.java
+++ b/core/src/main/java/bisq/core/network/p2p/seed/DefaultSeedNodeAddresses.java
@@ -79,8 +79,9 @@ class DefaultSeedNodeAddresses {
// new NodeAddress("uqxi3zrpobhtoes6.onion:8000"),
// BTC testnet
- new NodeAddress("nbphlanpgbei4okt.onion:8001"),
- // new NodeAddress("vjkh4ykq7x5skdlt.onion:8001"), // dev test
+ new NodeAddress("nbphlanpgbei4okt.onion:8001"), // 88.99.216.98
+ new NodeAddress("o5qw2hy6l7jsf654.onion:8001"), // explorer node
+ new NodeAddress("vjkh4ykq7x5skdlt.onion:8001"), // local dev test
// BTC regtest
// For development you need to change that to your local onion addresses
diff --git a/core/src/main/java/bisq/core/notifications/MobileModel.java b/core/src/main/java/bisq/core/notifications/MobileModel.java
index d4d50b4b57..ffd015afa0 100644
--- a/core/src/main/java/bisq/core/notifications/MobileModel.java
+++ b/core/src/main/java/bisq/core/notifications/MobileModel.java
@@ -68,7 +68,7 @@ public class MobileModel {
}
public void applyKeyAndToken(String keyAndToken) {
- log.info("phoneId={}", keyAndToken);
+ log.info("applyKeyAndToken: keyAndToken={}", keyAndToken.substring(0, 20) + "...(truncated in log for privacy reasons)");
String[] tokens = keyAndToken.split(PHONE_SEPARATOR_ESCAPED);
String magic = tokens[0];
descriptor = tokens[1];
diff --git a/core/src/main/java/bisq/core/notifications/MobileNotificationService.java b/core/src/main/java/bisq/core/notifications/MobileNotificationService.java
index e7481e7612..4148444333 100644
--- a/core/src/main/java/bisq/core/notifications/MobileNotificationService.java
+++ b/core/src/main/java/bisq/core/notifications/MobileNotificationService.java
@@ -130,8 +130,7 @@ public class MobileNotificationService {
}
public boolean sendMessage(MobileMessage message, boolean useSound) throws Exception {
- log.info("sendMessage\n" +
- "Title: " + message.getTitle() + "\nMessage: " + message.getMessage());
+ log.info("Send message: '{}'", message.getMessage());
if (mobileModel.getKey() == null)
return false;
diff --git a/core/src/main/java/bisq/core/offer/OfferUtil.java b/core/src/main/java/bisq/core/offer/OfferUtil.java
index 3a1dd3a040..bc87e11f4a 100644
--- a/core/src/main/java/bisq/core/offer/OfferUtil.java
+++ b/core/src/main/java/bisq/core/offer/OfferUtil.java
@@ -18,8 +18,8 @@
package bisq.core.offer;
import bisq.core.app.BisqEnvironment;
-import bisq.core.btc.Restrictions;
import bisq.core.btc.wallet.BsqWalletService;
+import bisq.core.btc.wallet.Restrictions;
import bisq.core.monetary.Price;
import bisq.core.monetary.Volume;
import bisq.core.provider.fee.FeeService;
@@ -89,13 +89,14 @@ public class OfferUtil {
@Nullable
public static Coin getMakerFee(boolean isCurrencyForMakerFeeBtc, @Nullable Coin amount, boolean marketPriceAvailable, double marketPriceMargin) {
if (amount != null) {
- final Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getMakerFeePerBtc(isCurrencyForMakerFeeBtc), amount);
+ Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getMakerFeePerBtc(isCurrencyForMakerFeeBtc), amount);
double makerFeeAsDouble = (double) feePerBtc.value;
if (marketPriceAvailable) {
if (marketPriceMargin > 0)
makerFeeAsDouble = makerFeeAsDouble * Math.sqrt(marketPriceMargin * 100);
else
makerFeeAsDouble = 0;
+
// For BTC we round so min value change is 100 satoshi
if (isCurrencyForMakerFeeBtc)
makerFeeAsDouble = MathUtils.roundDouble(makerFeeAsDouble / 100, 0) * 100;
diff --git a/core/src/main/java/bisq/core/offer/placeoffer/tasks/CreateMakerFeeTx.java b/core/src/main/java/bisq/core/offer/placeoffer/tasks/CreateMakerFeeTx.java
index 8f343505ff..80c9f8b3ba 100644
--- a/core/src/main/java/bisq/core/offer/placeoffer/tasks/CreateMakerFeeTx.java
+++ b/core/src/main/java/bisq/core/offer/placeoffer/tasks/CreateMakerFeeTx.java
@@ -18,11 +18,11 @@
package bisq.core.offer.placeoffer.tasks;
import bisq.core.arbitration.Arbitrator;
-import bisq.core.btc.AddressEntry;
+import bisq.core.btc.exceptions.TxBroadcastException;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.btc.wallet.TradeWalletService;
-import bisq.core.btc.wallet.TxBroadcastException;
import bisq.core.btc.wallet.TxBroadcaster;
import bisq.core.btc.wallet.WalletService;
import bisq.core.offer.Offer;
diff --git a/core/src/main/java/bisq/core/payment/OKPayAccount.java b/core/src/main/java/bisq/core/payment/OKPayAccount.java
index 4009ee2c56..42282e93e7 100644
--- a/core/src/main/java/bisq/core/payment/OKPayAccount.java
+++ b/core/src/main/java/bisq/core/payment/OKPayAccount.java
@@ -24,7 +24,7 @@ import bisq.core.payment.payload.PaymentMethod;
import lombok.EqualsAndHashCode;
-//TODO missing support for selected trade currency
+@Deprecated
@EqualsAndHashCode(callSuper = true)
public final class OKPayAccount extends PaymentAccount {
public OKPayAccount() {
diff --git a/core/src/main/java/bisq/core/payment/payload/OKPayAccountPayload.java b/core/src/main/java/bisq/core/payment/payload/OKPayAccountPayload.java
index 111f841ae7..817f7f0cac 100644
--- a/core/src/main/java/bisq/core/payment/payload/OKPayAccountPayload.java
+++ b/core/src/main/java/bisq/core/payment/payload/OKPayAccountPayload.java
@@ -34,6 +34,7 @@ import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
+@Deprecated
@EqualsAndHashCode(callSuper = true)
@ToString
@Setter
diff --git a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java
index 209b512df1..e42ddbd8d4 100644
--- a/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java
+++ b/core/src/main/java/bisq/core/payment/payload/PaymentMethod.java
@@ -51,6 +51,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable {
// time in blocks (average 10 min for one block confirmation
private static final long DAY = TimeUnit.HOURS.toMillis(24);
+ @Deprecated
public static final String OK_PAY_ID = "OK_PAY";
public static final String UPHOLD_ID = "UPHOLD";
@Deprecated
@@ -81,6 +82,7 @@ public final class PaymentMethod implements PersistablePayload, Comparable {
public static final String F2F_ID = "F2F";
public static final String BLOCK_CHAINS_ID = "BLOCK_CHAINS";
+ @Deprecated
public static PaymentMethod OK_PAY;
public static PaymentMethod UPHOLD;
@Deprecated
diff --git a/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java b/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java
index 5a8eb4432b..c6028c52e6 100644
--- a/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java
+++ b/core/src/main/java/bisq/core/payment/validation/AltCoinAddressValidator.java
@@ -18,20 +18,23 @@
package bisq.core.payment.validation;
import bisq.core.app.BisqEnvironment;
-import bisq.core.btc.BaseCurrencyNetwork;
+import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.util.validation.InputValidator;
+import bisq.common.app.DevEnv;
+
+import com.google.inject.Inject;
+
+import java.util.Optional;
+
+import lombok.extern.slf4j.Slf4j;
+
+
+
import bisq.asset.AddressValidationResult;
import bisq.asset.Asset;
import bisq.asset.AssetRegistry;
-import bisq.asset.Coin;
-
-import com.google.inject.Inject;
-
-import lombok.extern.slf4j.Slf4j;
-
-import static java.lang.String.format;
@Slf4j
public final class AltCoinAddressValidator extends InputValidator {
@@ -54,30 +57,19 @@ public final class AltCoinAddressValidator extends InputValidator {
if (!validationResult.isValid || currencyCode == null)
return validationResult;
- Asset asset = assetRegistry.stream()
- .filter(this::assetMatchesSelectedCurrencyCode)
- .filter(this::assetIsNotBaseCurrencyForDifferentNetwork)
- .findFirst()
- .orElseThrow(() ->
- new IllegalArgumentException(format("'%s' is not a registered asset", currencyCode)));
+ Optional optionalAsset = CurrencyUtil.findAsset(assetRegistry, currencyCode,
+ BisqEnvironment.getBaseCurrencyNetwork(), DevEnv.isDaoTradingActivated());
+ if (optionalAsset.isPresent()) {
+ Asset asset = optionalAsset.get();
+ AddressValidationResult result = asset.validateAddress(input);
+ if (!result.isValid()) {
+ return new ValidationResult(false, Res.get(result.getI18nKey(), asset.getTickerSymbol(),
+ result.getMessage()));
+ }
- AddressValidationResult result = asset.validateAddress(input);
- if (!result.isValid())
- return new ValidationResult(false,
- Res.get(result.getI18nKey(), asset.getTickerSymbol(), result.getMessage()));
-
- return new ValidationResult(true);
- }
-
- private boolean assetMatchesSelectedCurrencyCode(Asset a) {
- return currencyCode.equals(a.getTickerSymbol());
- }
-
- private boolean assetIsNotBaseCurrencyForDifferentNetwork(Asset asset) {
- BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork();
-
- return !(asset instanceof Coin)
- || !asset.getTickerSymbol().equals(baseCurrencyNetwork.getCurrencyCode())
- || (((Coin) asset).getNetwork().name().equals(baseCurrencyNetwork.getNetwork()));
+ return new ValidationResult(true);
+ } else {
+ return new ValidationResult(false);
+ }
}
}
diff --git a/core/src/main/java/bisq/core/presentation/BalancePresentation.java b/core/src/main/java/bisq/core/presentation/BalancePresentation.java
index 3ddd94cb16..311a8570ea 100644
--- a/core/src/main/java/bisq/core/presentation/BalancePresentation.java
+++ b/core/src/main/java/bisq/core/presentation/BalancePresentation.java
@@ -17,7 +17,7 @@
package bisq.core.presentation;
-import bisq.core.btc.BalanceModel;
+import bisq.core.btc.model.BalanceModel;
import bisq.core.util.BSFormatter;
import javax.inject.Inject;
diff --git a/core/src/main/java/bisq/core/proto/ProtoDevUtil.java b/core/src/main/java/bisq/core/proto/ProtoDevUtil.java
index 84ce44b125..c62b65e0ea 100644
--- a/core/src/main/java/bisq/core/proto/ProtoDevUtil.java
+++ b/core/src/main/java/bisq/core/proto/ProtoDevUtil.java
@@ -18,7 +18,7 @@
package bisq.core.proto;
import bisq.core.arbitration.DisputeResult;
-import bisq.core.btc.AddressEntry;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.offer.AvailabilityResult;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferPayload;
diff --git a/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java b/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java
index dc56715e2b..9a119c57c1 100644
--- a/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java
+++ b/core/src/main/java/bisq/core/proto/network/CoreNetworkProtoResolver.java
@@ -26,6 +26,7 @@ import bisq.core.arbitration.messages.DisputeResultMessage;
import bisq.core.arbitration.messages.OpenNewDisputeMessage;
import bisq.core.arbitration.messages.PeerOpenedDisputeMessage;
import bisq.core.arbitration.messages.PeerPublishedDisputePayoutTxMessage;
+import bisq.core.dao.governance.blindvote.network.messages.RepublishGovernanceDataRequest;
import bisq.core.dao.governance.proposal.storage.temp.TempProposalPayload;
import bisq.core.dao.node.messages.GetBlocksRequest;
import bisq.core.dao.node.messages.GetBlocksResponse;
@@ -157,6 +158,9 @@ public class CoreNetworkProtoResolver extends CoreProtoResolver implements Netwo
return AddPersistableNetworkPayloadMessage.fromProto(proto.getAddPersistableNetworkPayloadMessage(), this, messageVersion);
case ACK_MESSAGE:
return AckMessage.fromProto(proto.getAckMessage(), messageVersion);
+ case REPUBLISH_GOVERNANCE_DATA_REQUEST:
+ return RepublishGovernanceDataRequest.fromProto(proto.getRepublishGovernanceDataRequest(), messageVersion);
+
default:
throw new ProtobufferException("Unknown proto message case (PB.NetworkEnvelope). messageCase=" +
proto.getMessageCase() + "; proto raw data=" + proto.toString());
diff --git a/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java b/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java
index ef78c0caee..ddecb704b4 100644
--- a/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java
+++ b/core/src/main/java/bisq/core/proto/persistable/CorePersistenceProtoResolver.java
@@ -18,8 +18,9 @@
package bisq.core.proto.persistable;
import bisq.core.arbitration.DisputeList;
-import bisq.core.btc.AddressEntryList;
+import bisq.core.btc.model.AddressEntryList;
import bisq.core.btc.wallet.BtcWalletService;
+import bisq.core.dao.governance.asset.RemovedAssetsList;
import bisq.core.dao.governance.ballot.BallotList;
import bisq.core.dao.governance.blindvote.MyBlindVoteList;
import bisq.core.dao.governance.blindvote.storage.BlindVoteStore;
@@ -29,6 +30,8 @@ import bisq.core.dao.governance.proposal.MyProposalList;
import bisq.core.dao.governance.proposal.storage.appendonly.ProposalStore;
import bisq.core.dao.governance.proposal.storage.temp.TempProposalStore;
import bisq.core.dao.governance.role.BondedRoleList;
+import bisq.core.dao.governance.voteresult.DecryptedBallotsWithMeritsList;
+import bisq.core.dao.governance.voteresult.EvaluatedProposalList;
import bisq.core.dao.state.BsqState;
import bisq.core.payment.AccountAgeWitnessStore;
import bisq.core.payment.PaymentAccountList;
@@ -132,6 +135,12 @@ public class CorePersistenceProtoResolver extends CoreProtoResolver implements P
return MeritList.fromProto(proto.getMeritList());
case BONDED_ROLE_LIST:
return BondedRoleList.fromProto(proto.getBondedRoleList());
+ case REMOVED_ASSET_LIST:
+ return RemovedAssetsList.fromProto(proto.getRemovedAssetList());
+ case EVALUATED_PROPOSAL_LIST:
+ return EvaluatedProposalList.fromProto(proto.getEvaluatedProposalList());
+ case DECRYPTED_BALLOTS_WITH_MERITS_LIST:
+ return DecryptedBallotsWithMeritsList.fromProto(proto.getDecryptedBallotsWithMeritsList());
default:
throw new ProtobufferRuntimeException("Unknown proto message case(PB.PersistableEnvelope). " +
"messageCase=" + proto.getMessageCase() + "; proto raw data=" + proto.toString());
diff --git a/core/src/main/java/bisq/core/provider/fee/FeeService.java b/core/src/main/java/bisq/core/provider/fee/FeeService.java
index 9ffc31fdac..f6a878541a 100644
--- a/core/src/main/java/bisq/core/provider/fee/FeeService.java
+++ b/core/src/main/java/bisq/core/provider/fee/FeeService.java
@@ -18,13 +18,15 @@
package bisq.core.provider.fee;
import bisq.core.app.BisqEnvironment;
+import bisq.core.dao.state.BsqStateService;
+import bisq.core.dao.state.governance.Param;
+import bisq.core.dao.state.period.PeriodService;
import bisq.common.UserThread;
import bisq.common.handlers.FaultHandler;
import bisq.common.util.Tuple2;
import org.bitcoinj.core.Coin;
-import org.bitcoinj.core.Transaction;
import com.google.inject.Inject;
@@ -53,40 +55,53 @@ import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j
public class FeeService {
- // fixed min fee
- public static final Coin BTC_REFERENCE_DEFAULT_MIN_TX_FEE_PER_KB = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; // 5000
- // https://litecoin.info/Transaction_fees min fee is 100_000
- public static final Coin LTC_REFERENCE_DEFAULT_MIN_TX_FEE = Coin.valueOf(100_000);
- //TODO check
- // min tx fee per tx is 10000 now, 1000 in sept 2017
- public static final Coin DASH_REFERENCE_DEFAULT_MIN_TX_FEE = Coin.valueOf(10_000);
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Static
+ ///////////////////////////////////////////////////////////////////////////////////////////
- // DEFAULT_TX_FEE used in FeeRequestService for non-BTC currencies and for BTC only if we cannot access fee service
- // fees are per byte
- public static final long BTC_DEFAULT_TX_FEE = 200; // fees are between 20-600 sat/byte. We try to stay on the safe side.
- public static final long LTC_DEFAULT_TX_FEE = LTC_REFERENCE_DEFAULT_MIN_TX_FEE.value / 200;
- public static final long DASH_DEFAULT_TX_FEE = DASH_REFERENCE_DEFAULT_MIN_TX_FEE.value / 200; // 200 bytes tx -> 200*50=10000
+ // Miner fees are between 1-600 sat/byte. We try to stay on the safe side. BTC_DEFAULT_TX_FEE is only used if our
+ // fee service would not deliver data.
+ private static final long BTC_DEFAULT_TX_FEE = 50;
+ private static final long MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN = 2;
+ private static BsqStateService bsqStateService;
+ private static PeriodService periodService;
- private static long MIN_MAKER_FEE_IN_BASE_CUR;
- private static long MIN_TAKER_FEE_IN_BASE_CUR;
- private static long DEFAULT_MAKER_FEE_IN_BASE_CUR;
- private static long DEFAULT_TAKER_FEE_IN_BASE_CUR;
+ private static long getFeeFromParam(Param parm) {
+ return bsqStateService != null && periodService != null ? bsqStateService.getParamValue(parm, periodService.getChainHeight()) : 0;
+ }
- private static final long MIN_MAKER_FEE_IN_BSQ = 5;
- private static final long MIN_TAKER_FEE_IN_BSQ = 5;
- private static final long DEFAULT_MAKER_FEE_IN_BSQ = 200; // about 2 USD at 1 BSQ = 1 USD for a 1 BTC trade
- private static final long DEFAULT_TAKER_FEE_IN_BSQ = 200;
+ public static Coin getMakerFeePerBtc(boolean currencyForFeeIsBtc) {
+ long fee = currencyForFeeIsBtc ? getFeeFromParam(Param.DEFAULT_MAKER_FEE_BTC) : getFeeFromParam(Param.DEFAULT_MAKER_FEE_BSQ);
+ return Coin.valueOf(fee);
+ }
- public static final long MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN = 2;
+ public static Coin getMinMakerFee(boolean currencyForFeeIsBtc) {
+ long fee = currencyForFeeIsBtc ? getFeeFromParam(Param.MIN_MAKER_FEE_BTC) : getFeeFromParam(Param.MIN_MAKER_FEE_BSQ);
+ return Coin.valueOf(fee);
+ }
+
+ public static Coin getTakerFeePerBtc(boolean currencyForFeeIsBtc) {
+ long fee = currencyForFeeIsBtc ? getFeeFromParam(Param.DEFAULT_TAKER_FEE_BTC) : getFeeFromParam(Param.DEFAULT_TAKER_FEE_BSQ);
+ return Coin.valueOf(fee);
+ }
+
+ public static Coin getMinTakerFee(boolean currencyForFeeIsBtc) {
+ long fee = currencyForFeeIsBtc ? getFeeFromParam(Param.MIN_TAKER_FEE_BTC) : getFeeFromParam(Param.MIN_TAKER_FEE_BSQ);
+ return Coin.valueOf(fee);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Class fields
+ ///////////////////////////////////////////////////////////////////////////////////////////
private final FeeProvider feeProvider;
- private final String baseCurrencyCode;
- private long txFeePerByte;
+ private final IntegerProperty feeUpdateCounter = new SimpleIntegerProperty(0);
+ private long txFeePerByte = BTC_DEFAULT_TX_FEE;
private Map timeStampMap;
- private long epochInSecondAtLastRequest;
private long lastRequest;
- private IntegerProperty feeUpdateCounter = new SimpleIntegerProperty(0);
private long minFeePerByte;
+ private long epochInSecondAtLastRequest;
///////////////////////////////////////////////////////////////////////////////////////////
@@ -94,50 +109,33 @@ public class FeeService {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
- public FeeService(FeeProvider feeProvider) {
+ public FeeService(FeeProvider feeProvider, BsqStateService bsqStateService, PeriodService periodService) {
this.feeProvider = feeProvider;
- baseCurrencyCode = BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode();
-
- /* How to calculate:
- MIN_MAKER_FEE_IN_BASE_CUR = target fiat price * 100000000 / price (in btc: 0.5*100000000/2500)
- DEFAULT_MAKER_FEE_IN_BASE_CUR = target fiat price * (100000000 / price) / maxTradeAmount
- (in btc: 5*100000000/2500 / 1)
- (in ltc: 5*100000000/40 / 50)
- */
- switch (baseCurrencyCode) {
- case "BTC":
- MIN_MAKER_FEE_IN_BASE_CUR = 5_000; // 0.5 USD at BTC price 10000 USD
- MIN_TAKER_FEE_IN_BASE_CUR = 5_000;
- DEFAULT_MAKER_FEE_IN_BASE_CUR = 200_000; // 20 USD at BTC price 10000 USD for a 1 BTC trade
- DEFAULT_TAKER_FEE_IN_BASE_CUR = 200_000;
- txFeePerByte = BTC_DEFAULT_TX_FEE;
- break;
- case "LTC":
- MIN_MAKER_FEE_IN_BASE_CUR = 1_200_000; // 0.5 USD at LTC price 40 USD
- MIN_TAKER_FEE_IN_BASE_CUR = 1_200_000;
- DEFAULT_MAKER_FEE_IN_BASE_CUR = 240_000; // 5 USD at LTC price 40 USD for 50 LTC (maxTradeAmount)
- DEFAULT_TAKER_FEE_IN_BASE_CUR = 360_000; // 7.5 USD at LTC price 40 USD
- txFeePerByte = LTC_DEFAULT_TX_FEE;
- break;
- case "DASH":
- MIN_MAKER_FEE_IN_BASE_CUR = 300_000; // 0.5 USD at DASH price 150 USD
- MIN_TAKER_FEE_IN_BASE_CUR = 300_000;
- DEFAULT_MAKER_FEE_IN_BASE_CUR = 160_000; // 5 USD at DASH price 150 USD
- DEFAULT_TAKER_FEE_IN_BASE_CUR = 240_000; // 7.5 USD at DASH price 150 USD for 20 DASH (maxTradeAmount)
- txFeePerByte = DASH_DEFAULT_TX_FEE;
- break;
- default:
- throw new RuntimeException("baseCurrencyCode not defined. baseCurrencyCode=" + baseCurrencyCode);
- }
+ FeeService.bsqStateService = bsqStateService;
+ FeeService.periodService = periodService;
}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // API
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
public void onAllServicesInitialized() {
minFeePerByte = BisqEnvironment.getBaseCurrencyNetwork().getDefaultMinFeePerByte();
- requestFees(null, null);
+ requestFees();
// We update all 5 min.
- UserThread.runPeriodically(() -> requestFees(null, null), 5, TimeUnit.MINUTES);
+ UserThread.runPeriodically(this::requestFees, 5, TimeUnit.MINUTES);
+ }
+
+
+ public void requestFees() {
+ requestFees(null, null);
+ }
+
+ public void requestFees(Runnable resultHandler) {
+ requestFees(resultHandler, null);
}
public void requestFees(@Nullable Runnable resultHandler, @Nullable FaultHandler faultHandler) {
@@ -155,7 +153,7 @@ public class FeeService {
timeStampMap = result.first;
epochInSecondAtLastRequest = timeStampMap.get("bitcoinFeesTs");
final Map map = result.second;
- txFeePerByte = map.get(baseCurrencyCode);
+ txFeePerByte = map.get("BTC");
if (txFeePerByte < minFeePerByte) {
log.warn("The delivered fee per byte is smaller than the min. default fee of 5 sat/byte");
@@ -163,7 +161,7 @@ public class FeeService {
}
feeUpdateCounter.set(feeUpdateCounter.get() + 1);
- log.info("{} tx fee: txFeePerByte={}", baseCurrencyCode, txFeePerByte);
+ log.info("BTC tx fee: txFeePerByte={}", txFeePerByte);
if (resultHandler != null)
resultHandler.run();
});
@@ -193,22 +191,6 @@ public class FeeService {
return Coin.valueOf(txFeePerByte);
}
- public static Coin getMakerFeePerBtc(boolean currencyForMakerFeeBtc) {
- return currencyForMakerFeeBtc ? Coin.valueOf(DEFAULT_MAKER_FEE_IN_BASE_CUR) : Coin.valueOf(DEFAULT_MAKER_FEE_IN_BSQ);
- }
-
- public static Coin getMinMakerFee(boolean currencyForMakerFeeBtc) {
- return currencyForMakerFeeBtc ? Coin.valueOf(MIN_MAKER_FEE_IN_BASE_CUR) : Coin.valueOf(MIN_MAKER_FEE_IN_BSQ);
- }
-
- public static Coin getTakerFeePerBtc(boolean currencyForTakerFeeBtc) {
- return currencyForTakerFeeBtc ? Coin.valueOf(DEFAULT_TAKER_FEE_IN_BASE_CUR) : Coin.valueOf(DEFAULT_TAKER_FEE_IN_BSQ);
- }
-
- public static Coin getMinTakerFee(boolean currencyForTakerFeeBtc) {
- return currencyForTakerFeeBtc ? Coin.valueOf(MIN_TAKER_FEE_IN_BASE_CUR) : Coin.valueOf(MIN_TAKER_FEE_IN_BSQ);
- }
-
public ReadOnlyIntegerProperty feeUpdateCounterProperty() {
return feeUpdateCounter;
}
diff --git a/core/src/main/java/bisq/core/provider/price/PriceFeedService.java b/core/src/main/java/bisq/core/provider/price/PriceFeedService.java
index c3b0f9d7fc..76e646792f 100644
--- a/core/src/main/java/bisq/core/provider/price/PriceFeedService.java
+++ b/core/src/main/java/bisq/core/provider/price/PriceFeedService.java
@@ -280,13 +280,13 @@ public class PriceFeedService {
}
public Date getLastRequestTimeStampBtcAverage() {
- return new Date(epochInSecondAtLastRequest * 1000);
+ return new Date(epochInSecondAtLastRequest);
}
public Date getLastRequestTimeStampPoloniex() {
Long ts = timeStampMap.get("btcAverageTs");
if (ts != null) {
- return new Date(ts * 1000);
+ return new Date(ts);
} else
return new Date();
}
@@ -294,7 +294,7 @@ public class PriceFeedService {
public Date getLastRequestTimeStampCoinmarketcap() {
Long ts = timeStampMap.get("coinmarketcapTs");
if (ts != null) {
- return new Date(ts * 1000);
+ return new Date(ts);
} else
return new Date();
}
diff --git a/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java b/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java
index 46816773ce..2b396f6760 100644
--- a/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java
+++ b/core/src/main/java/bisq/core/setup/CorePersistedDataHost.java
@@ -18,13 +18,15 @@
package bisq.core.setup;
import bisq.core.arbitration.DisputeManager;
-import bisq.core.btc.AddressEntryList;
+import bisq.core.btc.model.AddressEntryList;
import bisq.core.dao.DaoOptionKeys;
+import bisq.core.dao.governance.asset.AssetService;
import bisq.core.dao.governance.ballot.BallotListService;
import bisq.core.dao.governance.blindvote.MyBlindVoteListService;
import bisq.core.dao.governance.myvote.MyVoteListService;
import bisq.core.dao.governance.proposal.MyProposalListService;
import bisq.core.dao.governance.role.BondedRolesService;
+import bisq.core.dao.governance.voteresult.VoteResultService;
import bisq.core.offer.OpenOfferManager;
import bisq.core.trade.TradeManager;
import bisq.core.trade.closed.ClosedTradableManager;
@@ -67,6 +69,8 @@ public class CorePersistedDataHost {
persistedDataHosts.add(injector.getInstance(MyVoteListService.class));
persistedDataHosts.add(injector.getInstance(MyProposalListService.class));
persistedDataHosts.add(injector.getInstance(BondedRolesService.class));
+ persistedDataHosts.add(injector.getInstance(AssetService.class));
+ persistedDataHosts.add(injector.getInstance(VoteResultService.class));
}
return persistedDataHosts;
}
diff --git a/core/src/main/java/bisq/core/trade/TradeManager.java b/core/src/main/java/bisq/core/trade/TradeManager.java
index 14c8627b07..436cd72d9f 100644
--- a/core/src/main/java/bisq/core/trade/TradeManager.java
+++ b/core/src/main/java/bisq/core/trade/TradeManager.java
@@ -17,8 +17,8 @@
package bisq.core.trade;
-import bisq.core.btc.AddressEntry;
-import bisq.core.btc.AddressEntryException;
+import bisq.core.btc.exceptions.AddressEntryException;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.btc.wallet.TradeWalletService;
diff --git a/core/src/main/java/bisq/core/trade/messages/PayDepositRequest.java b/core/src/main/java/bisq/core/trade/messages/PayDepositRequest.java
index 56fe5c0b17..6f4b0e15c9 100644
--- a/core/src/main/java/bisq/core/trade/messages/PayDepositRequest.java
+++ b/core/src/main/java/bisq/core/trade/messages/PayDepositRequest.java
@@ -17,7 +17,7 @@
package bisq.core.trade.messages;
-import bisq.core.btc.data.RawTransactionInput;
+import bisq.core.btc.model.RawTransactionInput;
import bisq.core.payment.payload.PaymentAccountPayload;
import bisq.core.proto.CoreProtoResolver;
diff --git a/core/src/main/java/bisq/core/trade/messages/PublishDepositTxRequest.java b/core/src/main/java/bisq/core/trade/messages/PublishDepositTxRequest.java
index 6a8e3e218f..c493621281 100644
--- a/core/src/main/java/bisq/core/trade/messages/PublishDepositTxRequest.java
+++ b/core/src/main/java/bisq/core/trade/messages/PublishDepositTxRequest.java
@@ -17,7 +17,7 @@
package bisq.core.trade.messages;
-import bisq.core.btc.data.RawTransactionInput;
+import bisq.core.btc.model.RawTransactionInput;
import bisq.core.payment.payload.PaymentAccountPayload;
import bisq.core.proto.CoreProtoResolver;
diff --git a/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java b/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java
index fdc56fd3cf..78b798086d 100644
--- a/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java
+++ b/core/src/main/java/bisq/core/trade/protocol/ProcessModel.java
@@ -18,9 +18,9 @@
package bisq.core.trade.protocol;
import bisq.core.app.BisqEnvironment;
-import bisq.core.btc.data.RawTransactionInput;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
+import bisq.core.btc.model.RawTransactionInput;
import bisq.core.btc.wallet.TradeWalletService;
import bisq.core.filter.FilterManager;
import bisq.core.network.MessageState;
diff --git a/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java b/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java
index ed148c7b7c..9b04dc222f 100644
--- a/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java
+++ b/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java
@@ -17,7 +17,7 @@
package bisq.core.trade.protocol;
-import bisq.core.btc.data.RawTransactionInput;
+import bisq.core.btc.model.RawTransactionInput;
import bisq.core.payment.payload.PaymentAccountPayload;
import bisq.core.proto.CoreProtoResolver;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerProcessPayoutTxPublishedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerProcessPayoutTxPublishedMessage.java
index ae3cdc1d2a..3543fbddd1 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerProcessPayoutTxPublishedMessage.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerProcessPayoutTxPublishedMessage.java
@@ -17,7 +17,7 @@
package bisq.core.trade.protocol.tasks.buyer;
-import bisq.core.btc.AddressEntry;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.trade.Trade;
import bisq.core.trade.messages.PayoutTxPublishedMessage;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSendCounterCurrencyTransferStartedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSendCounterCurrencyTransferStartedMessage.java
index 00d13310bf..f905981553 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSendCounterCurrencyTransferStartedMessage.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSendCounterCurrencyTransferStartedMessage.java
@@ -17,7 +17,7 @@
package bisq.core.trade.protocol.tasks.buyer;
-import bisq.core.btc.AddressEntry;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.trade.Trade;
import bisq.core.trade.messages.CounterCurrencyTransferStartedMessage;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSetupPayoutTxListener.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSetupPayoutTxListener.java
index 20fe4cef1d..178a08d786 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSetupPayoutTxListener.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSetupPayoutTxListener.java
@@ -17,8 +17,8 @@
package bisq.core.trade.protocol.tasks.buyer;
-import bisq.core.btc.AddressEntry;
import bisq.core.btc.listeners.AddressConfidenceListener;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.trade.Trade;
import bisq.core.trade.protocol.tasks.TradeTask;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerCreatesAndSignsDepositTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerCreatesAndSignsDepositTx.java
index c5ae6bc416..4edd9bdb66 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerCreatesAndSignsDepositTx.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerCreatesAndSignsDepositTx.java
@@ -17,10 +17,10 @@
package bisq.core.trade.protocol.tasks.buyer_as_maker;
-import bisq.core.btc.AddressEntry;
-import bisq.core.btc.data.PreparedDepositTxAndMakerInputs;
-import bisq.core.btc.data.RawTransactionInput;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
+import bisq.core.btc.model.PreparedDepositTxAndMakerInputs;
+import bisq.core.btc.model.RawTransactionInput;
import bisq.core.offer.Offer;
import bisq.core.trade.Trade;
import bisq.core.trade.protocol.TradingPeer;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerSignPayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerSignPayoutTx.java
index 1d0733514b..b1f48b471e 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerSignPayoutTx.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_maker/BuyerAsMakerSignPayoutTx.java
@@ -17,7 +17,7 @@
package bisq.core.trade.protocol.tasks.buyer_as_maker;
-import bisq.core.btc.AddressEntry;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.trade.Trade;
import bisq.core.trade.protocol.tasks.TradeTask;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerCreatesDepositTxInputs.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerCreatesDepositTxInputs.java
index b8eacae4f9..653525fda5 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerCreatesDepositTxInputs.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerCreatesDepositTxInputs.java
@@ -17,9 +17,9 @@
package bisq.core.trade.protocol.tasks.buyer_as_taker;
-import bisq.core.btc.AddressEntry;
-import bisq.core.btc.data.InputsAndChangeOutput;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
+import bisq.core.btc.model.InputsAndChangeOutput;
import bisq.core.trade.Trade;
import bisq.core.trade.protocol.tasks.TradeTask;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerSignAndPublishDepositTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerSignAndPublishDepositTx.java
index 6a93d8fdec..0e4d4edcc1 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerSignAndPublishDepositTx.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer_as_taker/BuyerAsTakerSignAndPublishDepositTx.java
@@ -17,10 +17,10 @@
package bisq.core.trade.protocol.tasks.buyer_as_taker;
-import bisq.core.btc.AddressEntry;
-import bisq.core.btc.data.RawTransactionInput;
+import bisq.core.btc.exceptions.TxBroadcastException;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
-import bisq.core.btc.wallet.TxBroadcastException;
+import bisq.core.btc.model.RawTransactionInput;
import bisq.core.btc.wallet.TxBroadcaster;
import bisq.core.trade.Trade;
import bisq.core.trade.protocol.TradingPeer;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerCreateAndSignContract.java b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerCreateAndSignContract.java
index 1d0844af26..2983383576 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerCreateAndSignContract.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerCreateAndSignContract.java
@@ -17,7 +17,7 @@
package bisq.core.trade.protocol.tasks.maker;
-import bisq.core.btc.AddressEntry;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.payment.payload.PaymentAccountPayload;
import bisq.core.trade.BuyerAsMakerTrade;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerProcessDepositTxPublishedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerProcessDepositTxPublishedMessage.java
index dd1b7e97cf..5325934d98 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerProcessDepositTxPublishedMessage.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerProcessDepositTxPublishedMessage.java
@@ -17,7 +17,7 @@
package bisq.core.trade.protocol.tasks.maker;
-import bisq.core.btc.AddressEntry;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.trade.Trade;
import bisq.core.trade.messages.DepositTxPublishedMessage;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSendPublishDepositTxRequest.java b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSendPublishDepositTxRequest.java
index 33f1f8e5fa..82e2dd1fe1 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSendPublishDepositTxRequest.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSendPublishDepositTxRequest.java
@@ -17,7 +17,7 @@
package bisq.core.trade.protocol.tasks.maker;
-import bisq.core.btc.AddressEntry;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.payment.payload.PaymentAccountPayload;
import bisq.core.trade.Trade;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSetupDepositTxListener.java b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSetupDepositTxListener.java
index 5fe05c1c4b..e296f4015a 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSetupDepositTxListener.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSetupDepositTxListener.java
@@ -17,8 +17,8 @@
package bisq.core.trade.protocol.tasks.maker;
-import bisq.core.btc.AddressEntry;
import bisq.core.btc.listeners.AddressConfidenceListener;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.trade.Trade;
import bisq.core.trade.protocol.tasks.TradeTask;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerBroadcastPayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerBroadcastPayoutTx.java
index dd90bd6756..c056b52dd8 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerBroadcastPayoutTx.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerBroadcastPayoutTx.java
@@ -17,7 +17,7 @@
package bisq.core.trade.protocol.tasks.seller;
-import bisq.core.btc.wallet.TxBroadcastException;
+import bisq.core.btc.exceptions.TxBroadcastException;
import bisq.core.btc.wallet.TxBroadcaster;
import bisq.core.trade.Trade;
import bisq.core.trade.protocol.tasks.TradeTask;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignAndFinalizePayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignAndFinalizePayoutTx.java
index eafadb8c5c..2648a14f99 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignAndFinalizePayoutTx.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignAndFinalizePayoutTx.java
@@ -17,7 +17,7 @@
package bisq.core.trade.protocol.tasks.seller;
-import bisq.core.btc.AddressEntry;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.offer.Offer;
import bisq.core.trade.Trade;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_maker/SellerAsMakerCreatesAndSignsDepositTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_maker/SellerAsMakerCreatesAndSignsDepositTx.java
index 11a861dff8..5cf0c2c3d7 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_maker/SellerAsMakerCreatesAndSignsDepositTx.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_maker/SellerAsMakerCreatesAndSignsDepositTx.java
@@ -17,10 +17,10 @@
package bisq.core.trade.protocol.tasks.seller_as_maker;
-import bisq.core.btc.AddressEntry;
-import bisq.core.btc.data.PreparedDepositTxAndMakerInputs;
-import bisq.core.btc.data.RawTransactionInput;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
+import bisq.core.btc.model.PreparedDepositTxAndMakerInputs;
+import bisq.core.btc.model.RawTransactionInput;
import bisq.core.offer.Offer;
import bisq.core.trade.Trade;
import bisq.core.trade.protocol.TradingPeer;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerCreatesDepositTxInputs.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerCreatesDepositTxInputs.java
index 64f13f6711..14befb65d4 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerCreatesDepositTxInputs.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerCreatesDepositTxInputs.java
@@ -17,9 +17,9 @@
package bisq.core.trade.protocol.tasks.seller_as_taker;
-import bisq.core.btc.AddressEntry;
-import bisq.core.btc.data.InputsAndChangeOutput;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
+import bisq.core.btc.model.InputsAndChangeOutput;
import bisq.core.trade.Trade;
import bisq.core.trade.protocol.tasks.TradeTask;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java
index ff9be4ef6f..73709a364e 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller_as_taker/SellerAsTakerSignAndPublishDepositTx.java
@@ -17,10 +17,10 @@
package bisq.core.trade.protocol.tasks.seller_as_taker;
-import bisq.core.btc.AddressEntry;
-import bisq.core.btc.data.RawTransactionInput;
+import bisq.core.btc.exceptions.TxBroadcastException;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
-import bisq.core.btc.wallet.TxBroadcastException;
+import bisq.core.btc.model.RawTransactionInput;
import bisq.core.btc.wallet.TxBroadcaster;
import bisq.core.trade.Contract;
import bisq.core.trade.Trade;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/CreateTakerFeeTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/CreateTakerFeeTx.java
index 06d181f36f..4a67933a29 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/CreateTakerFeeTx.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/CreateTakerFeeTx.java
@@ -18,11 +18,11 @@
package bisq.core.trade.protocol.tasks.taker;
import bisq.core.arbitration.Arbitrator;
-import bisq.core.btc.AddressEntry;
+import bisq.core.btc.exceptions.TxBroadcastException;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.btc.wallet.TradeWalletService;
-import bisq.core.btc.wallet.TxBroadcastException;
import bisq.core.btc.wallet.TxBroadcaster;
import bisq.core.btc.wallet.WalletService;
import bisq.core.trade.Trade;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerSendPayDepositRequest.java b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerSendPayDepositRequest.java
index a39279427c..353690b2e4 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerSendPayDepositRequest.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerSendPayDepositRequest.java
@@ -17,7 +17,7 @@
package bisq.core.trade.protocol.tasks.taker;
-import bisq.core.btc.AddressEntry;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.payment.payload.PaymentAccountPayload;
import bisq.core.trade.Trade;
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java
index 05760aa96a..38430830f7 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/taker/TakerVerifyAndSignContract.java
@@ -17,7 +17,7 @@
package bisq.core.trade.protocol.tasks.taker;
-import bisq.core.btc.AddressEntry;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.payment.payload.PaymentAccountPayload;
import bisq.core.trade.Contract;
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 77b3055dd9..bd7ee28df8 100644
--- a/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java
+++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatistics2.java
@@ -23,6 +23,7 @@ import bisq.core.monetary.AltcoinExchangeRate;
import bisq.core.monetary.Price;
import bisq.core.monetary.Volume;
import bisq.core.offer.OfferPayload;
+import bisq.core.offer.OfferUtil;
import bisq.network.p2p.storage.payload.CapabilityRequiringPayload;
import bisq.network.p2p.storage.payload.LazyProcessedPayload;
@@ -253,10 +254,12 @@ public final class TradeStatistics2 implements LazyProcessedPayload, Persistable
}
public Volume getTradeVolume() {
- if (getTradePrice().getMonetary() instanceof Altcoin)
+ if (getTradePrice().getMonetary() instanceof Altcoin) {
return new Volume(new AltcoinExchangeRate((Altcoin) getTradePrice().getMonetary()).coinToAltcoin(getTradeAmount()));
- else
- return new Volume(new ExchangeRate((Fiat) getTradePrice().getMonetary()).coinToFiat(getTradeAmount()));
+ } else {
+ Volume volume = new Volume(new ExchangeRate((Fiat) getTradePrice().getMonetary()).coinToFiat(getTradeAmount()));
+ return OfferUtil.getRoundedFiatVolume(volume);
+ }
}
@Override
diff --git a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java
index 7be153f351..b47cb5dad4 100644
--- a/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java
+++ b/core/src/main/java/bisq/core/trade/statistics/TradeStatisticsManager.java
@@ -18,6 +18,8 @@
package bisq.core.trade.statistics;
import bisq.core.app.AppOptionKeys;
+import bisq.core.dao.governance.asset.AssetService;
+import bisq.core.locale.CryptoCurrency;
import bisq.core.locale.CurrencyTuple;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
@@ -32,14 +34,19 @@ import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService;
import bisq.common.UserThread;
import bisq.common.storage.JsonFileManager;
import bisq.common.storage.Storage;
+import bisq.common.util.Tuple2;
import bisq.common.util.Utilities;
+import org.bitcoinj.core.Coin;
+
import com.google.inject.Inject;
import com.google.inject.name.Named;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
+import java.time.Duration;
+
import java.io.File;
import java.util.ArrayList;
@@ -59,29 +66,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j
public class TradeStatisticsManager {
- static TradeStatistics2 ConvertToTradeStatistics2(TradeStatistics tradeStatistics) {
- return new TradeStatistics2(tradeStatistics.getDirection(),
- tradeStatistics.getBaseCurrency(),
- tradeStatistics.getCounterCurrency(),
- tradeStatistics.getOfferPaymentMethod(),
- tradeStatistics.getOfferDate(),
- tradeStatistics.isOfferUseMarketBasedPrice(),
- tradeStatistics.getOfferMarketPriceMargin(),
- tradeStatistics.getOfferAmount(),
- tradeStatistics.getOfferMinAmount(),
- tradeStatistics.getOfferId(),
- tradeStatistics.getTradePrice().getValue(),
- tradeStatistics.getTradeAmount().getValue(),
- tradeStatistics.getTradeDate().getTime(),
- tradeStatistics.getDepositTxId(),
- null,
- tradeStatistics.getExtraDataMap());
- }
-
private final JsonFileManager jsonFileManager;
private final P2PService p2PService;
private final PriceFeedService priceFeedService;
private final ReferralIdService referralIdService;
+ private final AssetService assetService;
private final boolean dumpStatistics;
private final ObservableSet observableTradeStatisticsSet = FXCollections.observableSet();
@@ -91,11 +80,13 @@ public class TradeStatisticsManager {
TradeStatistics2StorageService tradeStatistics2StorageService,
AppendOnlyDataStoreService appendOnlyDataStoreService,
ReferralIdService referralIdService,
+ AssetService assetService,
@Named(Storage.STORAGE_DIR) File storageDir,
@Named(AppOptionKeys.DUMP_STATISTICS) boolean dumpStatistics) {
this.p2PService = p2PService;
this.priceFeedService = priceFeedService;
this.referralIdService = referralIdService;
+ this.assetService = assetService;
this.dumpStatistics = dumpStatistics;
jsonFileManager = new JsonFileManager(storageDir);
@@ -130,8 +121,7 @@ public class TradeStatisticsManager {
priceFeedService.applyLatestBisqMarketPrice(observableTradeStatisticsSet);
dump();
- // print all currencies sorted by nr. of trades
- // printAllCurrencyStats();
+ checkTradeActivity();
}
public void publishTradeStatistics(List trades) {
@@ -208,151 +198,102 @@ public class TradeStatisticsManager {
}
}
- // To have automatic check and removal we would need new fields in the asset class for the date when it was
- // added/released and a property if it was removed due either getting blocked by the DAO stakehodlers in voting or
- // removed due lack of activity.
- // For now we use the old script below to print the usage of the coins.
- private void printAllCurrencyStats() {
- Map> map1 = new HashMap<>();
- for (TradeStatistics2 tradeStatistics : observableTradeStatisticsSet) {
- if (CurrencyUtil.isFiatCurrency(tradeStatistics.getCounterCurrency())) {
- String counterCurrency = CurrencyUtil.getNameAndCode(tradeStatistics.getCounterCurrency());
- if (!map1.containsKey(counterCurrency))
- map1.put(counterCurrency, new HashSet<>());
+ private void checkTradeActivity() {
+ Date compareDate = new Date(new Date().getTime() - Duration.ofDays(120).toMillis());
+ long minTradeAmount = Coin.parseCoin("0.01").value;
+ long minNumOfTrades = 3;
- map1.get(counterCurrency).add(tradeStatistics);
+ Map> tradeStatMap = new HashMap<>();
+ observableTradeStatisticsSet.stream()
+ .filter(e -> CurrencyUtil.isCryptoCurrency(e.getBaseCurrency()))
+ .filter(e -> e.getTradeDate().getTime() > compareDate.getTime())
+ .forEach(e -> {
+ tradeStatMap.putIfAbsent(e.getBaseCurrency(), new Tuple2<>(0L, 0));
+ Tuple2 tuple2 = tradeStatMap.get(e.getBaseCurrency());
+ long accumulatedTradeAmount = tuple2.first + e.getTradeAmount().getValue();
+ int numTrades = tuple2.second + 1;
+ tradeStatMap.put(e.getBaseCurrency(), new Tuple2<>(accumulatedTradeAmount, numTrades));
+ });
+ StringBuilder sufficientlyTraded = new StringBuilder("\nSufficiently traded assets:");
+ StringBuilder insufficientlyTraded = new StringBuilder("\nInsufficiently traded assets:");
+ StringBuilder notTraded = new StringBuilder("\nNot traded assets:");
+ List whiteListedSortedCryptoCurrencies = CurrencyUtil.getWhiteListedSortedCryptoCurrencies(assetService);
+ Set assetsToRemove = new HashSet<>(whiteListedSortedCryptoCurrencies);
+ whiteListedSortedCryptoCurrencies.forEach(e -> {
+ String code = e.getCode();
+ if (!isWarmingUp(code) && !hasPaidBSQFee(code)) {
+ String nameAndCode = CurrencyUtil.getNameAndCode(code);
+ if (tradeStatMap.containsKey(code)) {
+ Tuple2 tuple = tradeStatMap.get(code);
+ Long tradeAmount = tuple.first;
+ Integer numTrades = tuple.second;
+ if (tradeAmount >= minTradeAmount || numTrades >= minNumOfTrades) {
+ assetsToRemove.remove(e);
+ sufficientlyTraded.append("\n")
+ .append(nameAndCode)
+ .append(": Trade amount: ")
+ .append(Coin.valueOf(tradeAmount).toFriendlyString())
+ .append(", number of trades: ")
+ .append(numTrades);
+ } else {
+ insufficientlyTraded.append("\n")
+ .append(nameAndCode)
+ .append(": Trade amount: ")
+ .append(Coin.valueOf(tradeAmount).toFriendlyString())
+ .append(", number of trades: ")
+ .append(numTrades);
+ }
+ } else {
+ assetsToRemove.remove(e);
+ notTraded.append("\n").append(nameAndCode);
+ }
}
- }
+ });
- StringBuilder sb1 = new StringBuilder("\nAll traded Fiat currencies:\n");
- map1.entrySet().stream()
- .sorted((o1, o2) -> Integer.compare(o2.getValue().size(), o1.getValue().size()))
- .forEach(e -> sb1.append(e.getKey()).append(": ").append(e.getValue().size()).append("\n"));
- log.error(sb1.toString());
+ log.debug(sufficientlyTraded.toString());
+ log.debug(insufficientlyTraded.toString());
+ log.debug(notTraded.toString());
+ }
- Map> map2 = new HashMap<>();
- for (TradeStatistics2 tradeStatistics : observableTradeStatisticsSet) {
- if (CurrencyUtil.isCryptoCurrency(tradeStatistics.getBaseCurrency())) {
- final String code = CurrencyUtil.getNameAndCode(tradeStatistics.getBaseCurrency());
- if (!map2.containsKey(code))
- map2.put(code, new HashSet<>());
+ private boolean hasPaidBSQFee(String code) {
+ return assetService.hasPaidBSQFee(code);
+ }
- map2.get(code).add(tradeStatistics);
- }
- }
-
- List allCryptoCurrencies = new ArrayList<>();
- Set coinsWithValidator = new HashSet<>();
-
- // List of coins with validator before 0.6.0 hard requirements for address validator
- coinsWithValidator.add("BTC");
- coinsWithValidator.add("BSQ");
- coinsWithValidator.add("LTC");
- coinsWithValidator.add("DOGE");
- coinsWithValidator.add("DASH");
- coinsWithValidator.add("ETH");
- coinsWithValidator.add("PIVX");
- coinsWithValidator.add("IOP");
- coinsWithValidator.add("888");
- coinsWithValidator.add("ZEC");
- coinsWithValidator.add("GBYTE");
- coinsWithValidator.add("NXT");
-
- // All those need to have a address validator
+ private boolean isWarmingUp(String code) {
Set newlyAdded = new HashSet<>();
- // v0.6.0
- newlyAdded.add("DCT");
- newlyAdded.add("PNC");
- newlyAdded.add("WAC");
- newlyAdded.add("ZEN");
- newlyAdded.add("ELLA");
- newlyAdded.add("XCN");
- newlyAdded.add("TRC");
- newlyAdded.add("INXT");
- newlyAdded.add("PART");
- // v0.6.1
- newlyAdded.add("MAD");
- newlyAdded.add("BCH");
- newlyAdded.add("BCHC");
- newlyAdded.add("BTG");
- // v0.6.2
- newlyAdded.add("CAGE");
- newlyAdded.add("CRED");
- newlyAdded.add("XSPEC");
- // v0.6.3
- newlyAdded.add("WILD");
- newlyAdded.add("ONION");
- // v0.6.4
- newlyAdded.add("CREA");
- newlyAdded.add("XIN");
- // v0.6.5
- newlyAdded.add("BETR");
- newlyAdded.add("MVT");
- newlyAdded.add("REF");
- // v0.6.6
- newlyAdded.add("STL");
- newlyAdded.add("DAI");
- newlyAdded.add("YTN");
- newlyAdded.add("DARX");
- newlyAdded.add("ODN");
- newlyAdded.add("CDT");
- newlyAdded.add("DGM");
- newlyAdded.add("SCS");
- newlyAdded.add("SOS");
- newlyAdded.add("ACH");
- newlyAdded.add("VDN");
- // v0.7.0
- newlyAdded.add("ALC");
- newlyAdded.add("DIN");
- newlyAdded.add("NAH");
- newlyAdded.add("ROI");
- newlyAdded.add("WMCC");
- newlyAdded.add("RTO");
- newlyAdded.add("KOTO");
- newlyAdded.add("PHR");
- newlyAdded.add("UBQ");
- newlyAdded.add("QWARK");
- newlyAdded.add("GEO");
- newlyAdded.add("GRANS");
- newlyAdded.add("ICH");
- // TODO add remaining coins since 0.7.0
- //newlyAdded.clear();
- /* new AssetRegistry().stream()
- .sorted(Comparator.comparing(o -> o.getName().toLowerCase()))
- .filter(e -> !e.getTickerSymbol().equals("BSQ")) // BSQ is not out yet...
- .filter(e -> !e.getTickerSymbol().equals("BTC"))
- .map(e -> e.getTickerSymbol()) // We want to get rid of duplicated entries for regtest/testnet...
- .distinct()
- .forEach(e -> newlyAdded.add(e));*/
+ // v0.7.1 Jul 4 2018
+ newlyAdded.add("ZOC");
+ newlyAdded.add("AQUA");
+ newlyAdded.add("BTDX");
+ newlyAdded.add("BTCC");
+ newlyAdded.add("BTI");
+ newlyAdded.add("CRDS");
+ newlyAdded.add("CNMC");
+ newlyAdded.add("TARI");
+ newlyAdded.add("DAC");
+ newlyAdded.add("DRIP");
+ newlyAdded.add("FTO");
+ newlyAdded.add("GRFT");
+ newlyAdded.add("LIKE");
+ newlyAdded.add("LOBS");
+ newlyAdded.add("MAX");
+ newlyAdded.add("MEC");
+ newlyAdded.add("MCC");
+ newlyAdded.add("XMN");
+ newlyAdded.add("XMY");
+ newlyAdded.add("NANO");
+ newlyAdded.add("NPW");
+ newlyAdded.add("NIM");
+ newlyAdded.add("PIX");
+ newlyAdded.add("PXL");
+ newlyAdded.add("PRIV");
+ newlyAdded.add("TRIT");
+ newlyAdded.add("WAVI");
- coinsWithValidator.addAll(newlyAdded);
+ // v0.8.0 Aug 22 2018
+ // none added
- CurrencyUtil.getAllSortedCryptoCurrencies()
- .forEach(e -> allCryptoCurrencies.add(e.getNameAndCode()));
- StringBuilder sb2 = new StringBuilder("\nAll traded Crypto currencies:\n");
- StringBuilder sb3 = new StringBuilder("\nNever traded Crypto currencies:\n");
- map2.entrySet().stream()
- .sorted((o1, o2) -> Integer.compare(o2.getValue().size(), o1.getValue().size()))
- .forEach(e -> {
- String key = e.getKey();
- sb2.append(key).append(": ").append(e.getValue().size()).append("\n");
- // key is: USD Tether (USDT)
- String code = key.substring(key.indexOf("(") + 1, key.length() - 1);
- if (!coinsWithValidator.contains(code) && !newlyAdded.contains(code))
- allCryptoCurrencies.remove(key);
- });
- log.error(sb2.toString());
-
- // Not considered age of newly added coins, so take care with removal if coin was added recently.
- allCryptoCurrencies.sort(String::compareTo);
- allCryptoCurrencies
- .forEach(e -> {
- // key is: USD Tether (USDT)
- String code = e.substring(e.indexOf("(") + 1, e.length() - 1);
- if (!coinsWithValidator.contains(code) && !newlyAdded.contains(code))
- sb3.append(e).append("\n");
- });
- log.error(sb3.toString());
+ return newlyAdded.contains(code);
}
}
diff --git a/core/src/main/java/bisq/core/user/Preferences.java b/core/src/main/java/bisq/core/user/Preferences.java
index 5d252d0758..173b258b3b 100644
--- a/core/src/main/java/bisq/core/user/Preferences.java
+++ b/core/src/main/java/bisq/core/user/Preferences.java
@@ -20,9 +20,9 @@ package bisq.core.user;
import bisq.core.app.AppOptionKeys;
import bisq.core.app.BisqEnvironment;
import bisq.core.btc.BaseCurrencyNetwork;
-import bisq.core.btc.BitcoinNodes;
import bisq.core.btc.BtcOptionKeys;
-import bisq.core.btc.Restrictions;
+import bisq.core.btc.nodes.BtcNodes;
+import bisq.core.btc.wallet.Restrictions;
import bisq.core.locale.Country;
import bisq.core.locale.CountryUtil;
import bisq.core.locale.CryptoCurrency;
@@ -283,7 +283,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
"The Bitcoin node(s) {} from the program argument will be used.", btcNodesFromOptions);
}
setBitcoinNodes(btcNodesFromOptions);
- setBitcoinNodesOptionOrdinal(BitcoinNodes.BitcoinNodesOption.CUSTOM.ordinal());
+ setBitcoinNodesOptionOrdinal(BtcNodes.BitcoinNodesOption.CUSTOM.ordinal());
}
if (referralIdFromOptions != null && !referralIdFromOptions.isEmpty())
setReferralId(referralIdFromOptions);
diff --git a/core/src/main/java/bisq/core/user/PreferencesPayload.java b/core/src/main/java/bisq/core/user/PreferencesPayload.java
index e850943b97..53f2ffd428 100644
--- a/core/src/main/java/bisq/core/user/PreferencesPayload.java
+++ b/core/src/main/java/bisq/core/user/PreferencesPayload.java
@@ -17,7 +17,7 @@
package bisq.core.user;
-import bisq.core.btc.Restrictions;
+import bisq.core.btc.wallet.Restrictions;
import bisq.core.locale.Country;
import bisq.core.locale.CryptoCurrency;
import bisq.core.locale.FiatCurrency;
diff --git a/core/src/main/java/bisq/core/util/BSFormatter.java b/core/src/main/java/bisq/core/util/BSFormatter.java
index 0699df4866..1a49166bb1 100644
--- a/core/src/main/java/bisq/core/util/BSFormatter.java
+++ b/core/src/main/java/bisq/core/util/BSFormatter.java
@@ -328,7 +328,7 @@ public class BSFormatter {
// TODO quick hack...
String res;
if (altcoin.getCurrencyCode().equals("BSQ"))
- res = altcoinFormat.noCode().minDecimals(3).repeatOptionalDecimals(0, 0).format(altcoin).toString();
+ res = altcoinFormat.noCode().minDecimals(2).repeatOptionalDecimals(0, 0).format(altcoin).toString();
else
res = altcoinFormat.noCode().format(altcoin).toString();
if (appendCurrencyCode)
diff --git a/core/src/main/java/bisq/core/util/BsqFormatter.java b/core/src/main/java/bisq/core/util/BsqFormatter.java
index 6ad5fc4a70..fa33b34610 100644
--- a/core/src/main/java/bisq/core/util/BsqFormatter.java
+++ b/core/src/main/java/bisq/core/util/BsqFormatter.java
@@ -18,6 +18,8 @@
package bisq.core.util;
import bisq.core.app.BisqEnvironment;
+import bisq.core.dao.state.governance.Param;
+import bisq.core.locale.Res;
import bisq.core.provider.price.MarketPrice;
import bisq.common.app.DevEnv;
@@ -114,4 +116,105 @@ public class BsqFormatter extends BSFormatter {
return Coin.ZERO;
}
}
+
+
+ public String formatParamValue(Param param, long value) {
+ switch (param) {
+ case UNDEFINED:
+ return Res.get("shared.na");
+
+ case DEFAULT_MAKER_FEE_BSQ:
+ case DEFAULT_TAKER_FEE_BSQ:
+ case DEFAULT_MAKER_FEE_BTC:
+ case DEFAULT_TAKER_FEE_BTC:
+ return formatToPercentWithSymbol(value / 10000d);
+
+ case PROPOSAL_FEE:
+ case BLIND_VOTE_FEE:
+ case COMPENSATION_REQUEST_MIN_AMOUNT:
+ case COMPENSATION_REQUEST_MAX_AMOUNT:
+ return formatCoinWithCode(Coin.valueOf(value));
+
+ case QUORUM_COMP_REQUEST:
+ case QUORUM_CHANGE_PARAM:
+ case QUORUM_ROLE:
+ case QUORUM_CONFISCATION:
+ case QUORUM_GENERIC:
+ case QUORUM_REMOVE_ASSET:
+ return formatCoinWithCode(Coin.valueOf(value));
+
+ case THRESHOLD_COMP_REQUEST:
+ case THRESHOLD_CHANGE_PARAM:
+ case THRESHOLD_ROLE:
+ case THRESHOLD_CONFISCATION:
+ case THRESHOLD_GENERIC:
+ case THRESHOLD_REMOVE_ASSET:
+ return formatToPercentWithSymbol(value / 10000d);
+
+ case PHASE_UNDEFINED:
+ return Res.get("shared.na");
+ case PHASE_PROPOSAL:
+ case PHASE_BREAK1:
+ case PHASE_BLIND_VOTE:
+ case PHASE_BREAK2:
+ case PHASE_VOTE_REVEAL:
+ case PHASE_BREAK3:
+ case PHASE_RESULT:
+ return Res.get("dao.param.blocks", value);
+
+ default:
+ return Res.get("shared.na");
+ }
+ }
+
+ public long parseParamValue(Param param, String inputValue) {
+ switch (param) {
+ case UNDEFINED:
+ return 0;
+
+ case DEFAULT_MAKER_FEE_BSQ:
+ case DEFAULT_TAKER_FEE_BSQ:
+ case DEFAULT_MAKER_FEE_BTC:
+ case DEFAULT_TAKER_FEE_BTC:
+ return (long) (parsePercentStringToDouble(inputValue) * 10000);
+
+ case PROPOSAL_FEE:
+ case BLIND_VOTE_FEE:
+ case COMPENSATION_REQUEST_MIN_AMOUNT:
+ case COMPENSATION_REQUEST_MAX_AMOUNT:
+ return parseToCoin(inputValue).value;
+
+
+ case QUORUM_COMP_REQUEST:
+ case QUORUM_CHANGE_PARAM:
+ case QUORUM_ROLE:
+ case QUORUM_CONFISCATION:
+ case QUORUM_GENERIC:
+ case QUORUM_REMOVE_ASSET:
+ return parseToCoin(inputValue).value;
+
+
+ case THRESHOLD_COMP_REQUEST:
+ case THRESHOLD_CHANGE_PARAM:
+ case THRESHOLD_ROLE:
+ case THRESHOLD_CONFISCATION:
+ case THRESHOLD_GENERIC:
+ case THRESHOLD_REMOVE_ASSET:
+ return (long) (parsePercentStringToDouble(inputValue) * 10000);
+
+ case PHASE_UNDEFINED:
+ return 0;
+ case PHASE_PROPOSAL:
+ case PHASE_BREAK1:
+ case PHASE_BLIND_VOTE:
+ case PHASE_BREAK2:
+ case PHASE_VOTE_REVEAL:
+ case PHASE_BREAK3:
+ case PHASE_RESULT:
+ return Long.valueOf(inputValue);
+
+ default:
+ return 0;
+ }
+ }
}
diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties
index 9450e6541d..669ef32427 100644
--- a/core/src/main/resources/i18n/displayStrings.properties
+++ b/core/src/main/resources/i18n/displayStrings.properties
@@ -515,6 +515,7 @@ portfolio.pending.step2_buyer.f2f=Please contact the BTC seller by the provided
portfolio.pending.step2_buyer.startPaymentUsing=Start payment using {0}
portfolio.pending.step2_buyer.amountToTransfer=Amount to transfer:
portfolio.pending.step2_buyer.sellersAddress=Seller''s {0} address:
+portfolio.pending.step2_buyer.buyerAccount=Your payment account to be used
portfolio.pending.step2_buyer.paymentStarted=Payment started
portfolio.pending.step2_buyer.warn=You still have not done your {0} payment!\nPlease note that the trade has to be completed by {1} otherwise the trade will be investigated by the arbitrator.
portfolio.pending.step2_buyer.openForDispute=You have not completed your payment!\nThe max. period for the trade has elapsed.\n\nPlease contact the arbitrator for opening a dispute.
@@ -569,7 +570,9 @@ portfolio.pending.step3_buyer.warn.part2=The BTC seller still has not confirmed
portfolio.pending.step3_buyer.openForDispute=The BTC seller has not confirmed your payment!\nThe max. period for the trade has elapsed.\nYou can wait longer and give the trading peer more time or contact the arbitrator for opening a dispute.
# suppress inspection "TrailingSpacesInProperty"
portfolio.pending.step3_seller.part=Your trading partner has confirmed that he initiated the {0} payment.\n\n
-portfolio.pending.step3_seller.altcoin={0}Please check on your favorite {1} blockchain explorer if the transaction to your receiving address\n\
+portfolio.pending.step3_seller.altcoin.explorer=on your favorite {0} blockchain explorer
+portfolio.pending.step3_seller.altcoin.wallet=at your {0} wallet
+portfolio.pending.step3_seller.altcoin={0}Please check {1} if the transaction to your receiving address\n\
{2}\n\
has already sufficient blockchain confirmations.\nThe payment amount has to be {3}\n\n\
You can copy & paste your {4} address from the main screen after closing that popup.
@@ -731,7 +734,7 @@ funds.locked.locked=Locked in multisig for trade with ID: {0}
funds.tx.direction.sentTo=Sent to:
funds.tx.direction.receivedWith=Received with:
funds.tx.direction.genesisTx=From Genesis tx:
-funds.tx.txFeePaymentForBsqTx=Tx fee payment for BSQ tx
+funds.tx.txFeePaymentForBsqTx=Miner fee for BSQ tx
funds.tx.createOfferFee=Maker and tx fee: {0}
funds.tx.takeOfferFee=Taker and tx fee: {0}
funds.tx.multiSigDeposit=Multisig deposit: {0}
@@ -855,7 +858,7 @@ setting.preferences.resetAllFlags=Reset all \"Don't show again\" flags:
setting.preferences.reset=Reset
settings.preferences.languageChange=To apply the language change to all screens requires a restart.
settings.preferences.arbitrationLanguageWarning=In case of a dispute, please note that arbitration is handled in {0}.
-settings.preferences.selectCurrencyNetwork=Select base currency
+settings.preferences.selectCurrencyNetwork=Select network
settings.net.btcHeader=Bitcoin network
settings.net.p2pHeader=P2P network
@@ -1190,42 +1193,66 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal
# suppress inspection "UnusedProperty"
dao.param.UNDEFINED=Undefined
+
# suppress inspection "UnusedProperty"
-dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ
+dao.param.DEFAULT_MAKER_FEE_BSQ=BSQ maker fee
# suppress inspection "UnusedProperty"
-dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ
+dao.param.DEFAULT_TAKER_FEE_BSQ=BSQ taker fee
# suppress inspection "UnusedProperty"
-dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC
+dao.param.MIN_MAKER_FEE_BSQ=Min. BSQ maker fee
# suppress inspection "UnusedProperty"
-dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC
+dao.param.MIN_TAKER_FEE_BSQ=Min. BSQ taker fee
+# suppress inspection "UnusedProperty"
+dao.param.DEFAULT_MAKER_FEE_BTC=BTC maker fee
+# suppress inspection "UnusedProperty"
+dao.param.DEFAULT_TAKER_FEE_BTC=BTC taker fee
+# suppress inspection "UnusedProperty"
+# suppress inspection "UnusedProperty"
+dao.param.MIN_MAKER_FEE_BTC=Min. BTC maker fee
+# suppress inspection "UnusedProperty"
+dao.param.MIN_TAKER_FEE_BTC=Min. BTC taker fee
# suppress inspection "UnusedProperty"
# suppress inspection "UnusedProperty"
-dao.param.PROPOSAL_FEE=Proposal fee
+dao.param.PROPOSAL_FEE=Proposal fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BLIND_VOTE_FEE=Voting fee
+dao.param.BLIND_VOTE_FEE=Voting fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_PROPOSAL=Required quorum for proposal
+dao.param.COMPENSATION_REQUEST_MIN_AMOUNT=Compensation request min. BSQ amount
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request
-# suppress inspection "UnusedProperty"
-dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter
-# suppress inspection "UnusedProperty"
-dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin
-# suppress inspection "UnusedProperty"
-dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation
+dao.param.COMPENSATION_REQUEST_MAX_AMOUNT=Compensation request max. BSQ amount
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal
+dao.param.QUORUM_GENERIC=Required quorum in BSQ for generic proposal
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request
+dao.param.QUORUM_COMP_REQUEST=Required quorum in BSQ for compensation request
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter
+dao.param.QUORUM_CHANGE_PARAM=Required quorum in BSQ for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin
+dao.param.QUORUM_REMOVE_ASSET=Required quorum in BSQ for removing an asset
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation
+dao.param.QUORUM_CONFISCATION=Required quorum in BSQ for bond confiscation
+# suppress inspection "UnusedProperty"
+dao.param.QUORUM_ROLE=Required quorum in BSQ for taking a bonded role
+
+
+
+# suppress inspection "UnusedProperty"
+dao.param.THRESHOLD_GENERIC=Required threshold in % for generic proposal
+# suppress inspection "UnusedProperty"
+dao.param.THRESHOLD_COMP_REQUEST=Required threshold in % for compensation request
+# suppress inspection "UnusedProperty"
+dao.param.THRESHOLD_CHANGE_PARAM=Required threshold in % for changing a parameter
+# suppress inspection "UnusedProperty"
+dao.param.THRESHOLD_REMOVE_ASSET=Required threshold in % for removing an asset
+# suppress inspection "UnusedProperty"
+dao.param.THRESHOLD_CONFISCATION=Required threshold in % for bond confiscation
+# suppress inspection "UnusedProperty"
+dao.param.THRESHOLD_ROLE=Required threshold in % for taking a bonded role
+
+dao.param.currentValue=Current value: {0}
+dao.param.blocks={0} blocks
# suppress inspection "UnusedProperty"
dao.results.cycle.duration.label=Duration of {0}
@@ -1252,8 +1279,6 @@ dao.phase.PHASE_VOTE_REVEAL=Vote reveal phase
dao.phase.PHASE_BREAK3=Break 3
# suppress inspection "UnusedProperty"
dao.phase.PHASE_RESULT=Result phase
-# suppress inspection "UnusedProperty"
-dao.phase.PHASE_BREAK4=Break 4
dao.results.votes.table.header.stakeAndMerit=Vote weight
dao.results.votes.table.header.stake=Stake
@@ -1352,7 +1377,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Compensation request
# suppress inspection "UnusedProperty"
dao.proposal.type.BONDED_ROLE=Proposal for a bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.REMOVE_ALTCOIN=Proposal for removing an altcoin
+dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset
# suppress inspection "UnusedProperty"
dao.proposal.type.CHANGE_PARAM=Proposal for changing a parameter
# suppress inspection "UnusedProperty"
@@ -1365,7 +1390,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Compensation request
# suppress inspection "UnusedProperty"
dao.proposal.type.short.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin
+dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin
# suppress inspection "UnusedProperty"
dao.proposal.type.short.CHANGE_PARAM=Changing a parameter
# suppress inspection "UnusedProperty"
@@ -1405,6 +1430,7 @@ dao.proposal.display.proposalFee=Proposal fee:
dao.proposal.display.myVote=My vote:
dao.proposal.display.voteResult=Vote result summary:
dao.proposal.display.bondedRoleComboBox.label=Choose bonded role type
+dao.proposal.display.tickerSymbol.label=Ticker Symbol
dao.proposal.table.header.proposalType=Proposal type
dao.proposal.table.header.link=Link
@@ -1422,6 +1448,7 @@ dao.proposal.display.paramComboBox.label=Choose parameter
dao.proposal.display.paramValue=Parameter value:
dao.proposal.display.confiscateBondComboBox.label=Choose bond
+dao.proposal.display.assetComboBox.label=Asset to remove
dao.blindVote=blind vote
@@ -2089,6 +2116,7 @@ payment.emailOrMobile=Email or mobile nr:
payment.useCustomAccountName=Use custom account name
payment.maxPeriod=Max. allowed trade period:
payment.maxPeriodAndLimit=Max. trade duration: {0} / Max. trade limit: {1} / Account age: {2}
+payment.maxPeriodAndLimitCrypto=Max. trade duration: {0} / Max. trade limit: {1}
payment.currencyWithSymbol=Currency: {0}
payment.nameOfAcceptedBank=Name of accepted bank
payment.addAcceptedBank=Add accepted bank
@@ -2341,3 +2369,5 @@ validation.iban.invalidLength=Number must have length 15 to 34 chars.
validation.interacETransfer.invalidAreaCode=Non-Canadian area code
validation.interacETransfer.invalidPhone=Invalid phone number format and not an email address
validation.inputTooLarge=Input must not be larger than {0}
+validation.inputTooSmall=Input has to be larger than {0}
+validation.amountBelowDust=The amount below the dust limit of {0} is not allowed.
diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties
index 1725ac6c62..b4badf1de4 100644
--- a/core/src/main/resources/i18n/displayStrings_de.properties
+++ b/core/src/main/resources/i18n/displayStrings_de.properties
@@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Wahlergebnisse für gewählten Vorsch
# suppress inspection "UnusedProperty"
dao.param.UNDEFINED=Undefiniert
# suppress inspection "UnusedProperty"
-dao.param.BSQ_MAKER_FEE_IN_PERCENT=Erstellergebühr in BSQ
+dao.param.DEFAULT_MAKER_FEE_BSQ=Erstellergebühr in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BSQ_TAKER_FEE_IN_PERCENT=Abnehmergebühr in BSQ
+dao.param.DEFAULT_TAKER_FEE_BSQ=Abnehmergebühr in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BTC_MAKER_FEE_IN_PERCENT=Erstellergebühr in BTC
+dao.param.DEFAULT_MAKER_FEE_BTC=Erstellergebühr in BTC
# suppress inspection "UnusedProperty"
-dao.param.BTC_TAKER_FEE_IN_PERCENT=Abnehmergebühr in BTC
+dao.param.DEFAULT_TAKER_FEE_BTC=Abnehmergebühr in BTC
# suppress inspection "UnusedProperty"
# suppress inspection "UnusedProperty"
@@ -1065,7 +1065,7 @@ dao.param.PROPOSAL_FEE=Vorschlag-Gebühr
dao.param.BLIND_VOTE_FEE=Stimm-Gebühr
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_PROPOSAL=Benötigtes Quorum für Vorschlag
+dao.param.QUORUM_GENERIC=Benötigtes Quorum für Vorschlag
# suppress inspection "UnusedProperty"
dao.param.QUORUM_COMP_REQUEST=Benötigtes Quorum für Entlohnungsanfrage
# suppress inspection "UnusedProperty"
@@ -1076,7 +1076,7 @@ dao.param.QUORUM_REMOVE_ASSET=Benötigtes Quorum um einen Altcoin zu entfernen
dao.param.QUORUM_CONFISCATION=Benötigtes Quorum für Kopplung Konfiszierung
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_PROPOSAL=Benötigter Schwellwert für Vorschlag
+dao.param.THRESHOLD_GENERIC=Benötigter Schwellwert für Vorschlag
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_COMP_REQUEST=Benötigter Schwellwert für Entlohnungsanfrage
# suppress inspection "UnusedProperty"
@@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Entlohnungsanfrage
# suppress inspection "UnusedProperty"
dao.proposal.type.BONDED_ROLE=Vorschlag für Gekoppelte Rolle
# suppress inspection "UnusedProperty"
-dao.proposal.type.REMOVE_ALTCOIN=Vorschlag einen Altcoin zu entfernen
+dao.proposal.type.REMOVE_ASSET=Vorschlag einen Altcoin zu entfernen
# suppress inspection "UnusedProperty"
dao.proposal.type.CHANGE_PARAM=Vorschlag einen Parameter zu ändern
# suppress inspection "UnusedProperty"
@@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Entlohnungsanfrage
# suppress inspection "UnusedProperty"
dao.proposal.type.short.BONDED_ROLE=Gekoppelte Rolle
# suppress inspection "UnusedProperty"
-dao.proposal.type.short.REMOVE_ALTCOIN=Einen Altcoin entfernen
+dao.proposal.type.short.REMOVE_ASSET=Einen Altcoin entfernen
# suppress inspection "UnusedProperty"
dao.proposal.type.short.CHANGE_PARAM=Einen Parameter ändern
# suppress inspection "UnusedProperty"
@@ -1872,6 +1872,7 @@ payment.emailOrMobile=E-Mail oder Mobil:
payment.useCustomAccountName=Spezifischen Kontonamen nutzen
payment.maxPeriod=Max. erlaubte Handelsdauer:
payment.maxPeriodAndLimit=Max. Handelsdauer: {0} / Max. Handelsgrenze: {1} / Konto-Alter: {2}
+payment.maxPeriodAndLimitCrypto=Max. Handelsdauer: {0} / Max. Handelsgrenze: {1}
payment.currencyWithSymbol=Währung: {0}
payment.nameOfAcceptedBank=Name der akzeptierten Bank
payment.addAcceptedBank=Akzeptierte Bank hinzufügen
diff --git a/core/src/main/resources/i18n/displayStrings_el.properties b/core/src/main/resources/i18n/displayStrings_el.properties
index ad5d0a687d..9d5626fb29 100644
--- a/core/src/main/resources/i18n/displayStrings_el.properties
+++ b/core/src/main/resources/i18n/displayStrings_el.properties
@@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal
# suppress inspection "UnusedProperty"
dao.param.UNDEFINED=Ακαθόριστο
# suppress inspection "UnusedProperty"
-dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ
+dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ
+dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC
+dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC
# suppress inspection "UnusedProperty"
-dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC
+dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC
# suppress inspection "UnusedProperty"
# suppress inspection "UnusedProperty"
@@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee
dao.param.BLIND_VOTE_FEE=Voting fee
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_PROPOSAL=Required quorum for proposal
+dao.param.QUORUM_GENERIC=Required quorum for proposal
# suppress inspection "UnusedProperty"
dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin
+dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal
+dao.param.THRESHOLD_GENERIC=Required threshold for proposal
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin
+dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation
@@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Αίτημα αποζημίωσης
# suppress inspection "UnusedProperty"
dao.proposal.type.BONDED_ROLE=Proposal for a bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.REMOVE_ALTCOIN=Πρόταση για απόσυρση εναλλακτικού νομίσματος
+dao.proposal.type.REMOVE_ASSET=Πρόταση για απόσυρση εναλλακτικού νομίσματος
# suppress inspection "UnusedProperty"
dao.proposal.type.CHANGE_PARAM=Πρόταση για αλλαγή παραμέτρου
# suppress inspection "UnusedProperty"
@@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Αίτημα αποζημίωσης
# suppress inspection "UnusedProperty"
dao.proposal.type.short.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin
+dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin
# suppress inspection "UnusedProperty"
dao.proposal.type.short.CHANGE_PARAM=Changing a parameter
# suppress inspection "UnusedProperty"
@@ -1872,6 +1872,7 @@ payment.emailOrMobile=Email ή αριθμός κινητού:
payment.useCustomAccountName=Χρήση προεπιλεγμένου ονόματος λογαριασμού
payment.maxPeriod=Μέγιστη επιτρεπόμενη χρονική περίοδος συναλλαγής:
payment.maxPeriodAndLimit=Μέγιστη διάρκεια συναλλαγής: {0} / Μέγιστο όριο συναλλαγής: {1} / Ηλικία λογαριασμού: {2}
+payment.maxPeriodAndLimitCrypto=Μέγιστη διάρκεια συναλλαγής: {0} / Μέγιστο όριο συναλλαγής: {1}
payment.currencyWithSymbol=Νόμισμα: {0}
payment.nameOfAcceptedBank=Όνομα αποδεκτής τράπεζας
payment.addAcceptedBank=Εισαγωγή αποδεκτής τράπεζας
diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties
index 7ad01989b7..bda4746b0c 100644
--- a/core/src/main/resources/i18n/displayStrings_es.properties
+++ b/core/src/main/resources/i18n/displayStrings_es.properties
@@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal
# suppress inspection "UnusedProperty"
dao.param.UNDEFINED=Indefinido
# suppress inspection "UnusedProperty"
-dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ
+dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ
+dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC
+dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC
# suppress inspection "UnusedProperty"
-dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC
+dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC
# suppress inspection "UnusedProperty"
# suppress inspection "UnusedProperty"
@@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee
dao.param.BLIND_VOTE_FEE=Voting fee
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_PROPOSAL=Required quorum for proposal
+dao.param.QUORUM_GENERIC=Required quorum for proposal
# suppress inspection "UnusedProperty"
dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin
+dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal
+dao.param.THRESHOLD_GENERIC=Required threshold for proposal
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin
+dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation
@@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Petición de compensación
# suppress inspection "UnusedProperty"
dao.proposal.type.BONDED_ROLE=Proposal for a bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.REMOVE_ALTCOIN=Propuesta para eliminar una altcoin
+dao.proposal.type.REMOVE_ASSET=Propuesta para eliminar una altcoin
# suppress inspection "UnusedProperty"
dao.proposal.type.CHANGE_PARAM=Propuesta para cambiar un parámetro
# suppress inspection "UnusedProperty"
@@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Petición de compensación
# suppress inspection "UnusedProperty"
dao.proposal.type.short.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin
+dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin
# suppress inspection "UnusedProperty"
dao.proposal.type.short.CHANGE_PARAM=Changing a parameter
# suppress inspection "UnusedProperty"
@@ -1871,7 +1871,8 @@ payment.accountNr=Número de cuenta:
payment.emailOrMobile=Email o número de móvil:
payment.useCustomAccountName=Utilizar nombre de cuenta personalizado
payment.maxPeriod=Periodo máximo de intercambio:
-payment.maxPeriodAndLimit=Duración máxima de intercambio: {0} / Límite máximo de intercambio: {1}
+payment.maxPeriodAndLimit=Duración máxima de intercambio: {0} / Límite máximo de intercambio: {1}
+payment.maxPeriodAndLimitCrypto=Duración máxima de intercambio: {0} / Límite máximo de intercambio: {1}
payment.currencyWithSymbol=Moneda: {0}
payment.nameOfAcceptedBank=Nombre de banco aceptado
payment.addAcceptedBank=Añadir banco aceptado
diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties
index 8002087f0f..37addf4dbf 100644
--- a/core/src/main/resources/i18n/displayStrings_fa.properties
+++ b/core/src/main/resources/i18n/displayStrings_fa.properties
@@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal
# suppress inspection "UnusedProperty"
dao.param.UNDEFINED=تعریف نشده
# suppress inspection "UnusedProperty"
-dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ
+dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ
+dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC
+dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC
# suppress inspection "UnusedProperty"
-dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC
+dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC
# suppress inspection "UnusedProperty"
# suppress inspection "UnusedProperty"
@@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee
dao.param.BLIND_VOTE_FEE=Voting fee
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_PROPOSAL=Required quorum for proposal
+dao.param.QUORUM_GENERIC=Required quorum for proposal
# suppress inspection "UnusedProperty"
dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin
+dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal
+dao.param.THRESHOLD_GENERIC=Required threshold for proposal
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin
+dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation
@@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=درخواست خسارت
# suppress inspection "UnusedProperty"
dao.proposal.type.BONDED_ROLE=Proposal for a bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.REMOVE_ALTCOIN=پیشنهاد برای حذف یک آلت کوین
+dao.proposal.type.REMOVE_ASSET=پیشنهاد برای حذف یک آلت کوین
# suppress inspection "UnusedProperty"
dao.proposal.type.CHANGE_PARAM=پیشنهاد برای تغییر یک پارامتر
# suppress inspection "UnusedProperty"
@@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=درخواست خسارت
# suppress inspection "UnusedProperty"
dao.proposal.type.short.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin
+dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin
# suppress inspection "UnusedProperty"
dao.proposal.type.short.CHANGE_PARAM=Changing a parameter
# suppress inspection "UnusedProperty"
@@ -1872,6 +1872,7 @@ payment.emailOrMobile=ایمیل یا شماره موبایل:
payment.useCustomAccountName=استفاده از نام حساب سفارشی
payment.maxPeriod=حداکثر دوره ی زمانی مجاز معامله:
payment.maxPeriodAndLimit=حداکثر طول مدت معامله: {0} / حداکثر حد معامله: {1} / عمر حساب: {2}
+payment.maxPeriodAndLimitCrypto=حداکثر طول مدت معامله: {0} / حداکثر حد معامله: {1}
payment.currencyWithSymbol=ارز: {0}
payment.nameOfAcceptedBank=نام بانک پذیرفته شده
payment.addAcceptedBank=افزودن بانک پذیرفته شده
diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties
index beb2b1a9e8..e9133d1707 100644
--- a/core/src/main/resources/i18n/displayStrings_fr.properties
+++ b/core/src/main/resources/i18n/displayStrings_fr.properties
@@ -975,7 +975,7 @@ dao.phase.short.BREAK4=Break
dao.proposal.type.COMPENSATION_REQUEST=Requête de compensation
dao.proposal.type.GENERIC=Generic proposal
dao.proposal.type.CHANGE_PARAM=Proposal for changing a parameter
-dao.proposal.type.REMOVE_ALTCOIN=Proposal for removing an altcoin
+dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset
dao.proposal.details=Proposal details
dao.proposal.selectedProposal=Selected proposal
dao.proposal.active.header=Active proposals
@@ -1596,6 +1596,7 @@ payment.emailOrMobile=Email ou numéro de mobile:
payment.useCustomAccountName=Utiliser un nom de compte personnalisé
payment.maxPeriod=Max. allowed trade period:
payment.maxPeriodAndLimit=Max. trade duration: {0} / Max. trade limit: {1} / Account age: {2}
+payment.maxPeriodAndLimitCrypto=Max. trade duration: {0} / Max. trade limit: {1}
payment.currencyWithSymbol=Devise:{0}
payment.nameOfAcceptedBank=Nom de la banque acceptée
payment.addAcceptedBank=Ajouter une banque acceptée
diff --git a/core/src/main/resources/i18n/displayStrings_hu.properties b/core/src/main/resources/i18n/displayStrings_hu.properties
index 48dc91beca..c1fea77564 100644
--- a/core/src/main/resources/i18n/displayStrings_hu.properties
+++ b/core/src/main/resources/i18n/displayStrings_hu.properties
@@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal
# suppress inspection "UnusedProperty"
dao.param.UNDEFINED=Határozatlan
# suppress inspection "UnusedProperty"
-dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ
+dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ
+dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC
+dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC
# suppress inspection "UnusedProperty"
-dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC
+dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC
# suppress inspection "UnusedProperty"
# suppress inspection "UnusedProperty"
@@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee
dao.param.BLIND_VOTE_FEE=Voting fee
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_PROPOSAL=Required quorum for proposal
+dao.param.QUORUM_GENERIC=Required quorum for proposal
# suppress inspection "UnusedProperty"
dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin
+dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal
+dao.param.THRESHOLD_GENERIC=Required threshold for proposal
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin
+dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation
@@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Kártérítési kérelem
# suppress inspection "UnusedProperty"
dao.proposal.type.BONDED_ROLE=Proposal for a bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.REMOVE_ALTCOIN=Proposal for removing an altcoin
+dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset
# suppress inspection "UnusedProperty"
dao.proposal.type.CHANGE_PARAM=Proposal for changing a parameter
# suppress inspection "UnusedProperty"
@@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Kártérítési kérelem
# suppress inspection "UnusedProperty"
dao.proposal.type.short.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin
+dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin
# suppress inspection "UnusedProperty"
dao.proposal.type.short.CHANGE_PARAM=Changing a parameter
# suppress inspection "UnusedProperty"
@@ -1872,6 +1872,7 @@ payment.emailOrMobile=E-mail vagy mobil:
payment.useCustomAccountName=Használj egyéni fióknevet
payment.maxPeriod=Max. megengedett tranzakció időszak:
payment.maxPeriodAndLimit=Max. tranzakció időtartama: {0} / Max. tranzakció korlátozás: {1} / Fiók kora: {2}
+payment.maxPeriodAndLimitCrypto=Max. tranzakció időtartama: {0} / Max. tranzakció korlátozás: {1}
payment.currencyWithSymbol=Valuta: {0}
payment.nameOfAcceptedBank=Elfogadott bank neve
payment.addAcceptedBank=Hozzáad elfogadott bankot
diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties
index 44b92ffeeb..661d5f6eb9 100644
--- a/core/src/main/resources/i18n/displayStrings_pt.properties
+++ b/core/src/main/resources/i18n/displayStrings_pt.properties
@@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal
# suppress inspection "UnusedProperty"
dao.param.UNDEFINED=Indefinido
# suppress inspection "UnusedProperty"
-dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ
+dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ
+dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC
+dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC
# suppress inspection "UnusedProperty"
-dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC
+dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC
# suppress inspection "UnusedProperty"
# suppress inspection "UnusedProperty"
@@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee
dao.param.BLIND_VOTE_FEE=Voting fee
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_PROPOSAL=Required quorum for proposal
+dao.param.QUORUM_GENERIC=Required quorum for proposal
# suppress inspection "UnusedProperty"
dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin
+dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal
+dao.param.THRESHOLD_GENERIC=Required threshold for proposal
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin
+dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation
@@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Pedido de compensação
# suppress inspection "UnusedProperty"
dao.proposal.type.BONDED_ROLE=Proposal for a bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.REMOVE_ALTCOIN=Proposal for removing an altcoin
+dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset
# suppress inspection "UnusedProperty"
dao.proposal.type.CHANGE_PARAM=Proposal for changing a parameter
# suppress inspection "UnusedProperty"
@@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Pedido de compensação
# suppress inspection "UnusedProperty"
dao.proposal.type.short.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin
+dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin
# suppress inspection "UnusedProperty"
dao.proposal.type.short.CHANGE_PARAM=Changing a parameter
# suppress inspection "UnusedProperty"
@@ -1872,6 +1872,7 @@ payment.emailOrMobile=Email ou celular:
payment.useCustomAccountName=Usar número de conta personalizado:
payment.maxPeriod=Max. allowed trade period:
payment.maxPeriodAndLimit=Max. trade duration: {0} / Max. trade limit: {1} / Account age: {2}
+payment.maxPeriodAndLimitCrypto=Max. trade duration: {0} / Max. trade limit: {1}
payment.currencyWithSymbol=Moeda: {0}
payment.nameOfAcceptedBank=Nome do banco aceito
payment.addAcceptedBank=Adicionar banco aceito
diff --git a/core/src/main/resources/i18n/displayStrings_ro.properties b/core/src/main/resources/i18n/displayStrings_ro.properties
index 8743f33cf2..9fb902dbd7 100644
--- a/core/src/main/resources/i18n/displayStrings_ro.properties
+++ b/core/src/main/resources/i18n/displayStrings_ro.properties
@@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal
# suppress inspection "UnusedProperty"
dao.param.UNDEFINED=Nedefinit
# suppress inspection "UnusedProperty"
-dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ
+dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ
+dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC
+dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC
# suppress inspection "UnusedProperty"
-dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC
+dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC
# suppress inspection "UnusedProperty"
# suppress inspection "UnusedProperty"
@@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee
dao.param.BLIND_VOTE_FEE=Voting fee
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_PROPOSAL=Required quorum for proposal
+dao.param.QUORUM_GENERIC=Required quorum for proposal
# suppress inspection "UnusedProperty"
dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin
+dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal
+dao.param.THRESHOLD_GENERIC=Required threshold for proposal
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin
+dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation
@@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Solicitare de despăgubire
# suppress inspection "UnusedProperty"
dao.proposal.type.BONDED_ROLE=Proposal for a bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.REMOVE_ALTCOIN=Proposal for removing an altcoin
+dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset
# suppress inspection "UnusedProperty"
dao.proposal.type.CHANGE_PARAM=Proposal for changing a parameter
# suppress inspection "UnusedProperty"
@@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Solicitare de despăgubire
# suppress inspection "UnusedProperty"
dao.proposal.type.short.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin
+dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin
# suppress inspection "UnusedProperty"
dao.proposal.type.short.CHANGE_PARAM=Changing a parameter
# suppress inspection "UnusedProperty"
@@ -1872,6 +1872,7 @@ payment.emailOrMobile=E-mail sau nr. mobil:
payment.useCustomAccountName=Folosește nume de cont preferințial
payment.maxPeriod=Perioada maximă de tranzacționare permisă:
payment.maxPeriodAndLimit=Durata maximă de tranzacționare: {0} / Limita maximă de tranzacționare: {1} / Vechimea contului: {2}
+payment.maxPeriodAndLimitCrypto=Durata maximă de tranzacționare: {0} / Limita maximă de tranzacționare: {1}
payment.currencyWithSymbol=Valuta: {0}
payment.nameOfAcceptedBank=Numele băncii acceptate
payment.addAcceptedBank=Adaugă bancă acceptată
diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties
index 332b89ecd1..39d3b14dfc 100644
--- a/core/src/main/resources/i18n/displayStrings_ru.properties
+++ b/core/src/main/resources/i18n/displayStrings_ru.properties
@@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Результаты голосова
# suppress inspection "UnusedProperty"
dao.param.UNDEFINED=Неопределено
# suppress inspection "UnusedProperty"
-dao.param.BSQ_MAKER_FEE_IN_PERCENT=Взнос BSQ создателя
+dao.param.DEFAULT_MAKER_FEE_BSQ=Взнос BSQ создателя
# suppress inspection "UnusedProperty"
-dao.param.BSQ_TAKER_FEE_IN_PERCENT=Взнос BSQ получателя
+dao.param.DEFAULT_TAKER_FEE_BSQ=Взнос BSQ получателя
# suppress inspection "UnusedProperty"
-dao.param.BTC_MAKER_FEE_IN_PERCENT=Взнос BТС создателя
+dao.param.DEFAULT_MAKER_FEE_BTC=Взнос BТС создателя
# suppress inspection "UnusedProperty"
-dao.param.BTC_TAKER_FEE_IN_PERCENT=Взнос BТС получателя
+dao.param.DEFAULT_TAKER_FEE_BTC=Взнос BТС получателя
# suppress inspection "UnusedProperty"
# suppress inspection "UnusedProperty"
@@ -1065,7 +1065,7 @@ dao.param.PROPOSAL_FEE=Сбор за предложение
dao.param.BLIND_VOTE_FEE=Сбор за голосование
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_PROPOSAL=Необходимый кворум для предложения
+dao.param.QUORUM_GENERIC=Необходимый кворум для предложения
# suppress inspection "UnusedProperty"
dao.param.QUORUM_COMP_REQUEST=Необходимый кворум для запроса компенсации
# suppress inspection "UnusedProperty"
@@ -1076,7 +1076,7 @@ dao.param.QUORUM_REMOVE_ASSET=Необходимый кворум для уда
dao.param.QUORUM_CONFISCATION=Необходимый кворум для конфискации гарантийного депозита
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_PROPOSAL=Необходимый порог для предложения
+dao.param.THRESHOLD_GENERIC=Необходимый порог для предложения
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_COMP_REQUEST=Необходимый порог для запроса компенсации
# suppress inspection "UnusedProperty"
@@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Запрос компенсации
# suppress inspection "UnusedProperty"
dao.proposal.type.BONDED_ROLE=Предложение учредить обеспеченную роль
# suppress inspection "UnusedProperty"
-dao.proposal.type.REMOVE_ALTCOIN=Предложение удалить алткойн
+dao.proposal.type.REMOVE_ASSET=Предложение удалить алткойн
# suppress inspection "UnusedProperty"
dao.proposal.type.CHANGE_PARAM=Предложение по изменению параметра
# suppress inspection "UnusedProperty"
@@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Запрос компенсации
# suppress inspection "UnusedProperty"
dao.proposal.type.short.BONDED_ROLE=Обеспеченная роль
# suppress inspection "UnusedProperty"
-dao.proposal.type.short.REMOVE_ALTCOIN=Удаление алткойна
+dao.proposal.type.short.REMOVE_ASSET=Удаление алткойна
# suppress inspection "UnusedProperty"
dao.proposal.type.short.CHANGE_PARAM=Изменение параметра
# suppress inspection "UnusedProperty"
@@ -1872,6 +1872,7 @@ payment.emailOrMobile=Э-почта или мобильный номер:
payment.useCustomAccountName=Использовать своё название счёта
payment.maxPeriod=Макс. допустимый срок сделки:
payment.maxPeriodAndLimit=Макс. продолжительность сделки: {0} / Макс. торговый предел: {1} / Срок существования счёта: {2}
+payment.maxPeriodAndLimitCrypto=Макс. продолжительность сделки: {0} / Макс. торговый предел: {1}
payment.currencyWithSymbol=Валюта: {0}
payment.nameOfAcceptedBank=Название приемлемого банка
payment.addAcceptedBank=Добавить приемлемый банк
diff --git a/core/src/main/resources/i18n/displayStrings_sr.properties b/core/src/main/resources/i18n/displayStrings_sr.properties
index f1fbd51c90..7ad7712fe7 100644
--- a/core/src/main/resources/i18n/displayStrings_sr.properties
+++ b/core/src/main/resources/i18n/displayStrings_sr.properties
@@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal
# suppress inspection "UnusedProperty"
dao.param.UNDEFINED=Undefined
# suppress inspection "UnusedProperty"
-dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ
+dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ
+dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC
+dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC
# suppress inspection "UnusedProperty"
-dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC
+dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC
# suppress inspection "UnusedProperty"
# suppress inspection "UnusedProperty"
@@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee
dao.param.BLIND_VOTE_FEE=Voting fee
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_PROPOSAL=Required quorum for proposal
+dao.param.QUORUM_GENERIC=Required quorum for proposal
# suppress inspection "UnusedProperty"
dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin
+dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal
+dao.param.THRESHOLD_GENERIC=Required threshold for proposal
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin
+dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation
@@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Zahtev za nadoknadu
# suppress inspection "UnusedProperty"
dao.proposal.type.BONDED_ROLE=Proposal for a bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.REMOVE_ALTCOIN=Proposal for removing an altcoin
+dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset
# suppress inspection "UnusedProperty"
dao.proposal.type.CHANGE_PARAM=Proposal for changing a parameter
# suppress inspection "UnusedProperty"
@@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Zahtev za nadoknadu
# suppress inspection "UnusedProperty"
dao.proposal.type.short.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin
+dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin
# suppress inspection "UnusedProperty"
dao.proposal.type.short.CHANGE_PARAM=Changing a parameter
# suppress inspection "UnusedProperty"
@@ -1872,6 +1872,7 @@ payment.emailOrMobile=Email ili br. mobilnog:
payment.useCustomAccountName=Koristi prilagođeno ime računa
payment.maxPeriod=Maks. dozvoljeni period trgovine:
payment.maxPeriodAndLimit=Maks. trajanje trgovine: {0} / Maks. rok trgovine: {1}
+payment.maxPeriodAndLimitCrypto=Maks. trajanje trgovine: {0} / Maks. rok trgovine: {1}
payment.currencyWithSymbol=Valuta: {0}
payment.nameOfAcceptedBank=Ime prihvaćene banke
payment.addAcceptedBank=Dodaj prihvaćenu banke
diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties
index 34f3ffbb6f..02dd86bc1b 100644
--- a/core/src/main/resources/i18n/displayStrings_th.properties
+++ b/core/src/main/resources/i18n/displayStrings_th.properties
@@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal
# suppress inspection "UnusedProperty"
dao.param.UNDEFINED=ไม่ได้กำหนด
# suppress inspection "UnusedProperty"
-dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ
+dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ
+dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC
+dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC
# suppress inspection "UnusedProperty"
-dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC
+dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC
# suppress inspection "UnusedProperty"
# suppress inspection "UnusedProperty"
@@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee
dao.param.BLIND_VOTE_FEE=Voting fee
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_PROPOSAL=Required quorum for proposal
+dao.param.QUORUM_GENERIC=Required quorum for proposal
# suppress inspection "UnusedProperty"
dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin
+dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal
+dao.param.THRESHOLD_GENERIC=Required threshold for proposal
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin
+dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation
@@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=คำขอชดเชย
# suppress inspection "UnusedProperty"
dao.proposal.type.BONDED_ROLE=Proposal for a bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.REMOVE_ALTCOIN=ข้อเสนอสำหรับการลบ altcoin
+dao.proposal.type.REMOVE_ASSET=ข้อเสนอสำหรับการลบ altcoin
# suppress inspection "UnusedProperty"
dao.proposal.type.CHANGE_PARAM=ข้อเสนอสำหรับการเปลี่ยนข้อจำกัด
# suppress inspection "UnusedProperty"
@@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=คำขอชดเชย
# suppress inspection "UnusedProperty"
dao.proposal.type.short.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin
+dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin
# suppress inspection "UnusedProperty"
dao.proposal.type.short.CHANGE_PARAM=Changing a parameter
# suppress inspection "UnusedProperty"
@@ -1872,6 +1872,7 @@ payment.emailOrMobile=หมายเลขโทรศัพท์มือถ
payment.useCustomAccountName=ใช้ชื่อบัญชีที่กำหนดเอง
payment.maxPeriod=ระยะเวลาสูงสุดการค้าที่อนุญาต:
payment.maxPeriodAndLimit=ระยะเวลาสูงสุดทางการค้า: {0} / ขีดจำกัดสูงสุดทางการค้า: {1} / อายุบัญชี: {2}
+payment.maxPeriodAndLimitCrypto=ระยะเวลาสูงสุดทางการค้า: {0} / ขีดจำกัดสูงสุดทางการค้า: {1}
payment.currencyWithSymbol=สกุลเงิน: {0}
payment.nameOfAcceptedBank=ชื่อธนาคารที่ได้รับการยอมรับ
payment.addAcceptedBank=เพิ่มธนาคารที่ยอมรับ
diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties
index 06bfcc9a46..8d2b88e2b1 100644
--- a/core/src/main/resources/i18n/displayStrings_vi.properties
+++ b/core/src/main/resources/i18n/displayStrings_vi.properties
@@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal
# suppress inspection "UnusedProperty"
dao.param.UNDEFINED=Không xác định
# suppress inspection "UnusedProperty"
-dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ
+dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ
+dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC
+dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC
# suppress inspection "UnusedProperty"
-dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC
+dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC
# suppress inspection "UnusedProperty"
# suppress inspection "UnusedProperty"
@@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee
dao.param.BLIND_VOTE_FEE=Voting fee
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_PROPOSAL=Required quorum for proposal
+dao.param.QUORUM_GENERIC=Required quorum for proposal
# suppress inspection "UnusedProperty"
dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin
+dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal
+dao.param.THRESHOLD_GENERIC=Required threshold for proposal
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin
+dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation
@@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=Yêu cầu bồi thường
# suppress inspection "UnusedProperty"
dao.proposal.type.BONDED_ROLE=Proposal for a bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.REMOVE_ALTCOIN=Đề xuất gỡ bỏ một altcoin
+dao.proposal.type.REMOVE_ASSET=Đề xuất gỡ bỏ một altcoin
# suppress inspection "UnusedProperty"
dao.proposal.type.CHANGE_PARAM=Đề xuất thay đổi một thông số
# suppress inspection "UnusedProperty"
@@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=Yêu cầu bồi thường
# suppress inspection "UnusedProperty"
dao.proposal.type.short.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin
+dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin
# suppress inspection "UnusedProperty"
dao.proposal.type.short.CHANGE_PARAM=Changing a parameter
# suppress inspection "UnusedProperty"
@@ -1872,6 +1872,7 @@ payment.emailOrMobile=Email hoặc số điện thoại:
payment.useCustomAccountName=Sử dụng tên tài khoản thông dụng
payment.maxPeriod=Thời gian giao dịch cho phép tối đa:
payment.maxPeriodAndLimit=Thời gian giao dịch tối đa: {0} / Giới hạn giao dịch tối đa: {1} / Tuổi tài khoản: {2}
+payment.maxPeriodAndLimitCrypto=Thời gian giao dịch tối đa: {0} / Giới hạn giao dịch tối đa: {1}
payment.currencyWithSymbol=Tiền tệ: {0}
payment.nameOfAcceptedBank=Tên NH được chấp nhận
payment.addAcceptedBank=Thêm NH được chấp nhận
diff --git a/core/src/main/resources/i18n/displayStrings_zh.properties b/core/src/main/resources/i18n/displayStrings_zh.properties
index 2d3d7f917b..e005e37bb2 100644
--- a/core/src/main/resources/i18n/displayStrings_zh.properties
+++ b/core/src/main/resources/i18n/displayStrings_zh.properties
@@ -1050,13 +1050,13 @@ dao.results.proposals.voting.detail.header=Vote results for selected proposal
# suppress inspection "UnusedProperty"
dao.param.UNDEFINED=Undefined
# suppress inspection "UnusedProperty"
-dao.param.BSQ_MAKER_FEE_IN_PERCENT=Maker fee in BSQ
+dao.param.DEFAULT_MAKER_FEE_BSQ=Maker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BSQ_TAKER_FEE_IN_PERCENT=Taker fee in BSQ
+dao.param.DEFAULT_TAKER_FEE_BSQ=Taker fee in BSQ
# suppress inspection "UnusedProperty"
-dao.param.BTC_MAKER_FEE_IN_PERCENT=Maker fee in BTC
+dao.param.DEFAULT_MAKER_FEE_BTC=Maker fee in BTC
# suppress inspection "UnusedProperty"
-dao.param.BTC_TAKER_FEE_IN_PERCENT=Taker fee in BTC
+dao.param.DEFAULT_TAKER_FEE_BTC=Taker fee in BTC
# suppress inspection "UnusedProperty"
# suppress inspection "UnusedProperty"
@@ -1065,24 +1065,24 @@ dao.param.PROPOSAL_FEE=Proposal fee
dao.param.BLIND_VOTE_FEE=Voting fee
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_PROPOSAL=Required quorum for proposal
+dao.param.QUORUM_GENERIC=Required quorum for proposal
# suppress inspection "UnusedProperty"
dao.param.QUORUM_COMP_REQUEST=Required quorum for compensation request
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CHANGE_PARAM=Required quorum for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an altcoin
+dao.param.QUORUM_REMOVE_ASSET=Required quorum for removing an asset
# suppress inspection "UnusedProperty"
dao.param.QUORUM_CONFISCATION=Required quorum for bond confiscation
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_PROPOSAL=Required threshold for proposal
+dao.param.THRESHOLD_GENERIC=Required threshold for proposal
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_COMP_REQUEST=Required threshold for compensation request
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CHANGE_PARAM=Required threshold for changing a parameter
# suppress inspection "UnusedProperty"
-dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an altcoin
+dao.param.THRESHOLD_REMOVE_ASSET=Required threshold for removing an asset
# suppress inspection "UnusedProperty"
dao.param.THRESHOLD_CONFISCATION=Required threshold for bond confiscation
@@ -1211,7 +1211,7 @@ dao.proposal.type.COMPENSATION_REQUEST=赔偿要求
# suppress inspection "UnusedProperty"
dao.proposal.type.BONDED_ROLE=Proposal for a bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.REMOVE_ALTCOIN=Proposal for removing an altcoin
+dao.proposal.type.REMOVE_ASSET=Proposal for removing an asset
# suppress inspection "UnusedProperty"
dao.proposal.type.CHANGE_PARAM=修改参数的提议
# suppress inspection "UnusedProperty"
@@ -1224,7 +1224,7 @@ dao.proposal.type.short.COMPENSATION_REQUEST=赔偿要求
# suppress inspection "UnusedProperty"
dao.proposal.type.short.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
-dao.proposal.type.short.REMOVE_ALTCOIN=Removing an altcoin
+dao.proposal.type.short.REMOVE_ASSET=Removing an altcoin
# suppress inspection "UnusedProperty"
dao.proposal.type.short.CHANGE_PARAM=Changing a parameter
# suppress inspection "UnusedProperty"
@@ -1872,6 +1872,7 @@ payment.emailOrMobile=电子邮箱或手机号码:
payment.useCustomAccountName=使用自定义名称
payment.maxPeriod=最大允许时限:
payment.maxPeriodAndLimit=最大交易期限:{0} /最大交易限额:{1}
+payment.maxPeriodAndLimitCrypto=最大交易期限:{0} /最大交易限额:{1}
payment.currencyWithSymbol=货币:{0}
payment.nameOfAcceptedBank=接受的银行名称
payment.addAcceptedBank=添加接受的银行
diff --git a/core/src/test/java/bisq/core/btc/wallet/WalletNetworkConfigTest.java b/core/src/test/java/bisq/core/btc/nodes/BtcNetworkConfigTest.java
similarity index 84%
rename from core/src/test/java/bisq/core/btc/wallet/WalletNetworkConfigTest.java
rename to core/src/test/java/bisq/core/btc/nodes/BtcNetworkConfigTest.java
index d1fc971e1e..04825ff7d4 100644
--- a/core/src/test/java/bisq/core/btc/wallet/WalletNetworkConfigTest.java
+++ b/core/src/test/java/bisq/core/btc/nodes/BtcNetworkConfigTest.java
@@ -15,7 +15,9 @@
* along with Bisq. If not, see .
*/
-package bisq.core.btc.wallet;
+package bisq.core.btc.nodes;
+
+import bisq.core.btc.setup.WalletConfig;
import bisq.network.Socks5MultiDiscovery;
@@ -34,7 +36,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-public class WalletNetworkConfigTest {
+public class BtcNetworkConfigTest {
private static final int MODE = 0;
private WalletConfig delegate;
@@ -46,7 +48,7 @@ public class WalletNetworkConfigTest {
@Test
public void testProposePeersWhenProxyPresentAndNoPeers() {
- WalletNetworkConfig config = new WalletNetworkConfig(delegate, mock(NetworkParameters.class), MODE,
+ BtcNetworkConfig config = new BtcNetworkConfig(delegate, mock(NetworkParameters.class), MODE,
mock(Socks5Proxy.class));
config.proposePeers(Collections.emptyList());
@@ -56,7 +58,7 @@ public class WalletNetworkConfigTest {
@Test
public void testProposePeersWhenProxyNotPresentAndNoPeers() {
- WalletNetworkConfig config = new WalletNetworkConfig(delegate, mock(NetworkParameters.class), MODE,
+ BtcNetworkConfig config = new BtcNetworkConfig(delegate, mock(NetworkParameters.class), MODE,
null);
config.proposePeers(Collections.emptyList());
@@ -66,7 +68,7 @@ public class WalletNetworkConfigTest {
@Test
public void testProposePeersWhenPeersPresent() {
- WalletNetworkConfig config = new WalletNetworkConfig(delegate, mock(NetworkParameters.class), MODE,
+ BtcNetworkConfig config = new BtcNetworkConfig(delegate, mock(NetworkParameters.class), MODE,
null);
config.proposePeers(Collections.singletonList(mock(PeerAddress.class)));
diff --git a/core/src/test/java/bisq/core/btc/wallet/BtcNodeConverterTest.java b/core/src/test/java/bisq/core/btc/nodes/BtcNodeConverterTest.java
similarity index 96%
rename from core/src/test/java/bisq/core/btc/wallet/BtcNodeConverterTest.java
rename to core/src/test/java/bisq/core/btc/nodes/BtcNodeConverterTest.java
index 1381ce9b5f..bc584d3e4b 100644
--- a/core/src/test/java/bisq/core/btc/wallet/BtcNodeConverterTest.java
+++ b/core/src/test/java/bisq/core/btc/nodes/BtcNodeConverterTest.java
@@ -15,10 +15,10 @@
* along with Bisq. If not, see .
*/
-package bisq.core.btc.wallet;
+package bisq.core.btc.nodes;
-import bisq.core.btc.BitcoinNodes.BtcNode;
-import bisq.core.btc.wallet.BtcNodeConverter.Facade;
+import bisq.core.btc.nodes.BtcNodeConverter.Facade;
+import bisq.core.btc.nodes.BtcNodes.BtcNode;
import bisq.network.DnsLookupException;
diff --git a/core/src/test/java/bisq/core/btc/wallet/PeerAddressesRepositoryTest.java b/core/src/test/java/bisq/core/btc/nodes/BtcNodesRepositoryTest.java
similarity index 88%
rename from core/src/test/java/bisq/core/btc/wallet/PeerAddressesRepositoryTest.java
rename to core/src/test/java/bisq/core/btc/nodes/BtcNodesRepositoryTest.java
index 353d0a569a..bead4cceb8 100644
--- a/core/src/test/java/bisq/core/btc/wallet/PeerAddressesRepositoryTest.java
+++ b/core/src/test/java/bisq/core/btc/nodes/BtcNodesRepositoryTest.java
@@ -15,9 +15,9 @@
* along with Bisq. If not, see .
*/
-package bisq.core.btc.wallet;
+package bisq.core.btc.nodes;
-import bisq.core.btc.BitcoinNodes.BtcNode;
+import bisq.core.btc.nodes.BtcNodes.BtcNode;
import org.bitcoinj.core.PeerAddress;
@@ -39,14 +39,14 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-public class PeerAddressesRepositoryTest {
+public class BtcNodesRepositoryTest {
@Test
public void testGetPeerAddressesWhenClearNodes() {
BtcNode node = mock(BtcNode.class);
when(node.hasClearNetAddress()).thenReturn(true);
BtcNodeConverter converter = mock(BtcNodeConverter.class, RETURNS_DEEP_STUBS);
- PeerAddressesRepository repository = new PeerAddressesRepository(converter,
+ BtcNodesRepository repository = new BtcNodesRepository(converter,
Collections.singletonList(node));
List peers = repository.getPeerAddresses(null, false);
@@ -62,7 +62,7 @@ public class PeerAddressesRepositoryTest {
BtcNode node = mock(BtcNode.class);
when(node.hasClearNetAddress()).thenReturn(true);
- PeerAddressesRepository repository = new PeerAddressesRepository(converter,
+ BtcNodesRepository repository = new BtcNodesRepository(converter,
Collections.singletonList(node));
List peers = repository.getPeerAddresses(null, false);
@@ -80,7 +80,7 @@ public class PeerAddressesRepositoryTest {
when(node.hasOnionAddress()).thenReturn(true);
BtcNodeConverter converter = mock(BtcNodeConverter.class, RETURNS_DEEP_STUBS);
- PeerAddressesRepository repository = new PeerAddressesRepository(converter,
+ BtcNodesRepository repository = new BtcNodesRepository(converter,
Lists.newArrayList(node, onionNode));
List peers = repository.getPeerAddresses(mock(Socks5Proxy.class), true);
@@ -97,7 +97,7 @@ public class PeerAddressesRepositoryTest {
when(node.hasOnionAddress()).thenReturn(true);
BtcNodeConverter converter = mock(BtcNodeConverter.class, RETURNS_DEEP_STUBS);
- PeerAddressesRepository repository = new PeerAddressesRepository(converter,
+ BtcNodesRepository repository = new BtcNodesRepository(converter,
Lists.newArrayList(node, onionNode));
List peers = repository.getPeerAddresses(mock(Socks5Proxy.class), false);
diff --git a/core/src/test/java/bisq/core/btc/wallet/WalletSetupPreferencesTest.java b/core/src/test/java/bisq/core/btc/nodes/BtcNodesSetupPreferencesTest.java
similarity index 80%
rename from core/src/test/java/bisq/core/btc/wallet/WalletSetupPreferencesTest.java
rename to core/src/test/java/bisq/core/btc/nodes/BtcNodesSetupPreferencesTest.java
index 32cd0c2db9..20077fc01f 100644
--- a/core/src/test/java/bisq/core/btc/wallet/WalletSetupPreferencesTest.java
+++ b/core/src/test/java/bisq/core/btc/nodes/BtcNodesSetupPreferencesTest.java
@@ -15,10 +15,9 @@
* along with Bisq. If not, see .
*/
-package bisq.core.btc.wallet;
+package bisq.core.btc.nodes;
-import bisq.core.btc.BitcoinNodes;
-import bisq.core.btc.BitcoinNodes.BtcNode;
+import bisq.core.btc.nodes.BtcNodes.BtcNode;
import bisq.core.user.Preferences;
import java.util.List;
@@ -30,8 +29,8 @@ import org.powermock.modules.junit4.PowerMockRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
-import static bisq.core.btc.BitcoinNodes.BitcoinNodesOption.CUSTOM;
-import static bisq.core.btc.BitcoinNodes.BitcoinNodesOption.PUBLIC;
+import static bisq.core.btc.nodes.BtcNodes.BitcoinNodesOption.CUSTOM;
+import static bisq.core.btc.nodes.BtcNodes.BitcoinNodesOption.PUBLIC;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@@ -40,14 +39,14 @@ import static org.mockito.Mockito.when;
@RunWith(PowerMockRunner.class)
@PrepareForTest(Preferences.class)
@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*"})
-public class WalletSetupPreferencesTest {
+public class BtcNodesSetupPreferencesTest {
@Test
public void testSelectPreferredNodesWhenPublicOption() {
Preferences delegate = mock(Preferences.class);
when(delegate.getBitcoinNodesOptionOrdinal()).thenReturn(PUBLIC.ordinal());
- WalletSetupPreferences preferences = new WalletSetupPreferences(delegate);
- List nodes = preferences.selectPreferredNodes(mock(BitcoinNodes.class));
+ BtcNodesSetupPreferences preferences = new BtcNodesSetupPreferences(delegate);
+ List nodes = preferences.selectPreferredNodes(mock(BtcNodes.class));
assertTrue(nodes.isEmpty());
}
@@ -58,8 +57,8 @@ public class WalletSetupPreferencesTest {
when(delegate.getBitcoinNodesOptionOrdinal()).thenReturn(CUSTOM.ordinal());
when(delegate.getBitcoinNodes()).thenReturn("aaa.onion,bbb.onion");
- WalletSetupPreferences preferences = new WalletSetupPreferences(delegate);
- List nodes = preferences.selectPreferredNodes(mock(BitcoinNodes.class));
+ BtcNodesSetupPreferences preferences = new BtcNodesSetupPreferences(delegate);
+ List nodes = preferences.selectPreferredNodes(mock(BtcNodes.class));
assertEquals(2, nodes.size());
}
diff --git a/core/src/test/java/bisq/core/btc/RestrictionsTest.java b/core/src/test/java/bisq/core/btc/wallet/RestrictionsTest.java
similarity index 98%
rename from core/src/test/java/bisq/core/btc/RestrictionsTest.java
rename to core/src/test/java/bisq/core/btc/wallet/RestrictionsTest.java
index 5a173082a3..f66b2d4c38 100644
--- a/core/src/test/java/bisq/core/btc/RestrictionsTest.java
+++ b/core/src/test/java/bisq/core/btc/wallet/RestrictionsTest.java
@@ -15,7 +15,7 @@
* along with Bisq. If not, see .
*/
-package bisq.core.btc;
+package bisq.core.btc.wallet;
import org.bitcoinj.core.Coin;
diff --git a/core/src/test/java/bisq/core/dao/node/parser/TxParserTest.java b/core/src/test/java/bisq/core/dao/node/parser/GenesisTxParserTest.java
similarity index 81%
rename from core/src/test/java/bisq/core/dao/node/parser/TxParserTest.java
rename to core/src/test/java/bisq/core/dao/node/parser/GenesisTxParserTest.java
index 105a6ddb4d..1d6a99e52f 100644
--- a/core/src/test/java/bisq/core/dao/node/parser/TxParserTest.java
+++ b/core/src/test/java/bisq/core/dao/node/parser/GenesisTxParserTest.java
@@ -32,15 +32,14 @@ import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
-import java.util.Optional;
import org.junit.Assert;
import org.junit.Test;
-public class TxParserTest {
+public class GenesisTxParserTest {
@Test
- public void testGetGenesisTx() {
+ public void testIsGenesis() {
// fixme(chirhonul): Assert.assertEquals(2, 3);
int blockHeight = 200;
@@ -73,8 +72,8 @@ public class TxParserTest {
int genesisBlockHeight = 150;
// With mismatch in block height and tx id, we should not get genesis tx back.
- Optional result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx);
- Optional want = Optional.empty();
+ boolean result = GenesisTxParser.isGenesis(rawTx, genesisTxId, genesisBlockHeight);
+ boolean want = false;
Assert.assertEquals(want, result);
// With correct block height but mismatch in tx id, we should still not get genesis tx back.
@@ -87,12 +86,35 @@ public class TxParserTest {
ImmutableList.copyOf(inputs),
ImmutableList.copyOf(Arrays.asList(output))
);
- result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx);
- want = Optional.empty();
+ result = GenesisTxParser.isGenesis(rawTx, genesisTxId, genesisBlockHeight);
+ want = false;
Assert.assertEquals(want, result);
+ }
+
+ @Test
+ public void testGetGenesisTempTx() {
+ int blockHeight = 200;
+ String blockHash = "abc123";
+ Coin genesisTotalSupply = Coin.parseCoin("2.5");
+ long time = new Date().getTime();
+ final List inputs = Arrays.asList(
+ new TxInput("tx0", 0, null),
+ new TxInput("tx1", 1, null)
+ );
+ RawTxOutput output = new RawTxOutput(
+ 0,
+ genesisTotalSupply.value,
+ null,
+ null,
+ null,
+ null,
+ blockHeight
+ );
+
+ String genesisTxId = "genesisTxId";
// With correct tx id and block height, we should find our genesis tx with correct tx and output type.
- rawTx = new RawTx(
+ RawTx rawTx = new RawTx(
genesisTxId,
blockHeight,
blockHash,
@@ -100,16 +122,16 @@ public class TxParserTest {
ImmutableList.copyOf(inputs),
ImmutableList.copyOf(Arrays.asList(output))
);
- result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx);
+ TempTx resultTempTx = GenesisTxParser.getGenesisTempTx(rawTx, genesisTotalSupply);
TempTx tempTx = TempTx.fromRawTx(rawTx);
tempTx.setTxType(TxType.GENESIS);
for (int i = 0; i < tempTx.getTempTxOutputs().size(); ++i) {
tempTx.getTempTxOutputs().get(i).setTxOutputType(TxOutputType.GENESIS_OUTPUT);
}
- want = Optional.of(tempTx);
+ TempTx wantTempTx = tempTx;
- Assert.assertEquals(want, result);
+ Assert.assertEquals(wantTempTx, resultTempTx);
// With correct tx id and block height, but too low sum of outputs (lower than genesisTotalSupply), we
// should see an exception raised.
@@ -131,7 +153,7 @@ public class TxParserTest {
ImmutableList.copyOf(Arrays.asList(output))
);
try {
- result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx);
+ GenesisTxParser.getGenesisTempTx(rawTx, genesisTotalSupply);
Assert.fail("Expected an InvalidGenesisTxException to be thrown when outputs are too low");
} catch (InvalidGenesisTxException igtxe) {
String wantMessage = "Genesis tx is invalid; not using all available inputs. Remaining input value is 1 sat";
@@ -168,7 +190,7 @@ public class TxParserTest {
ImmutableList.copyOf(Arrays.asList(output1, output2))
);
try {
- result = TxParser.findGenesisTx(genesisTxId, genesisBlockHeight, genesisTotalSupply, rawTx);
+ GenesisTxParser.getGenesisTempTx(rawTx, genesisTotalSupply);
Assert.fail("Expected an InvalidGenesisTxException to be thrown when outputs are too high");
} catch (InvalidGenesisTxException igtxe) {
String wantMessage = "Genesis tx is invalid; using more than available inputs. Remaining input value is 2 sat";
diff --git a/core/src/test/java/bisq/core/dao/state/SnapshotManagerTest.java b/core/src/test/java/bisq/core/dao/state/SnapshotManagerTest.java
index a1ab7f3ccf..babf312fd6 100644
--- a/core/src/test/java/bisq/core/dao/state/SnapshotManagerTest.java
+++ b/core/src/test/java/bisq/core/dao/state/SnapshotManagerTest.java
@@ -46,6 +46,7 @@ public class SnapshotManagerTest {
snapshotManager = new SnapshotManager(mock(BsqState.class),
mock(BsqStateService.class),
mock(PersistenceProtoResolver.class),
+ mock(GenesisTxInfo.class),
mock(File.class));
}
diff --git a/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java b/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java
index 8f6838fc5d..1f4e52708d 100644
--- a/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java
+++ b/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java
@@ -17,15 +17,30 @@
package bisq.core.locale;
+import bisq.core.btc.BaseCurrencyNetwork;
+
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.stream.Stream;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+
+
+import bisq.asset.Asset;
+import bisq.asset.AssetRegistry;
+import bisq.asset.Coin;
+import bisq.asset.coins.Ether;
+
public class CurrencyUtilTest {
@Before
@@ -39,9 +54,95 @@ public class CurrencyUtilTest {
Optional naira = CurrencyUtil.getTradeCurrency("NGN");
Optional fake = CurrencyUtil.getTradeCurrency("FAK");
-
assertTrue(euro.isPresent());
assertTrue(naira.isPresent());
assertFalse("Fake currency shouldn't exist", fake.isPresent());
}
+
+ @Test
+ public void testFindAsset() {
+ MockAssetRegistry assetRegistry = new MockAssetRegistry();
+
+ // test if code is matching
+ boolean daoTradingActivated = false;
+ // Test if BSQ on mainnet is failing
+ Assert.assertFalse(CurrencyUtil.findAsset(assetRegistry, "BSQ",
+ BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).isPresent());
+
+ // on testnet/regtest it is allowed
+ assertEquals(CurrencyUtil.findAsset(assetRegistry, "BSQ",
+ BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get().getTickerSymbol(), "BSQ");
+
+
+ daoTradingActivated = true;
+ // With daoTradingActivated we can request BSQ
+ assertEquals(CurrencyUtil.findAsset(assetRegistry, "BSQ",
+ BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).get().getTickerSymbol(), "BSQ");
+
+ // Test if not matching ticker is failing
+ Assert.assertFalse(CurrencyUtil.findAsset(assetRegistry, "BSQ1",
+ BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).isPresent());
+
+ // Add a mock coin which has no mainnet version, needs to fail if we are on mainnet
+ MockTestnetCoin.Testnet mockTestnetCoin = new MockTestnetCoin.Testnet();
+ try {
+ assetRegistry.addAsset(mockTestnetCoin);
+ CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN",
+ BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated);
+ Assert.fail("Expected an IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ String wantMessage = "We are on mainnet and we could not find an asset with network type mainnet";
+ Assert.assertTrue("Unexpected exception, want message starting with " +
+ "'" + wantMessage + "', got '" + e.getMessage() + "'", e.getMessage().startsWith(wantMessage));
+ }
+
+ // For testnet its ok
+ assertEquals(CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN",
+ BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get().getTickerSymbol(), "MOCK_COIN");
+ assertEquals(Coin.Network.TESTNET, mockTestnetCoin.getNetwork());
+
+ // For regtest its still found
+ assertEquals(CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN",
+ BaseCurrencyNetwork.BTC_REGTEST, daoTradingActivated).get().getTickerSymbol(), "MOCK_COIN");
+
+
+ // We test if we are not on mainnet to get the mainnet coin
+ Coin ether = new Ether();
+ assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH",
+ BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get().getTickerSymbol(), "ETH");
+ assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH",
+ BaseCurrencyNetwork.BTC_REGTEST, daoTradingActivated).get().getTickerSymbol(), "ETH");
+ assertEquals(Coin.Network.MAINNET, ether.getNetwork());
+
+ // We test if network matches exactly if there are distinct network types defined like with BSQ
+ Coin bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).get();
+ assertEquals("BSQ", bsq.getTickerSymbol());
+ assertEquals(Coin.Network.MAINNET, bsq.getNetwork());
+
+ bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get();
+ assertEquals("BSQ", bsq.getTickerSymbol());
+ assertEquals(Coin.Network.TESTNET, bsq.getNetwork());
+
+ bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.BTC_REGTEST, daoTradingActivated).get();
+ assertEquals("BSQ", bsq.getTickerSymbol());
+ assertEquals(Coin.Network.REGTEST, bsq.getNetwork());
+ }
+
+ class MockAssetRegistry extends AssetRegistry {
+ private List registeredAssets = new ArrayList<>();
+
+ MockAssetRegistry() {
+ for (Asset asset : ServiceLoader.load(Asset.class)) {
+ registeredAssets.add(asset);
+ }
+ }
+
+ void addAsset(Asset asset) {
+ registeredAssets.add(asset);
+ }
+
+ public Stream stream() {
+ return registeredAssets.stream();
+ }
+ }
}
diff --git a/core/src/test/java/bisq/core/locale/MockTestnetCoin.java b/core/src/test/java/bisq/core/locale/MockTestnetCoin.java
new file mode 100644
index 0000000000..ddffc0e8fa
--- /dev/null
+++ b/core/src/test/java/bisq/core/locale/MockTestnetCoin.java
@@ -0,0 +1,69 @@
+/*
+ * 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.locale;
+
+import org.bitcoinj.core.NetworkParameters;
+import org.bitcoinj.params.MainNetParams;
+import org.bitcoinj.params.RegTestParams;
+import org.bitcoinj.params.TestNet3Params;
+
+
+
+import bisq.asset.AddressValidationResult;
+import bisq.asset.Base58BitcoinAddressValidator;
+import bisq.asset.Coin;
+
+public class MockTestnetCoin extends Coin {
+
+ public MockTestnetCoin(Network network, NetworkParameters networkParameters) {
+ super("MockTestnetCoin", "MOCK_COIN", new BSQAddressValidator(networkParameters), network);
+ }
+
+ public static class Mainnet extends MockTestnetCoin {
+
+ public Mainnet() {
+ super(Network.MAINNET, MainNetParams.get());
+ }
+ }
+
+ public static class Testnet extends MockTestnetCoin {
+
+ public Testnet() {
+ super(Network.TESTNET, TestNet3Params.get());
+ }
+ }
+
+ public static class Regtest extends MockTestnetCoin {
+
+ public Regtest() {
+ super(Network.REGTEST, RegTestParams.get());
+ }
+ }
+
+ public static class BSQAddressValidator extends Base58BitcoinAddressValidator {
+
+ public BSQAddressValidator(NetworkParameters networkParameters) {
+ super(networkParameters);
+ }
+
+ @Override
+ public AddressValidationResult validate(String address) {
+ return super.validate(address);
+ }
+ }
+}
diff --git a/core/src/test/java/bisq/core/offer/OfferMaker.java b/core/src/test/java/bisq/core/offer/OfferMaker.java
index c32f9f0adb..aa9d294c2e 100644
--- a/core/src/test/java/bisq/core/offer/OfferMaker.java
+++ b/core/src/test/java/bisq/core/offer/OfferMaker.java
@@ -33,9 +33,10 @@ public class OfferMaker {
public static final Property direction = new Property<>();
public static final Property useMarketBasedPrice = new Property<>();
public static final Property marketPriceMargin = new Property<>();
+ public static final Property id = new Property<>();
public static final Instantiator Offer = lookup -> new Offer(
- new OfferPayload("",
+ new OfferPayload(lookup.valueOf(id, "1234"),
0L,
null,
null,
diff --git a/core/src/test/java/bisq/core/payment/validation/AltCoinAddressValidatorTest.java b/core/src/test/java/bisq/core/payment/validation/AltCoinAddressValidatorTest.java
index 095e9d9dd9..1ef48be4c3 100644
--- a/core/src/test/java/bisq/core/payment/validation/AltCoinAddressValidatorTest.java
+++ b/core/src/test/java/bisq/core/payment/validation/AltCoinAddressValidatorTest.java
@@ -22,14 +22,14 @@ import bisq.core.btc.BaseCurrencyNetwork;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
-import bisq.asset.AssetRegistry;
-
import org.junit.Test;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+
+
+
+import bisq.asset.AssetRegistry;
public class AltCoinAddressValidatorTest {
@@ -50,11 +50,7 @@ public class AltCoinAddressValidatorTest {
assertTrue(validator.validate("Lg3PX8wRWmApFCoCMAsPF5P9dPHYQHEWKW").isValid);
validator.setCurrencyCode("BOGUS");
- try {
- validator.validate("1BOGUSADDR");
- fail("expected validation to fail for unregistered asset 'BOGUS'");
- } catch (IllegalArgumentException ex) {
- assertThat(ex.getMessage(), containsString("'BOGUS' is not a registered asset"));
- }
+
+ assertFalse(validator.validate("1BOGUSADDR").isValid);
}
}
diff --git a/desktop/build.gradle b/desktop/build.gradle
index 6ce825246d..d189224faa 100644
--- a/desktop/build.gradle
+++ b/desktop/build.gradle
@@ -86,7 +86,7 @@ installDist.destinationDir = file('build/app')
// 1. Remove the block entirely
// 2. Replace the block with the following command:
//
-// ./gradlew -q calculateChecksums | grep -v network.bisq:bisq- >> build.gradle
+// ./gradlew -q calculateChecksums | grep -v network.bisq >> desktop/build.gradle
//
// 3. Run `git diff` to verify that expected hashes have changed
// 4. Commit the changes
@@ -101,6 +101,13 @@ dependencyVerification {
'de.jensd:fontawesomefx-commons:5539bb3335ecb822dbf928546f57766eeb9f1516cc1417a064b5709629612149',
'com.googlecode.jcsv:jcsv:73ca7d715e90c8d2c2635cc284543b038245a34f70790660ed590e157b8714a2',
'com.github.sarxos:webcam-capture:d960b7ea8ec3ddf2df0725ef214c3fccc9699ea7772df37f544e1f8e4fd665f6',
+ 'com.github.JesusMcCloud.netlayer:tor.native:de44e782b21838d3426dbff99abbfd1cbb8e5d3f6d5e997441ff4fd8354934fa',
+ 'org.apache.httpcomponents:httpclient:db3d1b6c2d6a5e5ad47577ad61854e2f0e0936199b8e05eb541ed52349263135',
+ 'net.sf.jopt-simple:jopt-simple:6f45c00908265947c39221035250024f2caec9a15c1c8cf553ebeecee289f342',
+ 'org.fxmisc.easybind:easybind:666af296dda6de68751668a62661571b5238ac6f1c07c8a204fc6f902b222aaf',
+ 'com.fasterxml.jackson.core:jackson-databind:fcf3c2b0c332f5f54604f7e27fa7ee502378a2cc5df6a944bbfae391872c32ff',
+ 'com.fasterxml.jackson.core:jackson-core:39a74610521d7fb9eb3f437bb8739bbf47f6435be12d17bf954c731a0c6352bb',
+ 'com.fasterxml.jackson.core:jackson-annotations:2566b3a6662afa3c6af4f5b25006cb46be2efc68f1b5116291d6998a8cdf7ed3',
'com.google.protobuf:protobuf-java:b5e2d91812d183c9f053ffeebcbcda034d4de6679521940a19064714966c2cd4',
'com.google.code.gson:gson:2d43eb5ea9e133d2ee2405cc14f5ee08951b8361302fdd93494a3a997b508d32',
'com.googlecode.json-simple:json-simple:4e69696892b88b41c55d49ab2fdcc21eead92bf54acc588c0050596c3b75199c',
@@ -111,8 +118,6 @@ dependencyVerification {
'com.google.code.findbugs:jsr305:c885ce34249682bc0236b4a7d56efcc12048e6135a5baf7a9cde8ad8cda13fcd',
'com.google.guava:guava:36a666e3b71ae7f0f0dca23654b67e086e6c93d192f60ba5dfd5519db6c288c8',
'com.google.inject:guice:9b9df27a5b8c7864112b4137fd92b36c3f1395bfe57be42fedf2f520ead1a93e',
- 'network.bisq.libdohj:libdohj-core:77949092ec50f4526257b2d8b84fff995714443fa6c0e37555335fd97fc35fa0',
- 'com.github.JesusMcCloud.netlayer:tor.native:de44e782b21838d3426dbff99abbfd1cbb8e5d3f6d5e997441ff4fd8354934fa',
'com.github.JesusMcCloud.netlayer:tor:3896950c56a41985f901ff9475524ac162cba18b2d5a0ed39810b20ddaf5128a',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:841b021d62fc007ce2883963ff9440d5393fb1f6a0604ed68cd016afcaf02967',
'com.github.MicroUtils:kotlin-logging:7dbd501cc210d721f730d480c53ee2a6e3c154ae89b07dc7dee224b9c5aca9eb',
@@ -123,21 +128,7 @@ dependencyVerification {
'commons-io:commons-io:cc6a41dc3eaacc9e440a6bd0d2890b20d36b4ee408fe2d67122f328bb6e01581',
'org.apache.commons:commons-lang3:734c8356420cc8e30c795d64fd1fcd5d44ea9d90342a2cc3262c5158fbc6d98b',
'org.bouncycastle:bcprov-jdk15on:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
- 'net.sf.jopt-simple:jopt-simple:6f45c00908265947c39221035250024f2caec9a15c1c8cf553ebeecee289f342',
- 'network.bisq.btcd-cli4j:btcd-cli4j-daemon:8a29f395d77080f276ec83db97bd786a48ec4cdff03fb4b578afa1c9d41040c0',
- 'network.bisq.btcd-cli4j:btcd-cli4j-core:0ee93923450dd600049d348ef2ea6c20c87f948d5a6ece3dba2b97f09b092436',
- 'com.fasterxml.jackson.core:jackson-databind:fcf3c2b0c332f5f54604f7e27fa7ee502378a2cc5df6a944bbfae391872c32ff',
- 'com.fasterxml.jackson.core:jackson-core:39a74610521d7fb9eb3f437bb8739bbf47f6435be12d17bf954c731a0c6352bb',
- 'com.fasterxml.jackson.core:jackson-annotations:2566b3a6662afa3c6af4f5b25006cb46be2efc68f1b5116291d6998a8cdf7ed3',
- 'org.apache.httpcomponents:httpclient:db3d1b6c2d6a5e5ad47577ad61854e2f0e0936199b8e05eb541ed52349263135',
- 'org.fxmisc.easybind:easybind:666af296dda6de68751668a62661571b5238ac6f1c07c8a204fc6f902b222aaf',
'com.google.zxing:javase:0ec23e2ec12664ddd6347c8920ad647bb3b9da290f897a88516014b56cc77eb9',
- 'commons-logging:commons-logging:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636',
- 'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
- 'aopalliance:aopalliance:0addec670fedcd3f113c5c8091d783280d23f75e3acb841b61a9cdb079376a08',
- 'com.github.bisq-network.bitcoinj:bitcoinj-core:e8a30946ee30c09d7e6cff6cb082c6c9117e30b8b940f73458f4ce4c4edb1585',
- 'com.lambdaworks:scrypt:9a82d218099fb14c10c0e86e7eefeebd8c104de920acdc47b8b4b7a686fb73b4',
- 'commons-codec:commons-codec:ad19d2601c3abf0b946b5c3a4113e226a8c1e3305e395b90013b78dd94a723ce',
'com.nativelibs4java:bridj:101bcd9b6637e6bc16e56deb3daefba62b1f5e8e9e37e1b3e56e3b5860d659cf',
'com.cedricwalter:tor-binary-macos:87790e9eade1e44eeadc81f92670f338cd47ef1b39b46a4b022c75d0cf6465fd',
'com.cedricwalter:tor-binary-linux32:814f6da3b662c96490bcb09781764dd31dfe497ea9c25c73fe61170d2a78086f',
@@ -145,16 +136,22 @@ dependencyVerification {
'com.cedricwalter:tor-binary-windows:9487a735dadcadc6ede5ffad36a911c2d4a484f996be93d71094f26591b8c29e',
'com.github.ravn:jsocks:3c71600af027b2b6d4244e4ad14d98ff2352a379410daebefff5d8cd48d742a4',
'org.apache.httpcomponents:httpcore:d7f853dee87680b07293d30855b39b9eb56c1297bd16ff1cd6f19ddb8fa745fb',
+ 'commons-codec:commons-codec:ad19d2601c3abf0b946b5c3a4113e226a8c1e3305e395b90013b78dd94a723ce',
+ 'commons-logging:commons-logging:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636',
+ 'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
+ 'aopalliance:aopalliance:0addec670fedcd3f113c5c8091d783280d23f75e3acb841b61a9cdb079376a08',
+ 'com.github.bisq-network.bitcoinj:bitcoinj-core:15e0f4304dd92259c4e9ff0114cbeab7a79abb51a5817b422ce629d3a0a2d551',
+ 'com.lambdaworks:scrypt:9a82d218099fb14c10c0e86e7eefeebd8c104de920acdc47b8b4b7a686fb73b4',
'com.google.zxing:core:11aae8fd974ab25faa8208be50468eb12349cd239e93e7c797377fa13e381729',
- 'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f',
- 'net.jcip:jcip-annotations:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
- 'org.bitcoinj:orchid:f836325cfa0466a011cb755c9b0fee6368487a2352eb45f4306ad9e4c18de080',
- 'com.squareup.okhttp:okhttp:b4c943138fcef2bcc9d2006b2250c4aabbedeafc5947ed7c0af7fd103ceb2707',
'com.cedricwalter:tor-binary-geoip:7fc7b5ebf80d65ec53d97dd8d3878b8d2c85dc04f3943e5e85e7ba641655492b',
'com.github.JesusMcCloud:jtorctl:c6ef92e46074d8d26db718ce0fe4b64b8cf7b934b7377d164c5d613b4cd7b847',
'org.apache.commons:commons-compress:a778bbd659722889245fc52a0ec2873fbbb89ec661bc1ad3dc043c0757c784c4',
'org.tukaani:xz:a594643d73cc01928cf6ca5ce100e094ea9d73af760a5d4fb6b75fa673ecec96',
- 'com.squareup.okio:okio:114bdc1f47338a68bcbc95abf2f5cdc72beeec91812f2fcd7b521c1937876266',
+ 'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f',
+ 'net.jcip:jcip-annotations:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
+ 'org.bitcoinj:orchid:f836325cfa0466a011cb755c9b0fee6368487a2352eb45f4306ad9e4c18de080',
+ 'com.squareup.okhttp:okhttp:b4c943138fcef2bcc9d2006b2250c4aabbedeafc5947ed7c0af7fd103ceb2707',
'org.objenesis:objenesis:5e168368fbc250af3c79aa5fef0c3467a2d64e5a7bd74005f25d8399aeb0708d',
+ 'com.squareup.okio:okio:114bdc1f47338a68bcbc95abf2f5cdc72beeec91812f2fcd7b521c1937876266',
]
}
diff --git a/desktop/src/main/java/bisq/desktop/app/BisqApp.java b/desktop/src/main/java/bisq/desktop/app/BisqApp.java
index 97e450c17f..d57353003e 100644
--- a/desktop/src/main/java/bisq/desktop/app/BisqApp.java
+++ b/desktop/src/main/java/bisq/desktop/app/BisqApp.java
@@ -17,7 +17,6 @@
package bisq.desktop.app;
-import bisq.desktop.SystemTray;
import bisq.desktop.common.view.CachingViewLoader;
import bisq.desktop.common.view.View;
import bisq.desktop.common.view.ViewLoader;
@@ -34,7 +33,8 @@ import bisq.desktop.util.ImageUtil;
import bisq.core.alert.AlertManager;
import bisq.core.app.AppOptionKeys;
-import bisq.core.app.AvoidStandbyMode;
+import bisq.core.app.AvoidStandbyModeService;
+import bisq.core.app.BisqEnvironment;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.btc.wallet.WalletsManager;
import bisq.core.filter.FilterManager;
@@ -44,6 +44,7 @@ import bisq.core.user.Preferences;
import bisq.common.UserThread;
import bisq.common.app.DevEnv;
+import bisq.common.app.Log;
import bisq.common.setup.GracefulShutDownHandler;
import bisq.common.setup.UncaughtExceptionHandler;
import bisq.common.util.Profiler;
@@ -73,6 +74,11 @@ import javafx.scene.layout.StackPane;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -125,7 +131,7 @@ public class BisqApp extends Application implements UncaughtExceptionHandler {
scene = createAndConfigScene(mainView, injector);
setupStage(scene);
- injector.getInstance(AvoidStandbyMode.class).init();
+ injector.getInstance(AvoidStandbyModeService.class).init();
UserThread.runPeriodically(() -> Profiler.printSystemLoad(log), LOG_MEMORY_PERIOD_MIN, TimeUnit.MINUTES);
} catch (Throwable throwable) {
@@ -219,6 +225,10 @@ public class BisqApp extends Application implements UncaughtExceptionHandler {
// configure the primary stage
String appName = injector.getInstance(Key.get(String.class, Names.named(AppOptionKeys.APP_NAME_KEY)));
+ if (BisqEnvironment.getBaseCurrencyNetwork().isTestnet())
+ appName += " [TESTNET]";
+ else if (BisqEnvironment.getBaseCurrencyNetwork().isRegtest())
+ appName += " [REGTEST]";
stage.setTitle(appName);
stage.setScene(scene);
stage.setMinWidth(1020);
@@ -259,6 +269,17 @@ public class BisqApp extends Application implements UncaughtExceptionHandler {
showSendAlertMessagePopup(injector);
} else if (Utilities.isAltOrCtrlPressed(KeyCode.F, keyEvent)) {
showFilterPopup(injector);
+ } else if (Utilities.isAltOrCtrlPressed(KeyCode.T, keyEvent)) {
+ // Toggle between show tor logs and only show warnings. Helpful in case of connection problems
+ String pattern = "org.berndpruenster.netlayer";
+ Level logLevel = ((Logger) LoggerFactory.getLogger(pattern)).getLevel();
+ if (logLevel != Level.DEBUG) {
+ log.info("Set log level for org.berndpruenster.netlayer classes to DEBUG");
+ Log.setCustomLogLevel(pattern, Level.DEBUG);
+ } else {
+ log.info("Set log level for org.berndpruenster.netlayer classes to WARN");
+ Log.setCustomLogLevel(pattern, Level.WARN);
+ }
} else if (Utilities.isAltOrCtrlPressed(KeyCode.J, keyEvent)) {
WalletsManager walletsManager = injector.getInstance(WalletsManager.class);
if (walletsManager.areWalletsAvailable())
@@ -291,7 +312,7 @@ public class BisqApp extends Application implements UncaughtExceptionHandler {
// We show a popup to inform user that open offers will be removed if Bisq is not running.
String key = "showOpenOfferWarnPopupAtShutDown";
- if (injector.getInstance(Preferences.class).showAgain(key)) {
+ if (injector.getInstance(Preferences.class).showAgain(key) && !DevEnv.isDevMode()) {
new Popup<>().information(Res.get("popup.info.shutDownWithOpenOffers"))
.dontShowAgainId(key)
.useShutDownButton()
diff --git a/desktop/src/main/java/bisq/desktop/SystemTray.java b/desktop/src/main/java/bisq/desktop/app/SystemTray.java
similarity index 99%
rename from desktop/src/main/java/bisq/desktop/SystemTray.java
rename to desktop/src/main/java/bisq/desktop/app/SystemTray.java
index 14baac9942..8cb53767ae 100644
--- a/desktop/src/main/java/bisq/desktop/SystemTray.java
+++ b/desktop/src/main/java/bisq/desktop/app/SystemTray.java
@@ -15,7 +15,7 @@
* along with Bisq. If not, see .
*/
-package bisq.desktop;
+package bisq.desktop.app;
import bisq.desktop.util.GUIUtil;
import bisq.desktop.util.ImageUtil;
diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/CryptoCurrencyForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/CryptoCurrencyForm.java
index b8c7ccdfea..4d397b2210 100644
--- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/CryptoCurrencyForm.java
+++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/CryptoCurrencyForm.java
@@ -21,6 +21,7 @@ import bisq.desktop.components.InputTextField;
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.Layout;
+import bisq.core.dao.governance.asset.AssetService;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.locale.TradeCurrency;
@@ -48,18 +49,15 @@ import javafx.util.StringConverter;
import java.util.Optional;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import static bisq.desktop.util.FormBuilder.addLabelInputTextField;
import static bisq.desktop.util.FormBuilder.addLabelTextField;
import static bisq.desktop.util.FormBuilder.addLabelTextFieldWithCopyIcon;
public class CryptoCurrencyForm extends PaymentMethodForm {
- private static final Logger log = LoggerFactory.getLogger(CryptoCurrencyForm.class);
-
private final CryptoCurrencyAccount cryptoCurrencyAccount;
private final AltCoinAddressValidator altCoinAddressValidator;
+ private final AssetService assetService;
+
private InputTextField addressInputTextField;
private ComboBox currencyComboBox;
@@ -80,10 +78,12 @@ public class CryptoCurrencyForm extends PaymentMethodForm {
InputValidator inputValidator,
GridPane gridPane,
int gridRow,
- BSFormatter formatter) {
+ BSFormatter formatter,
+ AssetService assetService) {
super(paymentAccount, accountAgeWitnessService, inputValidator, gridPane, gridRow, formatter);
this.cryptoCurrencyAccount = (CryptoCurrencyAccount) paymentAccount;
this.altCoinAddressValidator = altCoinAddressValidator;
+ this.assetService = assetService;
}
@Override
@@ -162,7 +162,7 @@ public class CryptoCurrencyForm extends PaymentMethodForm {
currencyComboBox = FormBuilder.addLabelSearchComboBox(gridPane, ++gridRow, Res.get("payment.altcoin"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
currencyComboBox.setPromptText(Res.get("payment.select.altcoin"));
- currencyComboBox.setItems(FXCollections.observableArrayList(CurrencyUtil.getAllSortedCryptoCurrencies()));
+ currencyComboBox.setItems(FXCollections.observableArrayList(CurrencyUtil.getWhiteListedSortedCryptoCurrencies(assetService)));
currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 15));
currencyComboBox.setConverter(new StringConverter() {
@Override
diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/OKPayForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/OKPayForm.java
index dd816a0554..21330a13c7 100644
--- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/OKPayForm.java
+++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/OKPayForm.java
@@ -52,6 +52,7 @@ import static bisq.desktop.util.FormBuilder.addLabelInputTextField;
import static bisq.desktop.util.FormBuilder.addLabelTextField;
import static bisq.desktop.util.FormBuilder.addLabelTextFieldWithCopyIcon;
+@Deprecated
public class OKPayForm extends PaymentMethodForm {
private static final Logger log = LoggerFactory.getLogger(OKPayForm.class);
diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java
index b60e5ac85e..6f09e8c4b1 100644
--- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java
+++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/PaymentMethodForm.java
@@ -156,12 +156,20 @@ public abstract class PaymentMethodForm {
CurrencyUtil.getAllSortedCryptoCurrencies().get(0) :
CurrencyUtil.getDefaultTradeCurrency();
+
final boolean isAddAccountScreen = paymentAccount.getAccountName() == null;
final long accountAge = !isAddAccountScreen ? accountAgeWitnessService.getMyAccountAge(paymentAccount.getPaymentAccountPayload()) : 0L;
- addLabelTextField(gridPane, ++gridRow, Res.get("payment.limitations"), Res.get("payment.maxPeriodAndLimit",
- getTimeText(hours),
- formatter.formatCoinWithCode(Coin.valueOf(accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrency.getCode()))),
- formatter.formatAccountAge(accountAge)));
+
+ final String limitationsText = paymentAccount instanceof CryptoCurrencyAccount ?
+ Res.get("payment.maxPeriodAndLimitCrypto",
+ getTimeText(hours),
+ formatter.formatCoinWithCode(Coin.valueOf(accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrency.getCode()))))
+ :
+ Res.get("payment.maxPeriodAndLimit",
+ getTimeText(hours),
+ formatter.formatCoinWithCode(Coin.valueOf(accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrency.getCode()))),
+ formatter.formatAccountAge(accountAge));
+ addLabelTextField(gridPane, ++gridRow, Res.get("payment.limitations"), limitationsText);
if (isAddAccountScreen) {
InputTextField inputTextField = addLabelInputTextField(gridPane, ++gridRow, Res.get("payment.salt"), 0).second;
diff --git a/desktop/src/main/java/bisq/desktop/main/MainViewModel.java b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java
index f54415c432..3235fafa8c 100644
--- a/desktop/src/main/java/bisq/desktop/main/MainViewModel.java
+++ b/desktop/src/main/java/bisq/desktop/main/MainViewModel.java
@@ -17,6 +17,7 @@
package bisq.desktop.main;
+import bisq.desktop.app.BisqApp;
import bisq.desktop.common.model.ViewModel;
import bisq.desktop.components.BalanceWithConfirmationTextField;
import bisq.desktop.components.TxIdTextField;
@@ -33,8 +34,8 @@ import bisq.core.alert.PrivateNotificationManager;
import bisq.core.app.AppOptionKeys;
import bisq.core.app.BisqEnvironment;
import bisq.core.app.BisqSetup;
+import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.BtcWalletService;
-import bisq.core.btc.wallet.WalletsSetup;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.payment.AccountAgeWitnessService;
@@ -281,6 +282,9 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupCompleteList
.onAction(() -> GUIUtil.reSyncSPVChain(walletsSetup, preferences))
.show();
});
+ bisqSetup.setVoteResultExceptionHandler(voteResultException -> {
+ new Popup<>().error(voteResultException.toString()).show();
+ });
bisqSetup.setChainFileLockedExceptionHandler(msg -> {
new Popup<>().warning(msg)
@@ -291,7 +295,7 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupCompleteList
bisqSetup.setShowFirstPopupIfResyncSPVRequestedHandler(this::showFirstPopupIfResyncSPVRequested);
bisqSetup.setRequestWalletPasswordHandler(aesKeyHandler -> walletPasswordWindow
.onAesKey(aesKeyHandler::accept)
- .hideCloseButton()
+ .onClose(() -> BisqApp.getShutDownHandler().run())
.show());
bisqSetup.setDisplayUpdateHandler((alert, key) -> new DisplayUpdateDownloadWindow(alert)
diff --git a/desktop/src/main/java/bisq/desktop/main/account/arbitratorregistration/ArbitratorRegistrationViewModel.java b/desktop/src/main/java/bisq/desktop/main/account/arbitratorregistration/ArbitratorRegistrationViewModel.java
index 035cfde20e..b7bdfa72a3 100644
--- a/desktop/src/main/java/bisq/desktop/main/account/arbitratorregistration/ArbitratorRegistrationViewModel.java
+++ b/desktop/src/main/java/bisq/desktop/main/account/arbitratorregistration/ArbitratorRegistrationViewModel.java
@@ -21,7 +21,7 @@ import bisq.desktop.common.model.ActivatableViewModel;
import bisq.core.arbitration.Arbitrator;
import bisq.core.arbitration.ArbitratorManager;
-import bisq.core.btc.AddressEntry;
+import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.locale.LanguageUtil;
import bisq.core.user.User;
diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsView.java b/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsView.java
index 8914e3fb3f..6229958422 100644
--- a/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsView.java
+++ b/desktop/src/main/java/bisq/desktop/main/account/content/altcoinaccounts/AltCoinAccountsView.java
@@ -29,6 +29,7 @@ import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.ImageUtil;
import bisq.desktop.util.Layout;
+import bisq.core.dao.governance.asset.AssetService;
import bisq.core.locale.CryptoCurrency;
import bisq.core.locale.Res;
import bisq.core.locale.TradeCurrency;
@@ -76,6 +77,7 @@ public class AltCoinAccountsView extends ActivatableViewAndModel toRemove = new ArrayList<>();
paymentAccounts.stream()
.filter(paymentAccount -> paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.VENMO_ID) ||
- paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.CASH_APP_ID))
+ paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.CASH_APP_ID) ||
+ paymentAccount.getPaymentMethod().getId().equals(PaymentMethod.OK_PAY_ID))
.forEach(toRemove::add);
toRemove.forEach(paymentAccount -> {
diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java
index a233028a10..d32aace01b 100644
--- a/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java
+++ b/desktop/src/main/java/bisq/desktop/main/account/content/fiataccounts/FiatAccountsView.java
@@ -395,6 +395,7 @@ public class FiatAccountsView extends ActivatableViewAndModel !paymentMethod.getId().equals(PaymentMethod.BLOCK_CHAINS_ID))
.filter(paymentMethod -> !paymentMethod.getId().equals(PaymentMethod.VENMO_ID))
.filter(paymentMethod -> !paymentMethod.getId().equals(PaymentMethod.CASH_APP_ID))
+ .filter(paymentMethod -> !paymentMethod.getId().equals(PaymentMethod.OK_PAY_ID))
.collect(Collectors.toList());
paymentMethodComboBox.setItems(FXCollections.observableArrayList(list));
paymentMethodComboBox.setConverter(new StringConverter() {
diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/notifications/MobileNotificationsView.java b/desktop/src/main/java/bisq/desktop/main/account/content/notifications/MobileNotificationsView.java
index 0a56003e53..d40cce4be9 100644
--- a/desktop/src/main/java/bisq/desktop/main/account/content/notifications/MobileNotificationsView.java
+++ b/desktop/src/main/java/bisq/desktop/main/account/content/notifications/MobileNotificationsView.java
@@ -472,8 +472,7 @@ public class MobileNotificationsView extends ActivatableView {
paymentAccountsComboBox = FormBuilder.addLabelComboBox(root, gridRow,
Res.getWithCol("account.notifications.marketAlert.selectPaymentAccount"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
- paymentAccountsComboBox.setPromptText(Res.get("shared.select"));
- paymentAccountsComboBox.setConverter(new StringConverter() {
+ paymentAccountsComboBox.setConverter(new StringConverter<>() {
@Override
public String toString(PaymentAccount paymentAccount) {
return paymentAccount.getAccountName();
diff --git a/desktop/src/main/java/bisq/desktop/main/account/content/seedwords/SeedWordsView.java b/desktop/src/main/java/bisq/desktop/main/account/content/seedwords/SeedWordsView.java
index 663a545575..08c824a12c 100644
--- a/desktop/src/main/java/bisq/desktop/main/account/content/seedwords/SeedWordsView.java
+++ b/desktop/src/main/java/bisq/desktop/main/account/content/seedwords/SeedWordsView.java
@@ -203,7 +203,7 @@ public class SeedWordsView extends ActivatableView {
walletPasswordWindow.headLine(Res.get("account.seed.enterPw")).onAesKey(aesKey -> {
initSeedWords(walletsManager.getDecryptedSeed(aesKey, btcWalletService.getKeyChainSeed(), btcWalletService.getKeyCrypter()));
showSeedScreen();
- }).show();
+ }).hideForgotPasswordButton().show();
}
private void initSeedWords(DeterministicSeed seed) {
diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java
index c25e9107bd..12d3feb416 100644
--- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java
+++ b/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java
@@ -24,7 +24,7 @@ import bisq.desktop.main.funds.deposit.DepositView;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.util.GUIUtil;
-import bisq.core.btc.wallet.WalletsSetup;
+import bisq.core.btc.setup.WalletsSetup;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.bonding.lockup.LockupType;
import bisq.core.dao.governance.role.BondedRole;
diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/lockup/LockupView.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/lockup/LockupView.java
index ced735affe..4681ebeaee 100644
--- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/lockup/LockupView.java
+++ b/desktop/src/main/java/bisq/desktop/main/dao/bonding/lockup/LockupView.java
@@ -26,9 +26,9 @@ import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.Layout;
import bisq.desktop.util.validation.BsqValidator;
-import bisq.core.btc.Restrictions;
-import bisq.core.btc.wallet.BsqBalanceListener;
+import bisq.core.btc.listeners.BsqBalanceListener;
import bisq.core.btc.wallet.BsqWalletService;
+import bisq.core.btc.wallet.Restrictions;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.bonding.BondingConsensus;
import bisq.core.dao.bonding.lockup.LockupType;
@@ -119,8 +119,7 @@ public class LockupView extends ActivatableView implements BsqBa
timeInputTextField.setValidator(timeInputTextFieldValidator);
lockupTypeComboBox = FormBuilder.